diff --git a/.bazelrc b/.bazelrc index 51cabb040..504f20a08 100644 --- a/.bazelrc +++ b/.bazelrc @@ -6,9 +6,10 @@ # curl -sSL https://raw.githubusercontent.com/envoyproxy/envoy-wasm/master/.bazelrc > envoy.bazelrc import %workspace%/envoy.bazelrc -# MSAN does not recognize host libc's stat() as initializing Bazel's runfiles buffer. +# MSAN does not recognize host libc's stat() as initializing the rules_cc runfiles buffer +# (@bazel_tools//tools/cpp/runfiles is an alias for @rules_cc//cc/runfiles since Bazel 8). # Scope zero initialization to this helper so proxy and Envoy sources remain fully checked. -build:msan --per_file_copt=external/bazel_tools/tools/cpp/runfiles/runfiles[.]cc@-ftrivial-auto-var-init=zero +build:msan --per_file_copt=external/rules_cc/cc/runfiles/runfiles[.]cc@-ftrivial-auto-var-init=zero # Rust crate repinning for repository rules. Inherit CARGO_BAZEL_REPIN from the # invocation environment so developers can repin explicitly without forcing diff --git a/.bazelversion b/.bazelversion index 5942a0d3a..df5119ec6 100644 --- a/.bazelversion +++ b/.bazelversion @@ -1 +1 @@ -7.7.1 +8.7.0 diff --git a/.github/renovate.json5 b/.github/renovate.json5 index fe3b1001e..731a9ee1e 100644 --- a/.github/renovate.json5 +++ b/.github/renovate.json5 @@ -180,11 +180,11 @@ ], }, { - groupName: 'envoy 1.38.x', + groupName: 'envoy 1.39.x', matchDepNames: [ 'envoyproxy/envoy', ], - allowedVersions: '<=1.38', + allowedVersions: '<=1.39', matchBaseBranches: [ 'main', ], diff --git a/.github/workflows/ci-tests.yaml b/.github/workflows/ci-tests.yaml index e6fa67bbf..1da625448 100644 --- a/.github/workflows/ci-tests.yaml +++ b/.github/workflows/ci-tests.yaml @@ -12,9 +12,9 @@ concurrency: cancel-in-progress: true jobs: - proxylib: - timeout-minutes: 360 - name: Run unit tests for proxylib + go-vendoring: + timeout-minutes: 30 + name: Check Go module vendoring runs-on: ubuntu-latest steps: - name: Install Go @@ -31,9 +31,6 @@ jobs: go mod tidy go mod vendor test -z "$(git status --porcelain)" || (echo "please run 'go mod tidy && go mod vendor', and submit your changes"; exit 1) - - name: Run unit tests - run: | - make -C proxylib test tests: timeout-minutes: 360 @@ -177,10 +174,6 @@ jobs: platforms: linux/amd64 build-args: | BUILDER_BASE=quay.io/${{ github.repository_owner }}/cilium-envoy-builder-dev:${{ env.BUILDER_DOCKER_HASH }} - PROXYLIB_BUILDER=quay.io/${{ github.repository_owner }}/cilium-envoy-builder-dev:${{ env.BUILDER_DOCKER_HASH }} - PROXYLIB_CC=/usr/lib/llvm-18/bin/clang - PROXYLIB_GO_BUILD_FLAGS=-msan -buildvcs=false - PROXYLIB_GOCACHE=/tmp/go-build ARCHIVE_IMAGE=quay.io/${{ github.repository_owner }}/cilium-envoy-builder:test-main-archive-latest DEBUG=1 BAZEL_BUILD_OPTS=--config=msan --remote_upload_local_results=false @@ -231,11 +224,6 @@ jobs: platforms: linux/amd64 build-args: | BUILDER_BASE=quay.io/${{ github.repository_owner }}/cilium-envoy-builder-dev:${{ env.BUILDER_DOCKER_HASH }} - PROXYLIB_BUILDER=quay.io/${{ github.repository_owner }}/cilium-envoy-builder-dev:${{ env.BUILDER_DOCKER_HASH }} - PROXYLIB_CC=/usr/lib/llvm-18/bin/clang - PROXYLIB_CGO_CFLAGS=-fsanitize=thread - PROXYLIB_GO_BUILD_FLAGS=-installsuffix=tsan -buildvcs=false - PROXYLIB_GOCACHE=/tmp/go-build ARCHIVE_IMAGE=quay.io/${{ github.repository_owner }}/cilium-envoy-builder:test-main-archive-latest DEBUG=1 BAZEL_BUILD_OPTS=--config=tsan --remote_upload_local_results=false diff --git a/.gitignore b/.gitignore index 1096677df..43ced23e1 100644 --- a/.gitignore +++ b/.gitignore @@ -37,7 +37,6 @@ __pycache__/ # generated from make targets *.ok -/proxylib/libcilium.so # Istio porting files /envoy_bootstrap*.json @@ -48,9 +47,6 @@ __pycache__/ # generated for docker builds via make /SOURCE_VERSION -/proxylib/libcilium.so* -/proxylib/_obj* - /BUILD_DEP_HASHES # clangd compilation database diff --git a/Dockerfile b/Dockerfile index d595177b4..bb6e680bf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,15 +14,6 @@ ARG BUILDER_BASE=quay.io/cilium/cilium-envoy-builder:6.1.0-latest # ARG ARCHIVE_IMAGE=builder-fresh -FROM --platform=$BUILDPLATFORM $BUILDER_BASE AS proxylib -WORKDIR /go/src/github.com/cilium/proxy -COPY --chown=1337:1337 . ./ -ARG TARGETARCH -ENV TARGETARCH=$TARGETARCH -RUN --mount=mode=0777,gid=1337,uid=1337,target=/cilium/proxy/.cache,type=cache \ - --mount=mode=0777,gid=1337,uid=1337,target=/go/pkg,type=cache \ - PATH=$PATH:/usr/local/go/bin GOARCH=${TARGETARCH} make -C proxylib all && mv proxylib/libcilium.so /tmp/libcilium.so - FROM --platform=$BUILDPLATFORM $BUILDER_BASE AS builder-fresh LABEL maintainer="maintainer@cilium.io" WORKDIR /cilium/proxy @@ -73,10 +64,6 @@ RUN --mount=mode=0777,uid=1337,gid=1337,target=/cilium/proxy/.cache,type=cache,i if [ -n "${COPY_CACHE_EXT}" ]; then PKG_BUILD=1 make BUILD_DEP_HASHES; if [ -f /tmp/bazel-cache/BUILD_DEP_HASHES ] && ! diff BUILD_DEP_HASHES /tmp/bazel-cache/BUILD_DEP_HASHES; then echo "Build dependencies have changed, clearing bazel cache"; rm -rf /tmp/bazel-cache/*; rm -rf /cilium/proxy/.cache/*; fi ; cp BUILD_DEP_HASHES ENVOY_VERSION /tmp/bazel-cache; fi && \ BAZEL_BUILD_OPTS="${BAZEL_BUILD_OPTS} --disk_cache=/tmp/bazel-cache" PKG_BUILD=1 V=$V DEBUG=$DEBUG RELEASE_DEBUG=$RELEASE_DEBUG DESTDIR=/tmp/install make install && \ if [ -n "${COPY_CACHE_EXT}" ]; then cp -ra /tmp/bazel-cache /tmp/bazel-cache${COPY_CACHE_EXT}; ls -la /tmp/bazel-cache${COPY_CACHE_EXT}; fi -# -# Copy proxylib after build to allow install as non-root to succeed -# -COPY --from=proxylib /tmp/libcilium.so /tmp/install/usr/lib/libcilium.so FROM scratch AS empty-builder-archive LABEL maintainer="maintainer@cilium.io" diff --git a/Dockerfile.tests b/Dockerfile.tests index 14953d804..c10718457 100644 --- a/Dockerfile.tests +++ b/Dockerfile.tests @@ -3,9 +3,6 @@ # ARG BUILDER_BASE=quay.io/cilium/cilium-envoy-builder:6.5.0-latest@sha256:3f98b069a4c4737d8252fdf47f77d9f7e27ef5acb1bec13af3619180d6baee23 -# Common Builder image used in cilium/cilium -ARG PROXYLIB_BUILDER=quay.io/cilium/cilium-builder:767c4152bb156a879fca4c5b76f445de4b4cdaa9@sha256:26392846fa25ab2607c120ece242d61365724a5f21e85f5733f72221637b70fa - # # ARCHIVE_IMAGE defaults to the result of the first stage below, # refreshing the build caches from Envoy dependencies before the final @@ -17,22 +14,6 @@ ARG PROXYLIB_BUILDER=quay.io/cilium/cilium-builder:767c4152bb156a879fca4c5b76f44 # ARG ARCHIVE_IMAGE=builder-fresh -FROM --platform=$BUILDPLATFORM $PROXYLIB_BUILDER AS proxylib -WORKDIR /go/src/github.com/cilium/proxy -ENV PATH=/usr/local/go/bin:$PATH -ARG TARGETARCH -ARG PROXYLIB_CC=gcc -ARG PROXYLIB_CGO_CFLAGS -ARG PROXYLIB_GO_BUILD_FLAGS -ARG PROXYLIB_GOCACHE -RUN --mount=type=bind,target=/go/src/github.com/cilium/proxy \ - --mount=mode=0777,target=/cilium/proxy/.cache,type=cache \ - --mount=mode=0777,target=/go/pkg,type=cache \ - --mount=mode=0777,uid=1337,gid=1337,target=/tmp/go-build,type=cache \ - if [ -n "${PROXYLIB_GOCACHE}" ]; then export GOCACHE="${PROXYLIB_GOCACHE}"; fi && \ - if [ -n "${PROXYLIB_CGO_CFLAGS}" ]; then export CGO_CFLAGS="${PROXYLIB_CGO_CFLAGS}"; fi && \ - CC="${PROXYLIB_CC}" GOARCH="${TARGETARCH}" GO_BUILD_FLAGS="${PROXYLIB_GO_BUILD_FLAGS}" make -C proxylib TARGET=/tmp/libcilium.so all - FROM --platform=$BUILDPLATFORM $BUILDER_BASE AS builder-fresh LABEL maintainer="maintainer@cilium.io" WORKDIR /cilium/proxy @@ -48,8 +29,6 @@ ENV TARGETARCH=$TARGETARCH # # Build dependencies # -# Make proxylib available for building the test dependencies by copying it before running the tests -COPY --from=proxylib /tmp/libcilium.so proxylib/libcilium.so RUN BAZEL_BUILD_OPTS="${BAZEL_BUILD_OPTS} --disk_cache=/tmp/bazel-cache" PKG_BUILD=1 V=$V DEBUG=$DEBUG make envoy-test-deps # By default this stage picks up the result of the build above, but ARCHIVE_IMAGE can be @@ -69,8 +48,6 @@ ENV TARGETARCH=$TARGETARCH # Clear runner's cache when building deps RUN --mount=mode=0777,uid=1337,gid=1337,target=/cilium/proxy/.cache,type=cache,id=$TARGETARCH,sharing=private rm -rf /cilium/proxy/.cache/* -# Make proxylib available for building the test dependencies by copying it before running the tests -COPY --from=proxylib /tmp/libcilium.so proxylib/libcilium.so RUN --mount=target=/tmp/bazel-cache,source=/tmp/bazel-cache,from=archive-cache,rw \ if [ -f /tmp/bazel-cache/ENVOY_VERSION ]; then CACHE_ENVOY_VERSION=`cat /tmp/bazel-cache/ENVOY_VERSION`; ENVOY_VERSION=`cat ENVOY_VERSION`; if [ "${CACHE_ENVOY_VERSION}" != "${ENVOY_VERSION}" ]; then echo "Testing Envoy ${ENVOY_VERSION} with bazel archive from different Envoy version (${CACHE_ENVOY_VERSION})"; else echo "Testing Envoy ${ENVOY_VERSION} with bazel cache of the same version"; fi; else echo "Bazel cache has no ENVOY_VERSION, it may be empty."; fi && \ touch /tmp/bazel-cache/permissions-check && \ @@ -92,8 +69,6 @@ ARG NO_CACHE ENV TARGETARCH=$TARGETARCH RUN --mount=mode=0777,uid=1337,gid=1337,target=/cilium/proxy/.cache,type=cache,id=$TARGETARCH,sharing=private if [ -n "$NO_CACHE" ]; then rm -rf /cilium/proxy/.cache/*; fi -# Make proxylib available for the tests by copying it before running the tests -COPY --from=proxylib /tmp/libcilium.so proxylib/libcilium.so RUN --mount=mode=0777,uid=1337,gid=1337,target=/cilium/proxy/.cache,type=cache,id=$TARGETARCH,sharing=private \ --mount=target=/tmp/bazel-cache,source=/tmp/bazel-cache,from=archive-cache,rw \ if [ "$TARGETARCH" != "$BUILDARCH" ]; then \ diff --git a/ENVOY_VERSION b/ENVOY_VERSION index b09114232..f6861fa0b 100644 --- a/ENVOY_VERSION +++ b/ENVOY_VERSION @@ -1 +1 @@ -envoy-1.38.4 +envoy-1.39.1 diff --git a/Makefile b/Makefile index eb5581a87..5af237373 100644 --- a/Makefile +++ b/Makefile @@ -59,14 +59,9 @@ BAZEL_ASAN_TEST_OPTS ?= --jobs=HOST_RAM*.0001 --test_timeout=600 --local_test_jo BAZEL_MSAN_BUILD_OPTS ?= --config=msan $(EXTRA_BAZEL_BUILD_OPTS) -c dbg BAZEL_MSAN_TEST_OPTS ?= --jobs=HOST_RAM*.00005 --test_timeout=900 --local_test_jobs=1 --flaky_test_attempts=1 --test_output=errors -PROXYLIB_MSAN_CC ?= clang -PROXYLIB_MSAN_GO_BUILD_FLAGS ?= -msan -buildvcs=false BAZEL_TSAN_BUILD_OPTS ?= --config=tsan $(EXTRA_BAZEL_BUILD_OPTS) -c dbg BAZEL_TSAN_TEST_OPTS ?= --jobs=HOST_RAM*.00005 --test_timeout=900 --local_test_jobs=1 --flaky_test_attempts=1 --test_output=errors -PROXYLIB_TSAN_CC ?= clang -PROXYLIB_TSAN_CGO_CFLAGS ?= -fsanitize=thread -PROXYLIB_TSAN_GO_BUILD_FLAGS ?= -installsuffix=tsan -buildvcs=false ifdef DEBUG BAZEL_BUILD_OPTS += -c dbg @@ -204,16 +199,13 @@ clean: force @$(ECHO_CLEAN) $(notdir $(shell pwd)) -$(QUIET) rm -f $(ENVOY_BINS) $(ENVOY_TESTS) -proxylib/libcilium.so: - make -C proxylib - .PHONY: envoy-test-deps -envoy-test-deps: $(COMPILER_DEP) SOURCE_VERSION proxylib/libcilium.so +envoy-test-deps: $(COMPILER_DEP) SOURCE_VERSION @$(ECHO_BAZEL) $(BAZEL) $(BAZEL_OPTS) build $(BAZEL_BUILD_OPTS) $(BAZEL_TEST_OPTS) //tests/... @envoy//test/integration:tcp_proxy_integration_test $(BAZEL_FILTER) .PHONY: envoy-tests -envoy-tests: $(COMPILER_DEP) SOURCE_VERSION proxylib/libcilium.so +envoy-tests: $(COMPILER_DEP) SOURCE_VERSION @$(ECHO_BAZEL) # Upstream tcp_proxy_integration_test included to validate that our custom patches # didn't break anything @@ -223,20 +215,12 @@ envoy-tests: $(COMPILER_DEP) SOURCE_VERSION proxylib/libcilium.so envoy-asan-tests: $(MAKE) envoy-tests BAZEL_BUILD_OPTS="$(BAZEL_ASAN_BUILD_OPTS)" BAZEL_TEST_OPTS="$(BAZEL_ASAN_TEST_OPTS)" -.PHONY: proxylib-msan -proxylib-msan: - $(MAKE) -C proxylib all CC="$(PROXYLIB_MSAN_CC)" GO_BUILD_FLAGS="$(PROXYLIB_MSAN_GO_BUILD_FLAGS)" - .PHONY: envoy-msan-tests -envoy-msan-tests: proxylib-msan +envoy-msan-tests: $(MAKE) envoy-tests BAZEL_BUILD_OPTS="$(BAZEL_MSAN_BUILD_OPTS)" BAZEL_TEST_OPTS="$(BAZEL_MSAN_TEST_OPTS)" -.PHONY: proxylib-tsan -proxylib-tsan: - $(MAKE) -C proxylib all CC="$(PROXYLIB_TSAN_CC)" CGO_CFLAGS="$(PROXYLIB_TSAN_CGO_CFLAGS)" GO_BUILD_FLAGS="$(PROXYLIB_TSAN_GO_BUILD_FLAGS)" - .PHONY: envoy-tsan-tests -envoy-tsan-tests: proxylib-tsan +envoy-tsan-tests: $(MAKE) envoy-tests BAZEL_BUILD_OPTS="$(BAZEL_TSAN_BUILD_OPTS)" BAZEL_TEST_OPTS="$(BAZEL_TSAN_TEST_OPTS)" .PHONY: \ diff --git a/Makefile.dev b/Makefile.dev index 4bee8d8df..363a2f8a7 100644 --- a/Makefile.dev +++ b/Makefile.dev @@ -42,7 +42,7 @@ veryclean: force-non-root clean precheck: force-non-root tools/check_repositories.sh -FORMAT_EXCLUDED_PREFIXES = "./linux/" "./proxylib/" "./starter/" "./vendor/" "./go/" "./envoy_build_config/" "./work/" "./bin/" "./.cache/" +FORMAT_EXCLUDED_PREFIXES = "./linux/" "./starter/" "./vendor/" "./go/" "./envoy_build_config/" "./work/" "./bin/" "./.cache/" # The default set of sources assumes all relevant sources are dependecies of some tests! TIDY_SOURCES ?= $(shell bazel query 'kind("source file", deps(//tests/...))' 2>/dev/null | sed -n "s/\/\/cilium:/cilium\//p; s/\/\/tests:/tests\//p") @@ -95,12 +95,11 @@ format-fix: $(COMPILER_DEP) force-non-root $(BAZEL) $(BAZEL_OPTS) run $(BAZEL_BUILD_OPTS) @envoy//tools/code_format:check_format -- --path "$(PWD)" --skip_envoy_build_rule_check --add-excluded-prefixes $(FORMAT_EXCLUDED_PREFIXES) --bazel_tools_check_excluded_paths="." --build_fixer_check_excluded_paths="./" fix # Run tests without debug by default. -tests: $(COMPILER_DEP) force-non-root SOURCE_VERSION proxylib/libcilium.so install-bazelisk +tests: $(COMPILER_DEP) force-non-root SOURCE_VERSION install-bazelisk $(BAZEL) $(BAZEL_OPTS) test $(BAZEL_BUILD_OPTS) //:envoy_binary_test $(BAZEL_FILTER) $(BAZEL) $(BAZEL_OPTS) test $(BAZEL_BUILD_OPTS) $(BAZEL_TEST_OPTS) //tests/... $(BAZEL_FILTER) - $(MAKE) -C proxylib test -unstripped-tests: $(COMPILER_DEP) force-non-root SOURCE_VERSION proxylib/libcilium.so install-bazelisk +unstripped-tests: $(COMPILER_DEP) force-non-root SOURCE_VERSION install-bazelisk $(BAZEL) $(BAZEL_OPTS) test $(BAZEL_BUILD_OPTS) --config=release_debug --fission=no --features=-per_object_debug_info //:envoy_binary_test $(BAZEL_FILTER) $(BAZEL) $(BAZEL_OPTS) test $(BAZEL_BUILD_OPTS) --config=release_debug --fission=no --features=-per_object_debug_info $(BAZEL_TEST_OPTS) //tests/... $(BAZEL_FILTER) diff --git a/WORKSPACE b/WORKSPACE index c19513f10..ba76ae29c 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -12,8 +12,8 @@ ENVOY_REPO = "envoy" # # No other line in this file may have ENVOY_SHA followed by an equals sign! # -# renovate: datasource=github-releases depName=envoyproxy/envoy digestVersion=v1.38.4 -ENVOY_SHA = "ef2d997c1b022cf8b849a1d3521fbf234d79ca26" +# renovate: datasource=github-releases depName=envoyproxy/envoy digestVersion=v1.39.1 +ENVOY_SHA = "b579d07d3ad7ee11d32b105e91a5a39ad24718d7" # // clang-format off: unexpected @bazel_tools reference, please indirect via a definition in //bazel load("@bazel_tools//tools/build_defs/repo:git.bzl", "git_repository") @@ -34,17 +34,18 @@ local_repository( git_repository( name = "envoy", commit = ENVOY_SHA, - patch_args = ["apply"], - patch_tool = "git", + # Use Bazel's native patch implementation. `patch_tool = "git"` must not be + # used: since Bazel 8, `git_repository` carries a `patch_strip` attribute and + # prepends its `-pN` to `patch_args`, which `git apply` rejects as an unknown + # global option. + patch_strip = 1, patches = [ "@//patches:0001-network-Add-callback-for-upstream-authorization.patch", "@//patches:0002-listener-add-socket-options.patch", "@//patches:0003-original_dst_cluster-Avoid-multiple-hosts-for-the-sa.patch", "@//patches:0004-thread_local-reset-slot-in-worker-threads-first.patch", "@//patches:0005-http-header-expose-attribute.patch", - "@//patches:0006-test-integration-Defer-fake-upstream-read-enable-un.patch", - "@//patches:0007-config-add-grpc-mux-stream-event-callback.patch", - "@//patches:0008-repo-Make-yq-dependency-optional-for-CI-config-parsi.patch", + "@//patches:0006-config-add-grpc-mux-stream-event-callback.patch", ], # // clang-format off: Envoy's format check: Only repository_locations.bzl may contains URL references remote = "https://github.com/envoyproxy/envoy.git", diff --git a/cilium/BUILD b/cilium/BUILD index cc2fac167..d581c6ce2 100644 --- a/cilium/BUILD +++ b/cilium/BUILD @@ -135,6 +135,7 @@ envoy_cc_library( "//cilium:network_policy_lib", "//cilium/api:l7policy_cc_proto", "@envoy//envoy/config:subscription_interface", + "@envoy//envoy/upstream:host_description_interface", "@envoy//source/common/http:utility_lib", "@envoy//source/common/network:upstream_server_name_lib", "@envoy//source/common/network:upstream_subject_alt_names_lib", @@ -269,26 +270,6 @@ envoy_cc_library( ], ) -envoy_cc_library( - name = "proxylib_lib", - srcs = [ - "proxylib.cc", - ], - hdrs = [ - "proxylib.h", - "//proxylib:libcilium.h", - "//proxylib:types.h", - ], - repository = "@envoy", - deps = [ - "@envoy//envoy/network:connection_interface", - "@envoy//envoy/singleton:manager_interface", - "@envoy//source/common/buffer:buffer_lib", - "@envoy//source/common/common:assert_lib", - "@envoy//source/common/common:logger_lib", - ], -) - envoy_cc_library( name = "network_filter_lib", srcs = [ @@ -302,7 +283,6 @@ envoy_cc_library( "//cilium:conntrack_lib", "//cilium:filter_state_lib", "//cilium:network_policy_lib", - "//cilium:proxylib_lib", "//cilium/api:network_filter_cc_proto", "@envoy//envoy/buffer:buffer_interface", "@envoy//envoy/network:connection_interface", diff --git a/cilium/accesslog.cc b/cilium/accesslog.cc index 36cd02a01..4b719c5f0 100644 --- a/cilium/accesslog.cc +++ b/cilium/accesslog.cc @@ -65,7 +65,10 @@ void AccessLog::log(AccessLog::Entry& log_entry, ::cilium::EntryType entry_type) // encode protobuf std::string msg; - entry.SerializeToString(&msg); + if (!entry.SerializeToString(&msg)) { + ENVOY_LOG_MISC(warn, "cilium.AccessLog: Failed to serialize log entry, skipping it"); + return; + } UDSClient::log(msg); } diff --git a/cilium/api/network_filter.proto b/cilium/api/network_filter.proto index 157ba77a7..d31f475ee 100644 --- a/cilium/api/network_filter.proto +++ b/cilium/api/network_filter.proto @@ -5,11 +5,10 @@ option go_package = "github.com/cilium/proxy/go/cilium/api;cilium"; package cilium; message NetworkFilter { - // Path to the proxylib to be opened - string proxylib = 1; - - // Transparent set of parameters provided for proxylib initialization - map proxylib_params = 2; + // Fields 1 and 2 were used for proxylib (Envoy Go extensions) configuration, + // which has been removed. + reserved 1, 2; + reserved "proxylib", "proxylib_params"; // Path to the unix domain socket for the cilium access log. string access_log_path = 5; diff --git a/cilium/api/npds.proto b/cilium/api/npds.proto index 2d28992ad..4c5a7702e 100644 --- a/cilium/api/npds.proto +++ b/cilium/api/npds.proto @@ -188,9 +188,10 @@ message PortNetworkPolicyRule { } }]; - // Optional L7 protocol parser name. This is only used if the parser is not - // one of the well knows ones. If specified, the l7 parser having this name - // needs to be built in to libcilium.so. + // Optional L7 protocol name. This is only used if the protocol is not + // one of the well known ones. If specified, it is added to the requested + // application protocols of the connection for filter chain matching, and + // names the Envoy filter whose dynamic metadata is matched by 'l7_rules'. string l7_proto = 2; // Optional. If not specified, any L7 request is matched by this predicate. diff --git a/cilium/bpf_metadata.cc b/cilium/bpf_metadata.cc index 1dddaafda..d67552845 100644 --- a/cilium/bpf_metadata.cc +++ b/cilium/bpf_metadata.cc @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -37,7 +38,6 @@ #include "source/common/protobuf/utility.h" #include "absl/strings/string_view.h" -#include "absl/types/optional.h" #include "cilium/api/bpf_metadata.pb.h" #include "cilium/api/bpf_metadata.pb.validate.h" // IWYU pragma: keep #include "cilium/conntrack.h" @@ -397,7 +397,7 @@ const PolicyInstance& Config::getPolicy(const std::string& pod_ip) const { bool Config::exists(const std::string& pod_ip) const { return npmap_->exists(pod_ip); } -absl::optional +std::optional Config::extractSocketMetadata(Network::ConnectionSocket& socket) { // connectionInfoProvider may provide original addresses as carried in PROXY protocol. Thus the // source address may be of a different address family than the destination address. @@ -418,7 +418,7 @@ Config::extractSocketMetadata(Network::ConnectionSocket& socket) { if (!sip || !dip) { ENVOY_LOG(debug, "Non-IP addresses: src: {} dst: {}", src_address->asString(), dst_address->asString()); - return absl::nullopt; + return std::nullopt; } std::string pod_ip, other_ip, ingress_policy_name; @@ -467,7 +467,7 @@ Config::extractSocketMetadata(Network::ConnectionSocket& socket) { "cilium.bpf_metadata (east/west L7 LB): Non-local pod can not use original " "source address: {}", pod_ip); - return absl::nullopt; + return std::nullopt; } // Use original source address with L7 LB for local endpoint sources if requested, as policy // enforcement after the proxy depends on it (i.e., for "east/west" LB). @@ -489,7 +489,7 @@ Config::extractSocketMetadata(Network::ConnectionSocket& socket) { "cilium.bpf_metadata (north/south L7 LB): No local Ingress IP source address configured " "for the family of {}", dip->addressAsString()); - return absl::nullopt; + return std::nullopt; } // Enforce pod policy only for local pods. @@ -512,7 +512,7 @@ Config::extractSocketMetadata(Network::ConnectionSocket& socket) { "cilium.bpf_metadata (north/south L7 LB): Unknown local Ingress IP source address " "configured: {}", ingress_ip->addressAsString()); - return absl::nullopt; + return std::nullopt; } // Original source address is never used for north/south LB @@ -589,13 +589,13 @@ Network::FilterStatus Instance::onAccept(Network::ListenerFilterCallbacks& cb) { const auto policy_fs = socket_metadata->buildCiliumPolicyFilterState(); cb.filterState().setData( Cilium::CiliumPolicyFilterState::key(), policy_fs, - StreamInfo::FilterState::StateType::ReadOnly, StreamInfo::FilterState::LifeSpan::Connection, + StreamInfo::FilterState::LifeSpan::Connection, StreamInfo::StreamSharingMayImpactPooling::SharedWithUpstreamConnection); const auto dest_fs = socket_metadata->buildCiliumDestinationFilterState(); cb.filterState().setData( Cilium::CiliumDestinationFilterState::key(), dest_fs, - StreamInfo::FilterState::StateType::Mutable, StreamInfo::FilterState::LifeSpan::Connection, + StreamInfo::FilterState::LifeSpan::Connection, StreamInfo::StreamSharingMayImpactPooling::SharedWithUpstreamConnection); // Restoring original source address on the upstream socket diff --git a/cilium/bpf_metadata.h b/cilium/bpf_metadata.h index 8ba3c9e5b..918bb6f81 100644 --- a/cilium/bpf_metadata.h +++ b/cilium/bpf_metadata.h @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -18,7 +19,6 @@ #include "source/common/common/logger.h" #include "absl/strings/string_view.h" -#include "absl/types/optional.h" #include "cilium/api/bpf_metadata.pb.h" #include "cilium/conntrack.h" #include "cilium/filter_state_cilium_destination.h" @@ -156,7 +156,7 @@ class Config : public Cilium::PolicyResolver, const PolicyInstance& getPolicy(const std::string&) const override; bool exists(const std::string&) const override; - virtual absl::optional extractSocketMetadata(Network::ConnectionSocket& socket); + virtual std::optional extractSocketMetadata(Network::ConnectionSocket& socket); // Possibility to prevent socket options that require // NET_ADMIN privileges from being applied. Used by tests. diff --git a/cilium/filter_state_cilium_policy.cc b/cilium/filter_state_cilium_policy.cc index 1bf350a92..0053e2c98 100644 --- a/cilium/filter_state_cilium_policy.cc +++ b/cilium/filter_state_cilium_policy.cc @@ -23,10 +23,8 @@ bool CiliumPolicyFilterState::enforceNetworkPolicy(const Network::Connection& co uint32_t destination_identity, uint16_t destination_port, const absl::string_view sni, - /* OUT */ bool& use_proxy_lib, /* OUT */ std::string& l7_proto, /* INOUT */ AccessLog::Entry& log_entry) const { - use_proxy_lib = false; l7_proto = ""; // enforce pod policy first, if any @@ -44,7 +42,7 @@ bool CiliumPolicyFilterState::enforceNetworkPolicy(const Network::Connection& co } // populate l7proto_ if available - use_proxy_lib = port_policy.useProxylib(proxy_id_, remote_id, l7_proto); + port_policy.useProxylib(proxy_id_, remote_id, l7_proto); } // enforce Ingress policy 2nd, if any diff --git a/cilium/filter_state_cilium_policy.h b/cilium/filter_state_cilium_policy.h index 09c65784f..9a78ec0af 100644 --- a/cilium/filter_state_cilium_policy.h +++ b/cilium/filter_state_cilium_policy.h @@ -63,7 +63,6 @@ class CiliumPolicyFilterState : public StreamInfo::FilterState::Object, bool enforceNetworkPolicy(const Network::Connection& conn, uint32_t destination_identity, uint16_t destination_port, const absl::string_view sni, - /* OUT */ bool& use_proxy_lib, /* OUT */ std::string& l7_proto, /* INOUT */ AccessLog::Entry& log_entry) const; diff --git a/cilium/grpc_subscription.cc b/cilium/grpc_subscription.cc index 988eb79d8..7c656aaf4 100644 --- a/cilium/grpc_subscription.cc +++ b/cilium/grpc_subscription.cc @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -38,7 +39,6 @@ #include "absl/status/statusor.h" #include "absl/strings/match.h" #include "absl/strings/string_view.h" -#include "absl/types/optional.h" namespace Envoy { namespace Cilium { @@ -176,8 +176,8 @@ void ManagedGrpcSubscription::create() { rate_limit_settings_or_error.value(), *scope_, std::move(nop_config_validators), - /*xds_resources_delegate_=*/absl::nullopt, - /*xds_config_tracker_=*/absl::nullopt, + /*xds_resources_delegate_=*/std::nullopt, + /*xds_config_tracker_=*/std::nullopt, std::make_unique( Config::SubscriptionFactory::RetryInitialDelayMs, Config::SubscriptionFactory::RetryMaxDelayMs, context_.api().randomGenerator()), diff --git a/cilium/health_check_sink.cc b/cilium/health_check_sink.cc index de7d78d22..90c68426e 100644 --- a/cilium/health_check_sink.cc +++ b/cilium/health_check_sink.cc @@ -52,7 +52,11 @@ void HealthCheckEventPipeSink::log(envoy::data::core::v3::HealthCheckEvent event return; } std::string msg; - event.SerializeToString(&msg); + if (!event.SerializeToString(&msg)) { + ENVOY_LOG_MISC(warn, "HealthCheckEventPipeSink: failed to serialize event, skipping it: {}", + event.DebugString()); + return; + } uds_client_->log(msg); }; diff --git a/cilium/l7policy.cc b/cilium/l7policy.cc index 6cb49750d..32fd1d35e 100644 --- a/cilium/l7policy.cc +++ b/cilium/l7policy.cc @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -31,7 +32,6 @@ #include "absl/status/statusor.h" #include "absl/strings/string_view.h" -#include "absl/types/optional.h" #include "cilium/accesslog.h" #include "cilium/api/accesslog.pb.h" #include "cilium/api/l7policy.pb.h" @@ -98,7 +98,7 @@ void AccessFilter::onDestroy() {} void AccessFilter::sendLocalError(absl::string_view details) { ENVOY_LOG(warn, details); - callbacks_->sendLocalReply(Http::Code::InternalServerError, "", nullptr, absl::nullopt, + callbacks_->sendLocalReply(Http::Code::InternalServerError, "", nullptr, std::nullopt, StringUtil::replaceAllEmptySpace(details)); } @@ -112,7 +112,6 @@ void AccessFilter::setDecoderFilterCallbacks(Http::StreamDecoderFilterCallbacks& auto log_entry = std::make_unique(); log_entry_ = log_entry.get(); callbacks_->streamInfo().filterState()->setData(AccessLogKey, std::move(log_entry), - StreamInfo::FilterState::StateType::Mutable, StreamInfo::FilterState::LifeSpan::Request); } @@ -202,7 +201,7 @@ Http::FilterHeadersStatus AccessFilter::decodeHeaders(Http::RequestHeaderMap& he if (!allowed) { config_->log(*log_entry_, ::cilium::EntryType::Denied); callbacks_->sendLocalReply(Http::Code::Forbidden, config_->denied_403_body_, nullptr, - absl::nullopt, absl::string_view()); + std::nullopt, absl::string_view()); return Http::FilterHeadersStatus::StopIteration; } @@ -272,7 +271,7 @@ Http::FilterHeadersStatus AccessFilter::decodeHeaders(Http::RequestHeaderMap& he if (!allowed) { config_->log(*log_entry_, ::cilium::EntryType::Denied); callbacks_->sendLocalReply(Http::Code::Forbidden, config_->denied_403_body_, nullptr, - absl::nullopt, absl::string_view()); + std::nullopt, absl::string_view()); return Http::FilterHeadersStatus::StopIteration; } } @@ -288,7 +287,7 @@ Http::FilterHeadersStatus AccessFilter::decodeHeaders(Http::RequestHeaderMap& he if (!allowed) { config_->log(*log_entry_, ::cilium::EntryType::Denied); callbacks_->sendLocalReply(Http::Code::Forbidden, config_->denied_403_body_, nullptr, - absl::nullopt, absl::string_view()); + std::nullopt, absl::string_view()); return Http::FilterHeadersStatus::StopIteration; } } diff --git a/cilium/l7policy.h b/cilium/l7policy.h index c3ff812d0..f1fc356b0 100644 --- a/cilium/l7policy.h +++ b/cilium/l7policy.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include "envoy/buffer/buffer.h" @@ -11,11 +12,11 @@ #include "envoy/http/metadata_interface.h" #include "envoy/stats/scope.h" #include "envoy/stats/stats_macros.h" // IWYU pragma: keep +#include "envoy/upstream/host_description.h" #include "source/common/common/logger.h" #include "absl/strings/string_view.h" -#include "absl/types/optional.h" #include "cilium/accesslog.h" #include "cilium/api/accesslog.pb.h" #include "cilium/api/l7policy.pb.h" @@ -72,6 +73,9 @@ class AccessFilter : public Http::StreamFilter, AccessFilter(ConfigSharedPtr& config) : config_(config) {} // UpstreamCallbacks + // Upstream host authorization is enforced by the Cilium network filter via the network-level + // upstream authorization callbacks, so nothing is done here. + void onHostSelected(const Upstream::HostDescriptionConstSharedPtr&) override {} void onUpstreamConnectionEstablished() override; // Http::StreamFilterBase @@ -114,7 +118,7 @@ class AccessFilter : public Http::StreamFilter, AccessLog::Entry* log_entry_ = nullptr; OptRef latched_headers_; - absl::optional latched_end_stream_; + std::optional latched_end_stream_; }; } // namespace Cilium diff --git a/cilium/network_filter.cc b/cilium/network_filter.cc index 201aa65e9..1ee72cbaa 100644 --- a/cilium/network_filter.cc +++ b/cilium/network_filter.cc @@ -12,11 +12,9 @@ #include "envoy/registry/registry.h" #include "envoy/server/factory_context.h" #include "envoy/server/filter_config.h" -#include "envoy/stream_info/filter_state.h" #include "envoy/stream_info/stream_info.h" #include "envoy/upstream/host_description.h" -#include "source/common/buffer/buffer_impl.h" #include "source/common/common/logger.h" #include "source/common/network/upstream_server_name.h" #include "source/common/network/upstream_subject_alt_names.h" @@ -31,8 +29,6 @@ #include "cilium/api/network_filter.pb.validate.h" // IWYU pragma: keep #include "cilium/filter_state_cilium_destination.h" #include "cilium/filter_state_cilium_policy.h" -#include "cilium/proxylib.h" -#include "proxylib/types.h" namespace Envoy { namespace Server { @@ -82,9 +78,6 @@ Config::Config(const ::cilium::NetworkFilter& config, if (!access_log_path.empty()) { access_log_ = Cilium::AccessLog::open(access_log_path, time_source_); } - if (!config.proxylib().empty()) { - proxylib_ = std::make_shared(config.proxylib(), config.proxylib_params()); - } } void Config::log(Cilium::AccessLog::Entry& entry, ::cilium::EntryType type) { @@ -111,9 +104,8 @@ bool Instance::enforceNetworkPolicy(const Cilium::CiliumPolicyFilterState* polic stream_info.downstreamAddressProvider().remoteAddress(), destination_identity, dst_address, &config_->time_source_); - bool use_proxy_lib; - if (!policy_fs->enforceNetworkPolicy(conn, remote_id_, destination_port_, sni, use_proxy_lib, - l7proto_, log_entry_)) { + if (!policy_fs->enforceNetworkPolicy(conn, remote_id_, destination_port_, sni, l7proto_, + log_entry_)) { ENVOY_CONN_LOG(debug, "cilium.network: policy DENY on id: {} port: {} sni: \"{}\"", conn, remote_id_, destination_port_, sni); config_->log(log_entry_, ::cilium::EntryType::Denied); @@ -127,22 +119,6 @@ bool Instance::enforceNetworkPolicy(const Cilium::CiliumPolicyFilterState* polic ENVOY_LOG(debug, "cilium.network: policy ALLOW on id: {} port: {} sni: \"{}\"", remote_id_, destination_port_, sni); - if (use_proxy_lib) { - const std::string& policy_name = policy_fs->pod_ip_; - - // Initialize Go parser if requested - if (config_->proxylib_.get() != nullptr) { - go_parser_ = config_->proxylib_->newInstance( - conn, l7proto_, policy_fs->ingress_, policy_fs->source_identity_, destination_identity, - stream_info.downstreamAddressProvider().remoteAddress()->asString(), - dst_address->asString(), policy_name); - if (go_parser_.get() == nullptr) { - ENVOY_CONN_LOG(warn, "cilium.network: Go parser \"{}\" not found", conn, l7proto_); - return false; - } - } - } - should_buffer_ = false; return true; } @@ -150,11 +126,6 @@ Network::FilterStatus Instance::onNewConnection() { auto& conn = callbacks_->connection(); ENVOY_CONN_LOG(debug, "cilium.network: onNewConnection", conn); - // Buffer data until proxylib policy is available, if configured with proxylib - if (config_->proxylib_.get() != nullptr) { - should_buffer_ = true; - } - const auto policy_fs = conn.streamInfo().filterState()->getDataReadOnly( Cilium::CiliumPolicyFilterState::key()); @@ -196,12 +167,10 @@ Network::FilterStatus Instance::onNewConnection() { Network::UpstreamSubjectAltNames::key()); if (!have_sni && !have_san) { filter_state->setData(Network::UpstreamServerName::key(), - std::make_unique(sni), - StreamInfo::FilterState::StateType::Mutable); + std::make_unique(sni)); filter_state->setData(Network::UpstreamSubjectAltNames::key(), std::make_unique( - std::vector{std::string(sni)}), - StreamInfo::FilterState::StateType::Mutable); + std::vector{std::string(sni)})); } } @@ -276,41 +245,7 @@ Network::FilterStatus Instance::onData(Buffer::Instance& data, bool end_stream) end_stream); const char* reason; - if (should_buffer_) { - // Buffer data until upstream is selected and policy resolved - buffer_.move(data); - return Network::FilterStatus::Continue; - } - // Prepend buffered data if any - if (buffer_.length() > 0) { - data.prepend(buffer_); - } - if (go_parser_) { - FilterResult res = - go_parser_->onIo(false, data, end_stream); // 'false' marks original direction data - ENVOY_CONN_LOG(trace, "cilium.network::onData: \'GoFilter::OnIO\' returned {}", conn, - Envoy::Cilium::toString(res)); - - if (res != FILTER_OK) { - // Drop the connection due to an error - go_parser_->close(); - reason = "proxylib error"; - goto drop_close; - } - - if (go_parser_->wantReplyInject()) { - ENVOY_CONN_LOG(trace, "cilium.network::onData: calling write() on an empty buffer", conn); - - // We have no idea when, if ever new data will be received on the - // reverse direction. Connection write on an empty buffer will cause - // write filter chain to be called, and gives our write path the - // opportunity to inject data. - Buffer::OwnedImpl empty; - conn.write(empty, false); - } - - go_parser_->setOrigEndStream(end_stream); - } else if (!l7proto_.empty()) { + if (!l7proto_.empty()) { const auto& metadata = conn.streamInfo().dynamicMetadata(); const auto& filter_metadata = metadata.filter_metadata(); const auto metadata_it = filter_metadata.find(l7proto_); @@ -352,25 +287,7 @@ Network::FilterStatus Instance::onData(Buffer::Instance& data, bool end_stream) return Network::FilterStatus::StopIteration; } -Network::FilterStatus Instance::onWrite(Buffer::Instance& data, bool end_stream) { - if (go_parser_) { - FilterResult res = - go_parser_->onIo(true, data, end_stream); // 'true' marks reverse direction data - ENVOY_CONN_LOG(trace, "cilium.network::OnWrite: \'GoFilter::OnIO\' returned {}", - callbacks_->connection(), Envoy::Cilium::toString(res)); - - if (res != FILTER_OK) { - // Drop the connection due to an error - go_parser_->close(); - return Network::FilterStatus::StopIteration; - } - - // XXX: Unfortunately continueReading() continues from the next filter, and - // there seems to be no way to trigger the whole filter chain to be called. - - go_parser_->setReplyEndStream(end_stream); - } - +Network::FilterStatus Instance::onWrite(Buffer::Instance&, bool) { return Network::FilterStatus::Continue; } diff --git a/cilium/network_filter.h b/cilium/network_filter.h index 6a8009148..2e70f5f9d 100644 --- a/cilium/network_filter.h +++ b/cilium/network_filter.h @@ -12,7 +12,6 @@ #include "envoy/server/factory_context.h" #include "envoy/stream_info/stream_info.h" -#include "source/common/buffer/buffer_impl.h" #include "source/common/common/logger.h" #include "absl/strings/string_view.h" @@ -21,7 +20,6 @@ #include "cilium/api/network_filter.pb.h" #include "cilium/filter_state_cilium_destination.h" #include "cilium/filter_state_cilium_policy.h" -#include "cilium/proxylib.h" namespace Envoy { namespace Filter { @@ -42,7 +40,6 @@ class Config : Logger::Loggable { void log(Cilium::AccessLog::Entry&, ::cilium::EntryType); - Cilium::GoFilterSharedPtr proxylib_; TimeSource& time_source_; private: @@ -84,9 +81,6 @@ class Instance : public Network::Filter, Logger::Loggable { uint32_t remote_id_ = 0; uint16_t destination_port_ = 0; std::string l7proto_; - bool should_buffer_ = false; - Buffer::OwnedImpl buffer_; // Buffer for initial connection data - Cilium::GoFilter::InstancePtr go_parser_; Cilium::AccessLog::Entry log_entry_{}; }; diff --git a/cilium/network_policy.cc b/cilium/network_policy.cc index dc7959c37..392de38f0 100644 --- a/cilium/network_policy.cc +++ b/cilium/network_policy.cc @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -57,6 +58,7 @@ #include "absl/status/status.h" #include "absl/strings/ascii.h" #include "absl/strings/match.h" +#include "absl/strings/str_join.h" #include "absl/strings/str_replace.h" #include "absl/strings/string_view.h" #include "cilium/accesslog.h" @@ -86,13 +88,18 @@ using RuleVerdict = enum { } // namespace Cilium } // namespace Envoy -namespace fmt { +// Envoy routes ENVOY_LOG() through spdlog, which is built with SPDLOG_USE_STD_FORMAT, so this +// has to be a std::formatter rather than a fmt::formatter. +namespace std { -template <> struct formatter { - constexpr auto parse(fmt::format_parse_context& ctx) { return ctx.begin(); } +// NOLINTNEXTLINE(readability-identifier-naming) +template <> struct formatter { + template constexpr ParseContext::iterator parse(ParseContext& ctx) { + return ctx.begin(); + } - template - auto format(Envoy::Cilium::RuleVerdict verdict, FormatContext& ctx) const { + template + FmtContext::iterator format(Envoy::Cilium::RuleVerdict verdict, FmtContext& ctx) const { absl::string_view name; switch (verdict) { case Envoy::Cilium::RuleVerdict::None: @@ -115,7 +122,7 @@ template <> struct formatter { } }; -} // namespace fmt +} // namespace std namespace Envoy { namespace Cilium { @@ -2110,7 +2117,7 @@ void NetworkPolicyMapImpl::removeInitManager() { warn, "Cilium NetworkPolicyMap parked init manager unexpectedly accumulated targets [{}]{}; " "replacing it before re-installing", - fmt::join(parked_dump.target_names(), ", "), + absl::StrJoin(parked_dump.target_names(), ", "), parked_wrong_state ? fmt::format(" in state {}", static_cast(parked_init_manager_->state())) : ""); diff --git a/cilium/proxylib.cc b/cilium/proxylib.cc deleted file mode 100644 index 126bffd8f..000000000 --- a/cilium/proxylib.cc +++ /dev/null @@ -1,337 +0,0 @@ -#include "cilium/proxylib.h" - -#include -#include - -#include -#include -#include - -#include "envoy/buffer/buffer.h" -#include "envoy/common/exception.h" -#include "envoy/network/connection.h" - -#include "source/common/common/assert.h" -#include "source/common/common/logger.h" -#include "source/common/protobuf/protobuf.h" // IWYU pragma: keep - -#include "absl/container/fixed_array.h" -#include "proxylib/types.h" - -namespace Envoy { -namespace Cilium { - -GoFilter::GoFilter(const std::string& go_module, - const Protobuf::Map<::std::string, ::std::string>& params) { - ENVOY_LOG(info, "GoFilter: Opening go module {}", go_module); - ::dlerror(); // clear any possible error state - go_module_handle_ = ::dlopen(go_module.c_str(), RTLD_NOW); - if (!go_module_handle_) { - throw EnvoyException( - fmt::format("cilium.network: Cannot load go module \'{}\': {}", go_module, dlerror())); - } - - go_close_module_ = GoCloseModuleCB(::dlsym(go_module_handle_, "CloseModule")); - if (!go_close_module_) { - throw EnvoyException(fmt::format("cilium.network: Cannot find symbol \'CloseModule\' from " - "module \'{}\': {}", - go_module, dlerror())); - } - GoOpenModuleCB go_open_module = GoOpenModuleCB(::dlsym(go_module_handle_, "OpenModule")); - if (!go_open_module) { - throw EnvoyException(fmt::format("cilium.network: Cannot find symbol \'OpenModule\' from " - "module \'{}\': {}", - go_module, dlerror())); - } else { - // Convert params to KeyValue pairs - auto num = params.size(); - absl::FixedArray values(num); - - int i = 0; - for (const auto& pair : params) { - values[i].key = GoString(pair.first); - values[i++].value = GoString(pair.second); - } - - go_module_id_ = - go_open_module(GoKeyValueSlice(values.data(), num), ENVOY_LOG_CHECK_LEVEL(debug)); - if (go_module_id_ == 0) { - throw EnvoyException( - fmt::format("cilium.network: \'{}::OpenModule()\' rejected parameters", go_module)); - } - } - - go_on_new_connection_ = GoOnNewConnectionCB(::dlsym(go_module_handle_, "OnNewConnection")); - if (!go_on_new_connection_) { - throw EnvoyException(fmt::format("cilium.network: Cannot find symbol \'OnNewConnection\' " - "from module \'{}\': {}", - go_module, dlerror())); - } - go_on_data_ = GoOnDataCB(::dlsym(go_module_handle_, "OnData")); - if (!go_on_data_) { - throw EnvoyException( - fmt::format("cilium.network: Cannot find symbol \'OnData\' from module \'{}\': {}", - go_module, dlerror())); - } - go_close_ = GoCloseCB(::dlsym(go_module_handle_, "Close")); - if (!go_close_) { - throw EnvoyException( - fmt::format("cilium.network: Cannot find symbol \'Close\' from module \'{}\': {}", - go_module, dlerror())); - } -} - -GoFilter::~GoFilter() { - if (go_module_id_ != 0) { - go_close_module_(go_module_id_); - } - if (go_module_handle_) { - ::dlclose(go_module_handle_); - } -} - -GoFilter::InstancePtr GoFilter::newInstance(Network::Connection& conn, const std::string& go_proto, - bool ingress, uint32_t src_id, uint32_t dst_id, - const std::string& src_addr, - const std::string& dst_addr, - const std::string& policy_name) const { - InstancePtr parser{nullptr}; - if (go_module_handle_) { - parser = std::make_unique(*this, conn); - ENVOY_CONN_LOG(trace, "GoFilter: Calling go module", conn); - auto res = (*go_on_new_connection_)( - go_module_id_, go_proto, conn.id(), ingress, src_id, dst_id, src_addr, dst_addr, - policy_name, &parser->orig_.inject_slice_, &parser->reply_.inject_slice_); - if (res == FILTER_OK) { - parser->connection_id_ = conn.id(); - } else { - ENVOY_CONN_LOG(warn, "Cilium Network: Connection with parser \"{}\" rejected: {}", conn, - go_proto, toString(res)); - parser.reset(nullptr); - } - } - return parser; -} - -FilterResult GoFilter::Instance::onIo(bool reply, Buffer::Instance& data, bool end_stream) { - auto& dir = reply ? reply_ : orig_; - int64_t data_len = data.length(); - - // Pass bytes based on an earlier verdict? - if (dir.pass_bytes_ > 0) { - ASSERT(dir.drop_bytes_ == 0); // Can't drop and pass the same bytes - ASSERT(dir.buffer_.length() == 0); // Passed data is not buffered - ASSERT(dir.need_bytes_ == 0); // Passed bytes can't be needed - // Can return immediately if passing more that we have input. - // May need to process injected data even when there is no input left. - if (dir.pass_bytes_ > data_len) { - if (data_len > 0) { - ENVOY_CONN_LOG(debug, "Cilium Network::OnIO: Passing all input: {} bytes: {} ", conn_, - data_len, data.toString()); - dir.pass_bytes_ -= data_len; - } - return FILTER_OK; // all of 'data' is passed to the next filter - } - // Pass of dir.pass_bytes_ is done after buffer rearrangement below. - // Using the available APIs it is easier to move data from the beginning of - // a buffer to another rather than from the end of a buffer to another. - } else { - // Drop bytes based on an earlier verdict? - if (dir.drop_bytes_ > 0) { - ASSERT(dir.buffer_.length() == 0); // Dropped data is not buffered - ASSERT(dir.need_bytes_ == 0); // Dropped bytes can't be needed - // Can return immediately if passing more that we have input. - // May need to process injected data even when there is no input left. - if (dir.drop_bytes_ > data_len) { - if (data_len > 0) { - ENVOY_CONN_LOG(debug, "Cilium Network::OnIO: Dropping all input: {} bytes: {} ", conn_, - data_len, data.toString()); - dir.drop_bytes_ -= data_len; - data.drain(data_len); - } - return FILTER_OK; // everything was dropped, nothing more to be done - } - ENVOY_CONN_LOG(debug, "Cilium Network::OnIO: Dropping first {} bytes of input: {}", conn_, - dir.drop_bytes_, data.toString()); - data.drain(dir.drop_bytes_); - dir.drop_bytes_ = 0; - // At frame boundary, more data may remain - } - } - - // Move data to the end of the input buffer, use 'data' as the output buffer - dir.buffer_.move(data); - ASSERT(data.length() == 0); - auto& input = dir.buffer_; - int64_t input_len = input.length(); - auto& output = data; - - // Move pre-passed input to output. - // Note that the case of all new input being passed is already taken care of - // above. - if (dir.pass_bytes_ > 0) { - ENVOY_CONN_LOG(debug, "Cilium Network::OnIO: Passing first {} bytes of input: {}", conn_, - input_len, input.toString()); - output.move(input, dir.pass_bytes_); - input_len -= dir.pass_bytes_; - dir.pass_bytes_ = 0; - // At frame boundary, more data may remain - } - - // Output now at frame boundary, output frame(s) injected by the reverse - // direction first - if (dir.inject_slice_.len() > 0) { - ENVOY_CONN_LOG( - debug, "Cilium Network::OnIO: Reverse Injecting: {} bytes: {} ", conn_, - dir.inject_slice_.len(), - std::string(reinterpret_cast(dir.inject_slice_.data_), dir.inject_slice_.len())); - output.add(dir.inject_slice_.data_, dir.inject_slice_.len()); - dir.inject_slice_.reset(); - } - - // Do nothing if we don't have enough input (partial input remains buffered) - if (input_len < dir.need_bytes_) { - return FILTER_OK; - } - dir.need_bytes_ = 0; - - const int max_ops = 16; // Make shorter for testing purposes - FilterOp ops[max_ops]; - GoFilterOpSlice op_slice(ops, max_ops); - - FilterResult res; - bool terminal_op_seen = false; - bool inject_buf_exhausted = false; - - do { - op_slice.reset(); - Buffer::RawSliceVector raw_slices = input.getRawSlices(); - - int64_t total_length = 0; - absl::FixedArray> buffer_slices(raw_slices.size()); - uint64_t non_empty_slices = 0; - for (const Buffer::RawSlice& raw_slice : raw_slices) { - if (raw_slice.len_ > 0) { - buffer_slices[non_empty_slices++] = - GoSlice(reinterpret_cast(raw_slice.mem_), raw_slice.len_); - total_length += raw_slice.len_; - } - } - GoDataSlices input_slices(buffer_slices.begin(), non_empty_slices); - - ENVOY_CONN_LOG(trace, "Cilium Network::OnIO: Calling go module with {} bytes of data", conn_, - total_length); - res = (*parent_.go_on_data_)(connection_id_, reply, end_stream, &input_slices, &op_slice); - ENVOY_CONN_LOG(trace, "Cilium Network::OnIO: \'go_on_data\' returned {}, ops({})", conn_, - toString(res), op_slice.len()); - if (res == FILTER_OK) { - // Process all returned filter operations. - for (int i = 0; i < op_slice.len(); i++) { - auto op = ops[i].op; - auto n_bytes = ops[i].n_bytes; - - if (n_bytes == 0) { - ENVOY_CONN_LOG(warn, "Cilium Network::OnIO: INVALID op ({}) length: {} bytes", conn_, op, - n_bytes); - return FILTER_PARSER_ERROR; - } - - if (terminal_op_seen) { - ENVOY_CONN_LOG(warn, - "Cilium Network::OnIO: Filter operation {} after " - "terminal operation.", - conn_, op); - return FILTER_PARSER_ERROR; - } - - switch (op) { - case FILTEROP_MORE: - ENVOY_CONN_LOG(debug, "Cilium Network::OnIO: FILTEROP_MORE: {} bytes", conn_, n_bytes); - dir.need_bytes_ = input_len + n_bytes; - terminal_op_seen = true; // MORE can not be followed with other ops. - continue; // errors out if more operations follow - - case FILTEROP_PASS: - ENVOY_CONN_LOG(debug, "Cilium Network::OnIO: FILTEROP_PASS: {} bytes", conn_, n_bytes); - if (n_bytes > input_len) { - output.move(input, input_len); - dir.pass_bytes_ = n_bytes - input_len; // pass the remainder later - input_len = 0; - terminal_op_seen = true; // PASS more than input is terminal operation. - continue; // errors out if more operations follow - } - output.move(input, n_bytes); - input_len -= n_bytes; - break; - - case FILTEROP_DROP: - ENVOY_CONN_LOG(debug, "Cilium Network::OnIO: FILTEROP_DROP: {} bytes", conn_, n_bytes); - if (n_bytes > input_len) { - input.drain(input_len); - dir.drop_bytes_ = n_bytes - input_len; // drop the remainder later - input_len = 0; - terminal_op_seen = true; // DROP more than input is terminal operation. - continue; // errors out if more operations follow - } - input.drain(n_bytes); - input_len -= n_bytes; - break; - - case FILTEROP_INJECT: - if (n_bytes > dir.inject_slice_.len()) { - ENVOY_CONN_LOG(warn, - "Cilium Network::OnIO: FILTEROP_INJECT: INVALID " - "length: {} bytes", - conn_, n_bytes); - return FILTER_PARSER_ERROR; - } - ENVOY_CONN_LOG(debug, "Cilium Network::OnIO: FILTEROP_INJECT: {} bytes: {}", conn_, - n_bytes, - std::string(reinterpret_cast(dir.inject_slice_.data_), - dir.inject_slice_.len())); - output.add(dir.inject_slice_.data_, n_bytes); - dir.inject_slice_.drain(n_bytes); - break; - - case FILTEROP_ERROR: - default: - ENVOY_CONN_LOG(warn, "Cilium Network::OnIO: FILTEROP_ERROR: {} bytes", conn_, n_bytes); - return FILTER_PARSER_ERROR; - } - } - } else { - // Close the connection an any error - ENVOY_CONN_LOG(warn, "Cilium Network::OnIO: FILTER_POLICY_DROP {}", conn_, toString(res)); - return FILTER_PARSER_ERROR; - } - - if (dir.inject_slice_.len() > 0) { - ENVOY_CONN_LOG(warn, "Cilium Network::OnIO: {} bytes abandoned in inject buffer", conn_, - dir.inject_slice_.len()); - return FILTER_PARSER_ERROR; - } - - inject_buf_exhausted = dir.inject_slice_.atCapacity(); - - // Make space for more injected data - dir.inject_slice_.reset(); - - // Loop back if ops or inject buffer was exhausted - } while (!terminal_op_seen && (op_slice.len() == max_ops || inject_buf_exhausted)); - - if (output.length() < 100) { - ENVOY_CONN_LOG(debug, "Cilium Network::OnIO: Output on return: {}", conn_, output.toString()); - } else { - ENVOY_CONN_LOG(debug, "Cilium Network::OnIO: Output length return: {}", conn_, output.length()); - } - return res; -} - -void GoFilter::Instance::close() { - (*parent_.go_close_)(connection_id_); - connection_id_ = 0; - conn_.close(Network::ConnectionCloseType::FlushWrite); -} - -} // namespace Cilium -} // namespace Envoy diff --git a/cilium/proxylib.h b/cilium/proxylib.h deleted file mode 100644 index f826d159e..000000000 --- a/cilium/proxylib.h +++ /dev/null @@ -1,187 +0,0 @@ -#pragma once - -#include - -#include -#include -#include - -#include "envoy/buffer/buffer.h" -#include "envoy/network/connection.h" - -#include "source/common/buffer/buffer_impl.h" -#include "source/common/common/logger.h" - -#include "proxylib/libcilium.h" -#include "proxylib/types.h" - -namespace Envoy { -namespace Cilium { - -struct GoString { - GoString(const std::string& str) : mem_(str.c_str()), len_(str.length()) {} - GoString() : mem_(nullptr), len_(0) {} - - const char* mem_; - GoInt len_; -}; - -template struct GoSlice { - GoSlice() : data_(nullptr), len_(0) {} - GoSlice(T* data, GoInt len) : data_(data), len_(len), cap_(len) {} // Initialized as full - GoInt len() const { return len_; } - GoInt cap() const { return cap_; } - T& operator[](GoInt x) { return data_[x]; } - const T& operator[](GoInt x) const { return data_[x]; } - operator T*() { return data_; } - operator const T*() const { return data_; } - operator void*() { return data_; } - operator const void*() const { return data_; } - - T* data_; - GoInt len_; - GoInt cap_; -}; - -inline std::string toString(const FilterResult res) { - switch (res) { - case FILTER_OK: - return "No error"; - case FILTER_PARSER_ERROR: - return "Parser error"; - case FILTER_UNKNOWN_CONNECTION: - return "Unknown connection"; - case FILTER_UNKNOWN_PARSER: - return "Unknown parser"; - case FILTER_INVALID_ADDRESS: - return "Invalid address"; - case FILTER_POLICY_DROP: - return "Connection rejected"; - case FILTER_INVALID_INSTANCE: - return "Invalid proxylib instance"; - case FILTER_UNKNOWN_ERROR: - break; - } - return "Unknown error"; -} - -// Slice that remembers the base pointer and that can be reset. -// Note that these have more header data than GoSlices and therefore may not -// used as array elements passed to Go! -template struct ResetableSlice : GoSlice { - // Templated base class member access is a bit ugly - using GoSlice::data_; - using GoSlice::len_; - using GoSlice::cap_; - - ResetableSlice(T* data, GoInt cap) : GoSlice(data, cap), base_(data) { - len_ = 0; // Init as empty - } - - // Non-Go helpers to consume data filled in by Go. Must reset() before slice - // used by Go again. - GoInt drain(GoInt len) { - if (len > len_) { - len = len_; - } - data_ += len; - len_ -= len; - - return len; - } - bool atCapacity() { - // Return true if all of the available space was used, not affected by - // draining - return (data_ + len_) >= (base_ + cap_); - } - void reset() { - data_ = base_; - len_ = 0; - } - - // private part not visible to Go - T* base_; -}; - -struct GoStringPair { - GoString key; - GoString value; -}; - -using GoKeyValueSlice = GoSlice; -using GoOpenModuleCB = uint64_t (*)(GoKeyValueSlice, bool); -using GoCloseModuleCB = void (*)(uint64_t); - -using GoBufferSlice = ResetableSlice; -using GoOnNewConnectionCB = FilterResult (*)(uint64_t, GoString, uint64_t, bool, uint32_t, uint32_t, - GoString, GoString, GoString, GoBufferSlice*, - GoBufferSlice*); - -using GoDataSlices = GoSlice>; // Scatter-gather buffer list as '[][]byte' -using GoFilterOpSlice = ResetableSlice; -using GoOnDataCB = FilterResult (*)(uint64_t, bool, bool, GoDataSlices*, GoFilterOpSlice*); -using GoCloseCB = void (*)(uint64_t); - -class GoFilter : public Logger::Loggable { -public: - GoFilter(const std::string& go_module, const Protobuf::Map<::std::string, ::std::string>&); - ~GoFilter(); - - class Instance : public Logger::Loggable { - public: - Instance(const GoFilter& parent, Network::Connection& conn) : parent_(parent), conn_(conn) {} - ~Instance() { - if (connection_id_) { - // Tell Go parser to scrap the state kept for the connection - (*parent_.go_close_)(connection_id_); - } - } - - void close(); - - FilterResult onIo(bool reply, Buffer::Instance& data, bool end_stream); - - bool wantReplyInject() const { return reply_.wantToInject(); } - void setOrigEndStream(bool end_stream) { orig_.closed_ = end_stream; } - void setReplyEndStream(bool end_stream) { reply_.closed_ = end_stream; } - - struct Direction { - Direction() : inject_slice_(inject_buf_, sizeof(inject_buf_)) {} - - bool wantToInject() const { return !closed_ && inject_slice_.len() > 0; } - void close() { closed_ = true; } - - Buffer::OwnedImpl buffer_; // Buffered data in this direction - int64_t need_bytes_{0}; // Number of additional data bytes needed before can parse again - int64_t pass_bytes_{0}; // Number of bytes to pass without calling the parser again - int64_t drop_bytes_{0}; - bool closed_{false}; - GoBufferSlice inject_slice_; - uint8_t inject_buf_[1024]; - }; - - const GoFilter& parent_; - Network::Connection& conn_; - Direction orig_; - Direction reply_; - uint64_t connection_id_ = 0; - }; - using InstancePtr = std::unique_ptr; - - InstancePtr newInstance(Network::Connection& conn, const std::string& go_proto, bool ingress, - uint32_t src_id, uint32_t dst_id, const std::string& src_addr, - const std::string& dst_addr, const std::string& policy_name) const; - -private: - void* go_module_handle_{nullptr}; - GoCloseModuleCB go_close_module_; - GoOnNewConnectionCB go_on_new_connection_; - GoOnDataCB go_on_data_; - GoCloseCB go_close_; - uint64_t go_module_id_{0}; -}; - -using GoFilterSharedPtr = std::shared_ptr; - -} // namespace Cilium -} // namespace Envoy diff --git a/cilium/socket_option_cilium_mark.h b/cilium/socket_option_cilium_mark.h index 494d71b00..07106522b 100644 --- a/cilium/socket_option_cilium_mark.h +++ b/cilium/socket_option_cilium_mark.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include "envoy/config/core/v3/socket_option.pb.h" @@ -8,8 +9,6 @@ #include "source/common/common/logger.h" -#include "absl/types/optional.h" - namespace Envoy { namespace Cilium { @@ -21,10 +20,10 @@ class CiliumMarkSocketOption : public Network::Socket::Option, public Logger::Loggable { public: CiliumMarkSocketOption(uint32_t mark); - absl::optional + std::optional getOptionDetails(const Network::Socket&, envoy::config::core::v3::SocketOption::SocketState) const override { - return absl::nullopt; + return std::nullopt; } bool setOption(Network::Socket& socket, diff --git a/cilium/socket_option_ip_transparent.h b/cilium/socket_option_ip_transparent.h index 1f54fb9a5..7d0509ea4 100644 --- a/cilium/socket_option_ip_transparent.h +++ b/cilium/socket_option_ip_transparent.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include "envoy/config/core/v3/socket_option.pb.h" @@ -8,8 +9,6 @@ #include "source/common/common/logger.h" -#include "absl/types/optional.h" - namespace Envoy { namespace Cilium { @@ -21,10 +20,10 @@ class IpTransparentSocketOption : public Network::Socket::Option, public: IpTransparentSocketOption(); - absl::optional + std::optional getOptionDetails(const Network::Socket&, envoy::config::core::v3::SocketOption::SocketState) const override { - return absl::nullopt; + return std::nullopt; } bool setOption(Network::Socket& socket, diff --git a/cilium/socket_option_source_address.h b/cilium/socket_option_source_address.h index 9a90b465f..2e256f4a3 100644 --- a/cilium/socket_option_source_address.h +++ b/cilium/socket_option_source_address.h @@ -2,6 +2,7 @@ #include #include +#include #include #include "envoy/config/core/v3/socket_option.pb.h" @@ -10,7 +11,6 @@ #include "source/common/common/logger.h" -#include "absl/types/optional.h" #include "cilium/filter_state_cilium_destination.h" #include "cilium/filter_state_cilium_policy.h" @@ -33,10 +33,10 @@ class SourceAddressSocketOption : public Network::Socket::Option, std::shared_ptr dest_fs = nullptr, std::shared_ptr policy_fs = nullptr); - absl::optional + std::optional getOptionDetails(const Network::Socket&, envoy::config::core::v3::SocketOption::SocketState) const override { - return absl::nullopt; + return std::nullopt; } bool setOption(Network::Socket& socket, diff --git a/cilium/tls_wrapper.cc b/cilium/tls_wrapper.cc index 416004c59..091aa8f9a 100644 --- a/cilium/tls_wrapper.cc +++ b/cilium/tls_wrapper.cc @@ -73,9 +73,9 @@ class SslSocketWrapper : public Network::TransportSocket, Logger::LoggablecloseSocket(type); + socket_->closeSocket(type, abort_reset); } } diff --git a/cilium/websocket.cc b/cilium/websocket.cc index c84a2b279..2506fee57 100644 --- a/cilium/websocket.cc +++ b/cilium/websocket.cc @@ -123,11 +123,9 @@ void Instance::initializeReadFilterCallbacks(Network::ReadFilterCallbacks& callb // Tell TcpProxy to not disable read so that we do WebSocket handshake before upstream // connection is established. - // Use Mutable StateType so that tests can have both client and server filters in the same - // filter chain. callbacks_->connection().streamInfo().filterState()->setData( TcpProxy::ReceiveBeforeConnectKey, std::make_unique(true), - StreamInfo::FilterState::StateType::Mutable, StreamInfo::FilterState::LifeSpan::Connection); + StreamInfo::FilterState::LifeSpan::Connection); } Network::FilterStatus Instance::onNewConnection() { diff --git a/cilium/websocket_config.cc b/cilium/websocket_config.cc index 1346e8be2..1a7d1bdb0 100644 --- a/cilium/websocket_config.cc +++ b/cilium/websocket_config.cc @@ -6,6 +6,7 @@ #include #include #include +#include #include #include "envoy/buffer/buffer.h" @@ -60,7 +61,7 @@ Config::Config(Server::Configuration::FactoryContext& context, bool client, handshake_timeout_(std::chrono::seconds(5)), ping_interval_(std::chrono::milliseconds(0)), ping_when_idle_(ping_when_idle), access_log_(nullptr) { envoy::extensions::filters::network::http_connection_manager::v3::RequestIDExtension x_rid_config; - x_rid_config.mutable_typed_config()->PackFrom( + std::ignore = x_rid_config.mutable_typed_config()->PackFrom( envoy::extensions::request_id::uuid::v3::UuidRequestIdConfig()); auto extension_or_error = Http::RequestIDExtensionFactory::fromProto(x_rid_config, context); THROW_IF_NOT_OK_REF(extension_or_error.status()); diff --git a/envoy.bazelrc b/envoy.bazelrc index 8a67880c1..935475d33 100644 --- a/envoy.bazelrc +++ b/envoy.bazelrc @@ -20,6 +20,10 @@ startup --host_jvm_args="-DBAZEL_TRACK_SOURCE_DIRECTORIES=1" ############################################################################# common --noenable_bzlmod +common --enable_workspace +common --noincompatible_disallow_empty_glob +common --noincompatible_disallow_ctx_resolve_tools +common --legacy_external_runfiles fetch --color=yes run --color=yes @@ -32,6 +36,8 @@ build --java_runtime_version=remotejdk_11 build --tool_java_runtime_version=remotejdk_11 build --java_language_version=11 build --tool_java_language_version=11 +# TODO(jwendell): Remove after https://github.com/protocolbuffers/protobuf/issues/20760 is fixed. +build --extra_toolchains=@envoy//bazel:envoy_java_toolchain_definition # silence absl logspam. build --copt=-DABSL_MIN_LOG_LEVEL=4 # Global C++ standard and common warning suppressions @@ -59,7 +65,7 @@ test --test_verbose_timeout_warnings test --experimental_ui_max_stdouterr_bytes=11712829 #default 1048576 # Allow tags to influence execution requirements -common --experimental_allow_tags_propagation +common --incompatible_allow_tags_propagation # Python common --@rules_python//python/config_settings:bootstrap_impl=script diff --git a/envoy_build_config/extensions_build_config.bzl b/envoy_build_config/extensions_build_config.bzl index d19115e75..c81c258cb 100644 --- a/envoy_build_config/extensions_build_config.bzl +++ b/envoy_build_config/extensions_build_config.bzl @@ -71,11 +71,13 @@ EXTENSIONS = { # "envoy.bootstrap.reverse_tunnel.downstream_socket_interface": "//source/extensions/bootstrap/reverse_tunnel/downstream_socket_interface:reverse_tunnel_initiator_lib", # "envoy.bootstrap.reverse_tunnel.upstream_socket_interface": "//source/extensions/bootstrap/reverse_tunnel/upstream_socket_interface:reverse_tunnel_acceptor_lib", + # "envoy.filters.upstream_network.reverse_tunnel_lifecycle": "//source/extensions/bootstrap/reverse_tunnel/upstream_socket_interface:reverse_tunnel_acceptor_lib", # # Health checkers # + # "envoy.health_checkers.dynamic_modules": "//source/extensions/health_checkers/dynamic_modules:config", "envoy.health_checkers.redis": "//source/extensions/health_checkers/redis:config", "envoy.health_checkers.thrift": "//source/extensions/health_checkers/thrift:config", "envoy.health_checkers.tcp": "//source/extensions/health_checkers/tcp:health_checker_lib", @@ -160,11 +162,13 @@ EXTENSIONS = { # "envoy.filters.http.a2a": "//source/extensions/filters/http/a2a:config", # "envoy.filters.http.adaptive_concurrency": "//source/extensions/filters/http/adaptive_concurrency:config", # "envoy.filters.http.admission_control": "//source/extensions/filters/http/admission_control:config", + # "envoy.filters.http.ai_protocol_manager": "//source/extensions/filters/http/ai_protocol_manager:config", # "envoy.filters.http.alternate_protocols_cache": "//source/extensions/filters/http/alternate_protocols_cache:config", # "envoy.filters.http.api_key_auth": "//source/extensions/filters/http/api_key_auth:config", # "envoy.filters.http.aws_lambda": "//source/extensions/filters/http/aws_lambda:config", # "envoy.filters.http.aws_request_signing": "//source/extensions/filters/http/aws_request_signing:config", # "envoy.filters.http.bandwidth_limit": "//source/extensions/filters/http/bandwidth_limit:config", + # "envoy.filters.http.bandwidth_share": "//source/extensions/filters/http/bandwidth_share:config", "envoy.filters.http.basic_auth": "//source/extensions/filters/http/basic_auth:config", "envoy.filters.http.buffer": "//source/extensions/filters/http/buffer:config", # "envoy.filters.http.cache": "//source/extensions/filters/http/cache:config", @@ -183,6 +187,7 @@ EXTENSIONS = { "envoy.filters.http.ext_proc": "//source/extensions/filters/http/ext_proc:config", # "envoy.filters.http.fault": "//source/extensions/filters/http/fault:config", # "envoy.filters.http.file_server": "//source/extensions/filters/http/file_server:config", + # "envoy.filters.http.filter_chain": "//source/extensions/filters/http/filter_chain:config", # "envoy.filters.http.file_system_buffer": "//source/extensions/filters/http/file_system_buffer:config", # "envoy.filters.http.gcp_authn": "//source/extensions/filters/http/gcp_authn:config", # "envoy.filters.http.geoip": "//source/extensions/filters/http/geoip:config", @@ -215,6 +220,7 @@ EXTENSIONS = { # "envoy.filters.http.proto_api_scrubber": "//source/extensions/filters/http/proto_api_scrubber:config", "envoy.filters.http.ratelimit": "//source/extensions/filters/http/ratelimit:config", "envoy.filters.http.rbac": "//source/extensions/filters/http/rbac:config", + # "envoy.filters.http.upstream_rbac": "//source/extensions/filters/http/upstream_rbac:config", "envoy.filters.http.router": "//source/extensions/filters/http/router:config", "envoy.filters.http.set_filter_state": "//source/extensions/filters/http/set_filter_state:config", "envoy.filters.http.set_metadata": "//source/extensions/filters/http/set_metadata:config", @@ -289,6 +295,7 @@ EXTENSIONS = { "envoy.filters.udp.session.http_capsule": "//source/extensions/filters/udp/udp_proxy/session_filters/http_capsule:config", "envoy.filters.udp.session.dynamic_forward_proxy": "//source/extensions/filters/udp/udp_proxy/session_filters/dynamic_forward_proxy:config", + # "envoy.filters.udp.session.ext_authz": "//source/extensions/filters/udp/udp_proxy/session_filters/ext_authz:config", # # Resource monitors @@ -305,6 +312,7 @@ EXTENSIONS = { # # "envoy.stat_sinks.dog_statsd": "//source/extensions/stat_sinks/dog_statsd:config", + # "envoy.stat_sinks.dynamic_modules": "//source/extensions/stat_sinks/dynamic_modules:config", # "envoy.stat_sinks.graphite_statsd": "//source/extensions/stat_sinks/graphite_statsd:config", # "envoy.stat_sinks.hystrix": "//source/extensions/stat_sinks/hystrix:config", "envoy.stat_sinks.metrics_service": "//source/extensions/stat_sinks/metrics_service:config", @@ -356,6 +364,7 @@ EXTENSIONS = { # # "envoy.transport_sockets.alts": "//source/extensions/transport_sockets/alts:config", + # "envoy.transport_sockets.dynamic_modules": "//source/extensions/transport_sockets/dynamic_modules:config", # "envoy.transport_sockets.http_11_proxy": "//source/extensions/transport_sockets/http_11_proxy:upstream_config", "envoy.transport_sockets.upstream_proxy_protocol": "//source/extensions/transport_sockets/proxy_protocol:upstream_config", "envoy.transport_sockets.raw_buffer": "//source/extensions/transport_sockets/raw_buffer:config", @@ -401,6 +410,7 @@ EXTENSIONS = { # "envoy.upstreams.http.dynamic_modules": "//source/extensions/upstreams/http/dynamic_modules:config", "envoy.upstreams.http.http": "//source/extensions/upstreams/http/http:config", + # "envoy.upstreams.http.reverse_tunnel": "//source/extensions/upstreams/http/reverse_tunnel:config", "envoy.upstreams.http.tcp": "//source/extensions/upstreams/http/tcp:config", "envoy.upstreams.http.udp": "//source/extensions/upstreams/http/udp:config", @@ -509,6 +519,7 @@ EXTENSIONS = { # # "envoy.formatter.cel": "//source/extensions/formatter/cel:config", + # "envoy.formatter.dynamic_modules": "//source/extensions/formatter/dynamic_modules:config", # "envoy.formatter.file_content": "//source/extensions/formatter/file_content:config", # "envoy.formatter.generic_secret": "//source/extensions/formatter/generic_secret:config", # "envoy.formatter.metadata": "//source/extensions/formatter/metadata:config", @@ -546,6 +557,12 @@ EXTENSIONS = { # Hickory DNS resolver extension uses a Rust-based DNS library with support for DoT, DoH, and `DNSSEC`. # "envoy.network.dns_resolver.hickory": "//source/extensions/network/dns_resolver/hickory:config", + # + # Socket interfaces + # + + # "envoy.extensions.network.socket_interface.sockmap": "//source/extensions/network/socket_interface/sockmap:config", + # # Address Resolvers # @@ -580,6 +597,7 @@ EXTENSIONS = { # Load balancing policies for upstream # "envoy.load_balancing_policies.least_request": "//source/extensions/load_balancing_policies/least_request:config", + # "envoy.load_balancing_policies.load_aware_locality": "//source/extensions/load_balancing_policies/load_aware_locality:config", "envoy.load_balancing_policies.random": "//source/extensions/load_balancing_policies/random:config", "envoy.load_balancing_policies.round_robin": "//source/extensions/load_balancing_policies/round_robin:config", "envoy.load_balancing_policies.maglev": "//source/extensions/load_balancing_policies/maglev:config", diff --git a/go.mod b/go.mod index 03150dccc..a65721da4 100644 --- a/go.mod +++ b/go.mod @@ -5,14 +5,9 @@ go 1.25.0 toolchain go1.27.0 require ( - github.com/cilium/kafka v0.0.0-20180809090225-01ce283b732b github.com/envoyproxy/go-control-plane/envoy v1.37.0 github.com/envoyproxy/protoc-gen-validate v1.3.3 - github.com/sirupsen/logrus v1.10.2 - github.com/stretchr/testify v1.12.1 - golang.org/x/sys v0.47.0 google.golang.org/genproto/googleapis/api v0.0.0-20260825221802-da73d73af1c5 - google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5 google.golang.org/grpc v1.83.2 google.golang.org/protobuf v1.36.12 ) @@ -20,17 +15,18 @@ require ( require ( cel.dev/expr v0.25.2 // indirect github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 // indirect - github.com/golang/snappy v0.0.4 // indirect github.com/iancoleman/strcase v0.3.0 // indirect github.com/lyft/protoc-gen-star/v2 v2.0.4 // indirect github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/spf13/afero v1.15.0 // indirect - go.yaml.in/yaml/v3 v3.0.5 // indirect + github.com/stretchr/testify v1.12.1 // indirect golang.org/x/mod v0.38.0 // indirect golang.org/x/net v0.58.0 // indirect golang.org/x/sync v0.22.0 // indirect + golang.org/x/sys v0.47.0 // indirect golang.org/x/text v0.41.0 // indirect golang.org/x/tools v0.48.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20260825221802-da73d73af1c5 // indirect google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.2 // indirect ) diff --git a/go.sum b/go.sum index 2448156e2..b9a64834d 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,6 @@ cel.dev/expr v0.25.2 h1:K6j46C81hXtZQfuX60cVWQFBJahKSE2gfRbNuvr5bFs= cel.dev/expr v0.25.2/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= 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/cilium/kafka v0.0.0-20180809090225-01ce283b732b h1:+bsFX/WOMIoaayXVyRem1awcpz3icz/HoL8Dxg/m6a4= -github.com/cilium/kafka v0.0.0-20180809090225-01ce283b732b/go.mod h1:ktgizta3CPZBKz5uW272SJyjiro0vn4nOVP7Pk4RopA= github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 h1:aBangftG7EVZoUb69Os8IaYg++6uMOdKK83QtkkvJik= github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2/go.mod h1:qwXFYgsP6T7XnJtbKlf1HP8AjxZZyzxMmc+Lq5GjlU4= github.com/envoyproxy/go-control-plane/envoy v1.37.0 h1:u3riX6BoYRfF4Dr7dwSOroNfdSbEPe9Yyl09/B6wBrQ= @@ -16,8 +14,6 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -28,8 +24,6 @@ github.com/lyft/protoc-gen-star/v2 v2.0.4 h1:JDlNKttNIRd68AAIychs0AqEpO8/I/WYi01 github.com/lyft/protoc-gen-star/v2 v2.0.4/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= -github.com/sirupsen/logrus v1.10.2 h1:G2SED73/qrAu6YwbdxOD6peLkCBI3z7L+ykJFTXJBBo= -github.com/sirupsen/logrus v1.10.2/go.mod h1:SLEg8TqYulVKKfIGHldVp2K2aYz2DKSVBq4g/H5bR7Q= github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I= github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg= github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWDWE= diff --git a/go/cilium/api/accesslog.pb.go b/go/cilium/api/accesslog.pb.go index 0dd4946ee..ecf71aa83 100644 --- a/go/cilium/api/accesslog.pb.go +++ b/go/cilium/api/accesslog.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.11 -// protoc v6.33.2 +// protoc-gen-go v1.36.12 +// protoc v7.35.1 // source: cilium/api/accesslog.proto package cilium diff --git a/go/cilium/api/bpf_metadata.pb.go b/go/cilium/api/bpf_metadata.pb.go index 81281988a..fd8edbd42 100644 --- a/go/cilium/api/bpf_metadata.pb.go +++ b/go/cilium/api/bpf_metadata.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.11 -// protoc v6.33.2 +// protoc-gen-go v1.36.12 +// protoc v7.35.1 // source: cilium/api/bpf_metadata.proto package cilium diff --git a/go/cilium/api/health_check_sink.pb.go b/go/cilium/api/health_check_sink.pb.go index 25312c94b..f040be7d9 100644 --- a/go/cilium/api/health_check_sink.pb.go +++ b/go/cilium/api/health_check_sink.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.11 -// protoc v6.33.2 +// protoc-gen-go v1.36.12 +// protoc v7.35.1 // source: cilium/api/health_check_sink.proto package cilium diff --git a/go/cilium/api/l7policy.pb.go b/go/cilium/api/l7policy.pb.go index 78ff1e397..bcc170f1d 100644 --- a/go/cilium/api/l7policy.pb.go +++ b/go/cilium/api/l7policy.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.11 -// protoc v6.33.2 +// protoc-gen-go v1.36.12 +// protoc v7.35.1 // source: cilium/api/l7policy.proto package cilium diff --git a/go/cilium/api/network_filter.pb.go b/go/cilium/api/network_filter.pb.go index 7ef0a00d0..fd4ac5cad 100644 --- a/go/cilium/api/network_filter.pb.go +++ b/go/cilium/api/network_filter.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.11 -// protoc v6.33.2 +// protoc-gen-go v1.36.12 +// protoc v7.35.1 // source: cilium/api/network_filter.proto package cilium @@ -23,10 +23,6 @@ const ( type NetworkFilter struct { state protoimpl.MessageState `protogen:"open.v1"` - // Path to the proxylib to be opened - Proxylib string `protobuf:"bytes,1,opt,name=proxylib,proto3" json:"proxylib,omitempty"` - // Transparent set of parameters provided for proxylib initialization - ProxylibParams map[string]string `protobuf:"bytes,2,rep,name=proxylib_params,json=proxylibParams,proto3" json:"proxylib_params,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` // Path to the unix domain socket for the cilium access log. AccessLogPath string `protobuf:"bytes,5,opt,name=access_log_path,json=accessLogPath,proto3" json:"access_log_path,omitempty"` unknownFields protoimpl.UnknownFields @@ -63,20 +59,6 @@ func (*NetworkFilter) Descriptor() ([]byte, []int) { return file_cilium_api_network_filter_proto_rawDescGZIP(), []int{0} } -func (x *NetworkFilter) GetProxylib() string { - if x != nil { - return x.Proxylib - } - return "" -} - -func (x *NetworkFilter) GetProxylibParams() map[string]string { - if x != nil { - return x.ProxylibParams - } - return nil -} - func (x *NetworkFilter) GetAccessLogPath() string { if x != nil { return x.AccessLogPath @@ -88,14 +70,9 @@ var File_cilium_api_network_filter_proto protoreflect.FileDescriptor const file_cilium_api_network_filter_proto_rawDesc = "" + "\n" + - "\x1fcilium/api/network_filter.proto\x12\x06cilium\"\xea\x01\n" + - "\rNetworkFilter\x12\x1a\n" + - "\bproxylib\x18\x01 \x01(\tR\bproxylib\x12R\n" + - "\x0fproxylib_params\x18\x02 \x03(\v2).cilium.NetworkFilter.ProxylibParamsEntryR\x0eproxylibParams\x12&\n" + - "\x0faccess_log_path\x18\x05 \x01(\tR\raccessLogPath\x1aA\n" + - "\x13ProxylibParamsEntry\x12\x10\n" + - "\x03key\x18\x01 \x01(\tR\x03key\x12\x14\n" + - "\x05value\x18\x02 \x01(\tR\x05value:\x028\x01B.Z,github.com/cilium/proxy/go/cilium/api;ciliumb\x06proto3" + "\x1fcilium/api/network_filter.proto\x12\x06cilium\"^\n" + + "\rNetworkFilter\x12&\n" + + "\x0faccess_log_path\x18\x05 \x01(\tR\raccessLogPathJ\x04\b\x01\x10\x02J\x04\b\x02\x10\x03R\bproxylibR\x0fproxylib_paramsB.Z,github.com/cilium/proxy/go/cilium/api;ciliumb\x06proto3" var ( file_cilium_api_network_filter_proto_rawDescOnce sync.Once @@ -109,18 +86,16 @@ func file_cilium_api_network_filter_proto_rawDescGZIP() []byte { return file_cilium_api_network_filter_proto_rawDescData } -var file_cilium_api_network_filter_proto_msgTypes = make([]protoimpl.MessageInfo, 2) +var file_cilium_api_network_filter_proto_msgTypes = make([]protoimpl.MessageInfo, 1) var file_cilium_api_network_filter_proto_goTypes = []any{ (*NetworkFilter)(nil), // 0: cilium.NetworkFilter - nil, // 1: cilium.NetworkFilter.ProxylibParamsEntry } var file_cilium_api_network_filter_proto_depIdxs = []int32{ - 1, // 0: cilium.NetworkFilter.proxylib_params:type_name -> cilium.NetworkFilter.ProxylibParamsEntry - 1, // [1:1] is the sub-list for method output_type - 1, // [1:1] is the sub-list for method input_type - 1, // [1:1] is the sub-list for extension type_name - 1, // [1:1] is the sub-list for extension extendee - 0, // [0:1] is the sub-list for field type_name + 0, // [0:0] is the sub-list for method output_type + 0, // [0:0] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name } func init() { file_cilium_api_network_filter_proto_init() } @@ -134,7 +109,7 @@ func file_cilium_api_network_filter_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: unsafe.Slice(unsafe.StringData(file_cilium_api_network_filter_proto_rawDesc), len(file_cilium_api_network_filter_proto_rawDesc)), NumEnums: 0, - NumMessages: 2, + NumMessages: 1, NumExtensions: 0, NumServices: 0, }, diff --git a/go/cilium/api/network_filter.pb.validate.go b/go/cilium/api/network_filter.pb.validate.go index 5e2c8af18..fefef53df 100644 --- a/go/cilium/api/network_filter.pb.validate.go +++ b/go/cilium/api/network_filter.pb.validate.go @@ -57,10 +57,6 @@ func (m *NetworkFilter) validate(all bool) error { var errors []error - // no validation rules for Proxylib - - // no validation rules for ProxylibParams - // no validation rules for AccessLogPath if len(errors) > 0 { diff --git a/go/cilium/api/npds.pb.go b/go/cilium/api/npds.pb.go index 5fad4195e..41033d0b3 100644 --- a/go/cilium/api/npds.pb.go +++ b/go/cilium/api/npds.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.11 -// protoc v6.33.2 +// protoc-gen-go v1.36.12 +// protoc v7.35.1 // source: cilium/api/npds.proto package cilium @@ -443,10 +443,10 @@ type PortNetworkPolicyRule struct { // The validation pattern here is synced with the corresponding k8s type in cilium/cilium. // The validation pattern consists of one or more dot-delimited subdomains, where each // subdomain can be: - // - '*', - // - '**', or - // - a pattern of one or more valid DNS name characters, optionally including non-consecutive - // wildcard specifiers ('*') + // - '*', + // - '**', or + // - a pattern of one or more valid DNS name characters, optionally including non-consecutive + // wildcard specifiers ('*') // // The pattern consists of repeating parts: // = "[-a-zA-Z0-9_]" @@ -454,9 +454,10 @@ type PortNetworkPolicyRule struct { // = "([*]{1,2}|)" // PATTERN = "^([.])*$" ServerNames []string `protobuf:"bytes,6,rep,name=server_names,json=serverNames,proto3" json:"server_names,omitempty"` - // Optional L7 protocol parser name. This is only used if the parser is not - // one of the well knows ones. If specified, the l7 parser having this name - // needs to be built in to libcilium.so. + // Optional L7 protocol name. This is only used if the protocol is not + // one of the well known ones. If specified, it is added to the requested + // application protocols of the connection for filter chain matching, and + // names the Envoy filter whose dynamic metadata is matched by 'l7_rules'. L7Proto string `protobuf:"bytes,2,opt,name=l7_proto,json=l7Proto,proto3" json:"l7_proto,omitempty"` // Optional. If not specified, any L7 request is matched by this predicate. // All rules on any given port must have the same type of L7 rules! diff --git a/go/cilium/api/npds_grpc.pb.go b/go/cilium/api/npds_grpc.pb.go index 98ee186bf..fcad83429 100644 --- a/go/cilium/api/npds_grpc.pb.go +++ b/go/cilium/api/npds_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.6.2 -// - protoc v6.33.2 +// - protoc v7.35.1 // source: cilium/api/npds.proto package cilium diff --git a/go/cilium/api/nphds.pb.go b/go/cilium/api/nphds.pb.go index c05898ac0..25a3707e5 100644 --- a/go/cilium/api/nphds.pb.go +++ b/go/cilium/api/nphds.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.11 -// protoc v6.33.2 +// protoc-gen-go v1.36.12 +// protoc v7.35.1 // source: cilium/api/nphds.proto package cilium diff --git a/go/cilium/api/nphds_grpc.pb.go b/go/cilium/api/nphds_grpc.pb.go index 63cd6c3fa..068003bc3 100644 --- a/go/cilium/api/nphds_grpc.pb.go +++ b/go/cilium/api/nphds_grpc.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.6.2 -// - protoc v6.33.2 +// - protoc v7.35.1 // source: cilium/api/nphds.proto package cilium diff --git a/go/cilium/api/tls_wrapper.pb.go b/go/cilium/api/tls_wrapper.pb.go index 7d8aa9f91..bbdd9c040 100644 --- a/go/cilium/api/tls_wrapper.pb.go +++ b/go/cilium/api/tls_wrapper.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.11 -// protoc v6.33.2 +// protoc-gen-go v1.36.12 +// protoc v7.35.1 // source: cilium/api/tls_wrapper.proto package cilium diff --git a/go/cilium/api/websocket.pb.go b/go/cilium/api/websocket.pb.go index b391a530d..5f4cf6992 100644 --- a/go/cilium/api/websocket.pb.go +++ b/go/cilium/api/websocket.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.36.11 -// protoc v6.33.2 +// protoc-gen-go v1.36.12 +// protoc v7.35.1 // source: cilium/api/websocket.proto package cilium diff --git a/patches/0001-network-Add-callback-for-upstream-authorization.patch b/patches/0001-network-Add-callback-for-upstream-authorization.patch index 18b911ee3..22307a170 100644 --- a/patches/0001-network-Add-callback-for-upstream-authorization.patch +++ b/patches/0001-network-Add-callback-for-upstream-authorization.patch @@ -1,7 +1,7 @@ -From 1c0419f207d59b764dca10e7f127efa3390d84f2 Mon Sep 17 00:00:00 2001 +From 4044a8fd8a2552319be18e04f88d78d69d1ca791 Mon Sep 17 00:00:00 2001 From: Jarno Rajahalme Date: Mon, 5 May 2025 11:15:52 +1000 -Subject: [PATCH 1/7] network: Add callback for upstream authorization +Subject: [PATCH 1/6] network: Add callback for upstream authorization Add new ReadFilterCallbacks addUpstreamCallback() and iterateUpstreamCallbacks(). Network filters can add callbacks using @@ -42,10 +42,10 @@ Signed-off-by: Jarno Rajahalme 15 files changed, 124 insertions(+) diff --git a/envoy/http/filter.h b/envoy/http/filter.h -index ef4d2aa7..90aab6b6 100644 +index e14eaef745..af5859fc02 100644 --- a/envoy/http/filter.h +++ b/envoy/http/filter.h -@@ -858,6 +858,14 @@ public: +@@ -915,6 +915,14 @@ public: virtual OptRef upstreamOverrideHost() const PURE; @@ -61,7 +61,7 @@ index ef4d2aa7..90aab6b6 100644 * @return true if the filter should shed load based on the system pressure, typically memory. */ diff --git a/envoy/network/filter.h b/envoy/network/filter.h -index a863eae8..75289a9a 100644 +index 494a60ca82..d61c948092 100644 --- a/envoy/network/filter.h +++ b/envoy/network/filter.h @@ -161,6 +161,22 @@ public: @@ -107,7 +107,7 @@ index a863eae8..75289a9a 100644 * Control the filter close status for read filters. * diff --git a/envoy/tcp/upstream.h b/envoy/tcp/upstream.h -index 78ba8b8c..1daa0e1f 100644 +index 78ba8b8c6a..1daa0e1fa9 100644 --- a/envoy/tcp/upstream.h +++ b/envoy/tcp/upstream.h @@ -84,6 +84,11 @@ public: @@ -123,10 +123,10 @@ index 78ba8b8c..1daa0e1f 100644 // An API for the UpstreamRequest to get callbacks from either an HTTP or TCP diff --git a/source/common/http/async_client_impl.h b/source/common/http/async_client_impl.h -index f73c462c..7bf49e95 100644 +index 6ce7b2d9c5..fd76fa1c3e 100644 --- a/source/common/http/async_client_impl.h +++ b/source/common/http/async_client_impl.h -@@ -293,6 +293,11 @@ private: +@@ -295,6 +295,11 @@ private: ResponseHeaderMapOptRef responseHeaders() override { return {}; } ResponseTrailerMapOptRef responseTrailers() override { return {}; } @@ -139,13 +139,13 @@ index f73c462c..7bf49e95 100644 void dumpState(std::ostream& os, int indent_level) const override { const char* spaces = spacesForLevel(indent_level); diff --git a/source/common/http/conn_manager_impl.h b/source/common/http/conn_manager_impl.h -index 0077c748..65fc1779 100644 +index 564f2f544b..6e26574091 100644 --- a/source/common/http/conn_manager_impl.h +++ b/source/common/http/conn_manager_impl.h -@@ -336,6 +336,12 @@ private: +@@ -343,6 +343,12 @@ private: } - absl::optional routeConfig(); + std::optional routeConfig(); + + bool iterateUpstreamCallbacks(Upstream::HostDescriptionConstSharedPtr host, + StreamInfo::StreamInfo& stream_info) const override { @@ -156,10 +156,10 @@ index 0077c748..65fc1779 100644 // Updates the snapped_route_config_ (by reselecting scoped route configuration), if a scope is diff --git a/source/common/http/filter_manager.cc b/source/common/http/filter_manager.cc -index 67b746f4..20dccfbc 100644 +index 170629b1be..5861b15c28 100644 --- a/source/common/http/filter_manager.cc +++ b/source/common/http/filter_manager.cc -@@ -2009,5 +2009,10 @@ ActiveStreamDecoderFilter::upstreamOverrideHost() const { +@@ -2048,5 +2048,10 @@ ActiveStreamDecoderFilter::upstreamOverrideHost() const { return parent_.upstream_override_host_; } @@ -171,10 +171,10 @@ index 67b746f4..20dccfbc 100644 } // namespace Http } // namespace Envoy diff --git a/source/common/http/filter_manager.h b/source/common/http/filter_manager.h -index a8670d67..93f796ba 100644 +index f52948b8b5..7ade3fddd7 100644 --- a/source/common/http/filter_manager.h +++ b/source/common/http/filter_manager.h -@@ -303,6 +303,8 @@ struct ActiveStreamDecoderFilter : public ActiveStreamFilterBase, +@@ -310,6 +310,8 @@ struct ActiveStreamDecoderFilter : public ActiveStreamFilterBase, void setUpstreamOverrideHost(Upstream::LoadBalancerContext::OverrideHost) override; OptRef upstreamOverrideHost() const override; bool shouldLoadShed() const override; @@ -183,7 +183,7 @@ index a8670d67..93f796ba 100644 void sendGoAwayAndClose(bool graceful = false) override; // Each decoder filter instance checks if the request passed to the filter is gRPC -@@ -592,6 +594,12 @@ public: +@@ -606,6 +608,12 @@ public: * This is used for HTTP/1.1 codec. */ virtual bool isHalfCloseEnabled() PURE; @@ -197,7 +197,7 @@ index a8670d67..93f796ba 100644 /** diff --git a/source/common/network/filter_manager_impl.h b/source/common/network/filter_manager_impl.h -index fff3f2ee..6dc2f867 100644 +index e2923755fd..d879a6e8f8 100644 --- a/source/common/network/filter_manager_impl.h +++ b/source/common/network/filter_manager_impl.h @@ -172,6 +172,13 @@ private: @@ -236,10 +236,10 @@ index fff3f2ee..6dc2f867 100644 const ConnectionSocket& socket_; Upstream::HostDescriptionConstSharedPtr host_description_; diff --git a/source/common/router/router.cc b/source/common/router/router.cc -index cb61c97f..e8d185fb 100644 +index 452f578665..bd6ccea120 100644 --- a/source/common/router/router.cc +++ b/source/common/router/router.cc -@@ -810,6 +810,14 @@ bool Filter::continueDecodeHeaders(Upstream::ThreadLocalCluster* cluster, +@@ -810,6 +810,14 @@ bool Filter::continueDecodeHeaders(Http::RequestHeaderMap& headers, bool end_str return false; } @@ -255,10 +255,10 @@ index cb61c97f..e8d185fb 100644 const Http::HeaderEntry* header_max_stream_duration_entry = headers.EnvoyUpstreamStreamDurationMs(); diff --git a/source/common/router/upstream_request.h b/source/common/router/upstream_request.h -index 8b095cd7..cecdacb7 100644 +index 3c3093ebc4..fb6698ddec 100644 --- a/source/common/router/upstream_request.h +++ b/source/common/router/upstream_request.h -@@ -355,6 +355,11 @@ public: +@@ -360,6 +360,11 @@ public: } OptRef upstreamCallbacks() override { return {*this}; } @@ -269,12 +269,12 @@ index 8b095cd7..cecdacb7 100644 + // Http::UpstreamStreamFilterCallbacks StreamInfo::StreamInfo& upstreamStreamInfo() override { return upstream_request_.streamInfo(); } - OptRef upstream() override { + OptRef downstreamWebTransportSession() override; diff --git a/source/common/tcp_proxy/tcp_proxy.cc b/source/common/tcp_proxy/tcp_proxy.cc -index 9b4597b1..67669507 100644 +index 36101861b5..baa76e24bd 100644 --- a/source/common/tcp_proxy/tcp_proxy.cc +++ b/source/common/tcp_proxy/tcp_proxy.cc -@@ -838,6 +838,13 @@ bool Filter::maybeTunnel(Upstream::ThreadLocalCluster& cluster) { +@@ -850,6 +850,13 @@ bool Filter::maybeTunnel(Upstream::ThreadLocalCluster& cluster) { upstream_decoder_filter_callbacks_, getStreamInfo()); } if (generic_conn_pool_) { @@ -289,10 +289,10 @@ index 9b4597b1..67669507 100644 connect_attempts_++; getStreamInfo().setAttemptCount(connect_attempts_); diff --git a/source/common/tcp_proxy/tcp_proxy.h b/source/common/tcp_proxy/tcp_proxy.h -index f6c82791..3b8267cd 100644 +index 7e2be743e0..ac99d09c75 100644 --- a/source/common/tcp_proxy/tcp_proxy.h +++ b/source/common/tcp_proxy/tcp_proxy.h -@@ -624,6 +624,10 @@ public: +@@ -634,6 +634,10 @@ public: return {}; } bool shouldLoadShed() const override { return false; } @@ -303,7 +303,7 @@ index f6c82791..3b8267cd 100644 void restoreContextOnContinue(ScopeTrackedObjectStack& tracked_object_stack) override { tracked_object_stack.add(*this); } -@@ -667,6 +671,7 @@ protected: +@@ -677,6 +681,7 @@ protected: NoHealthyUpstream, ResourceLimitExceeded, NoRoute, @@ -312,10 +312,10 @@ index f6c82791..3b8267cd 100644 // Callbacks for different error and success states during connection establishment diff --git a/source/common/tcp_proxy/upstream.cc b/source/common/tcp_proxy/upstream.cc -index ba48851e..0fb7be95 100644 +index b4010cf850..298e9f6e11 100644 --- a/source/common/tcp_proxy/upstream.cc +++ b/source/common/tcp_proxy/upstream.cc -@@ -341,6 +341,10 @@ void TcpConnPool::newStream(GenericConnectionPoolCallbacks& callbacks) { +@@ -345,6 +345,10 @@ void TcpConnPool::newStream(GenericConnectionPoolCallbacks& callbacks) { } } @@ -326,7 +326,7 @@ index ba48851e..0fb7be95 100644 void TcpConnPool::onPoolFailure(ConnectionPool::PoolFailureReason reason, absl::string_view failure_reason, Upstream::HostDescriptionConstSharedPtr host) { -@@ -447,6 +451,10 @@ void HttpConnPool::newStream(GenericConnectionPoolCallbacks& callbacks) { +@@ -451,6 +455,10 @@ void HttpConnPool::newStream(GenericConnectionPoolCallbacks& callbacks) { } } @@ -338,10 +338,10 @@ index ba48851e..0fb7be95 100644 absl::string_view failure_reason, Upstream::HostDescriptionConstSharedPtr host) { diff --git a/source/common/tcp_proxy/upstream.h b/source/common/tcp_proxy/upstream.h -index a9b05aaf..53cccdaa 100644 +index efce490a68..40aecea00a 100644 --- a/source/common/tcp_proxy/upstream.h +++ b/source/common/tcp_proxy/upstream.h -@@ -41,6 +41,7 @@ public: +@@ -42,6 +42,7 @@ public: // GenericConnPool void newStream(GenericConnectionPoolCallbacks& callbacks) override; @@ -349,7 +349,7 @@ index a9b05aaf..53cccdaa 100644 // Tcp::ConnectionPool::Callbacks void onPoolFailure(ConnectionPool::PoolFailureReason reason, -@@ -98,6 +99,7 @@ public: +@@ -99,6 +100,7 @@ public: // GenericConnPool void newStream(GenericConnectionPoolCallbacks& callbacks) override; @@ -358,7 +358,7 @@ index a9b05aaf..53cccdaa 100644 // Http::ConnectionPool::Callbacks, void onPoolFailure(ConnectionPool::PoolFailureReason reason, diff --git a/source/extensions/api_listeners/default_api_listener/api_listener_impl.h b/source/extensions/api_listeners/default_api_listener/api_listener_impl.h -index 918016a2..94ffa509 100644 +index 415670d67c..8c807f9d5b 100644 --- a/source/extensions/api_listeners/default_api_listener/api_listener_impl.h +++ b/source/extensions/api_listeners/default_api_listener/api_listener_impl.h @@ -81,6 +81,9 @@ protected: @@ -372,5 +372,5 @@ index 918016a2..94ffa509 100644 // Synthetic class that acts as a stub for the connection backing the // Network::ReadFilterCallbacks. -- -2.54.0 +2.55.0 diff --git a/patches/0002-listener-add-socket-options.patch b/patches/0002-listener-add-socket-options.patch index 2d0c6dcc1..aac653815 100644 --- a/patches/0002-listener-add-socket-options.patch +++ b/patches/0002-listener-add-socket-options.patch @@ -1,43 +1,41 @@ -From b25eed7816bed2a99a5d9b6a29b89cf532081025 Mon Sep 17 00:00:00 2001 +From 7eca64c432be0a808baaf9e6261eb41372a17926 Mon Sep 17 00:00:00 2001 From: Jarno Rajahalme Date: Mon, 14 Aug 2023 10:01:21 +0300 -Subject: [PATCH 2/7] listener: add socket options +Subject: [PATCH 2/6] listener: add socket options This reverts commit 170c89eb0b2afb7a39d44d0f8dfb77444ffc038f. Signed-off-by: Jarno Rajahalme --- - envoy/server/factory_context.h | 8 +++++++- + envoy/server/factory_context.h | 5 +++++ source/common/listener_manager/listener_impl.cc | 3 +++ source/common/listener_manager/listener_impl.h | 9 +++++++++ + source/server/factory_context_impl.h | 8 ++++++++ test/mocks/server/factory_context.h | 1 + test/mocks/server/listener_factory_context.h | 1 + - 5 files changed, 21 insertions(+), 1 deletion(-) + 6 files changed, 27 insertions(+) diff --git a/envoy/server/factory_context.h b/envoy/server/factory_context.h -index ee9fa056..d6b8c7e0 100644 +index e9fcd320fe..a11f7d8730 100644 --- a/envoy/server/factory_context.h +++ b/envoy/server/factory_context.h -@@ -341,7 +341,13 @@ public: - * An implementation of FactoryContext. The life time should cover the lifetime of the filter chains - * and connections. It can be used to create ListenerFilterChain. - */ --class ListenerFactoryContext : public virtual FactoryContext {}; -+class ListenerFactoryContext : public virtual FactoryContext { -+public: +@@ -363,6 +363,11 @@ public: + * @return ListenerInfo description of the listener. + */ + virtual const Network::ListenerInfo& listenerInfo() const PURE; ++ + /** + * Store socket options to be set on the listen socket before listening. + */ + virtual void addListenSocketOptions(const Network::Socket::OptionsSharedPtr& options) PURE; -+}; + }; /** - * FactoryContext for ProtocolOptionsFactory. diff --git a/source/common/listener_manager/listener_impl.cc b/source/common/listener_manager/listener_impl.cc -index 8883d241..74f42afe 100644 +index 82ebb4590e..74db624f77 100644 --- a/source/common/listener_manager/listener_impl.cc +++ b/source/common/listener_manager/listener_impl.cc -@@ -1007,6 +1007,9 @@ Configuration::ServerFactoryContext& PerListenerFactoryContextImpl::serverFactor +@@ -1056,6 +1056,9 @@ Stats::Scope& PerListenerFactoryContextImpl::prefixedScope() { Stats::Scope& PerListenerFactoryContextImpl::listenerScope() { return listener_factory_context_base_->listenerScope(); } @@ -48,11 +46,11 @@ index 8883d241..74f42afe 100644 bool ListenerImpl::createNetworkFilterChain( diff --git a/source/common/listener_manager/listener_impl.h b/source/common/listener_manager/listener_impl.h -index 416728bd..757886c2 100644 +index 4e89f24cde..f5d5ad06dd 100644 --- a/source/common/listener_manager/listener_impl.h +++ b/source/common/listener_manager/listener_impl.h -@@ -187,6 +187,8 @@ public: - +@@ -191,6 +191,8 @@ public: + Stats::Scope& prefixedScope() override; Stats::Scope& listenerScope() override; + void addListenSocketOptions(const Network::Socket::OptionsSharedPtr& options) override; @@ -60,7 +58,7 @@ index 416728bd..757886c2 100644 ListenerFactoryContextBaseImpl& parentFactoryContext() { return *listener_factory_context_base_; } friend class ListenerImpl; -@@ -337,6 +339,13 @@ public: +@@ -341,6 +343,13 @@ public: return listener_factory_context_->listener_factory_context_base_->listener_info_; } @@ -74,12 +72,38 @@ index 416728bd..757886c2 100644 void ensureSocketOptions(Network::Socket::OptionsSharedPtr& options) { if (options == nullptr) { options = std::make_shared>(); +diff --git a/source/server/factory_context_impl.h b/source/server/factory_context_impl.h +index 085f10e34b..ee0098f7ee 100644 +--- a/source/server/factory_context_impl.h ++++ b/source/server/factory_context_impl.h +@@ -3,6 +3,7 @@ + #include "envoy/server/factory_context.h" + #include "envoy/server/instance.h" + ++#include "source/common/common/assert.h" + #include "source/common/config/metadata.h" + + namespace Envoy { +@@ -27,6 +28,13 @@ public: + Stats::Scope& prefixedScope() override; + Stats::Scope& listenerScope() override; + ++ // Configuration::ListenerFactoryContext ++ // These contexts are not tied to a listen socket, so adding listen socket options is not ++ // supported. PerListenerFactoryContextImpl overrides this to forward to its ListenerImpl. ++ void addListenSocketOptions(const Network::Socket::OptionsSharedPtr&) override { ++ IS_ENVOY_BUG("Unexpected function call"); ++ } ++ + protected: + Server::Instance& server_; + ProtobufMessage::ValidationVisitor& validation_visitor_; diff --git a/test/mocks/server/factory_context.h b/test/mocks/server/factory_context.h -index 9fe92ab4..e36fdff5 100644 +index 1d2c8bb633..6fc365a905 100644 --- a/test/mocks/server/factory_context.h +++ b/test/mocks/server/factory_context.h -@@ -31,6 +31,7 @@ public: - MOCK_METHOD(const Network::DrainDecision&, drainDecision, ()); +@@ -38,6 +38,7 @@ public: + // Server::Configuration::ListenerFactoryContext MOCK_METHOD(Stats::Scope&, listenerScope, ()); MOCK_METHOD(const Network::ListenerInfo&, listenerInfo, (), (const)); + MOCK_METHOD(void, addListenSocketOptions, (const Network::Socket::OptionsSharedPtr&)); @@ -87,7 +111,7 @@ index 9fe92ab4..e36fdff5 100644 testing::NiceMock server_factory_context_; testing::NiceMock init_manager_; diff --git a/test/mocks/server/listener_factory_context.h b/test/mocks/server/listener_factory_context.h -index 8b2de57e..1fd30eaf 100644 +index cf2d40afac..ca3051a159 100644 --- a/test/mocks/server/listener_factory_context.h +++ b/test/mocks/server/listener_factory_context.h @@ -22,6 +22,7 @@ public: @@ -99,5 +123,5 @@ index 8b2de57e..1fd30eaf 100644 MOCK_METHOD(const Network::DrainDecision&, drainDecision, ()); MOCK_METHOD(Init::Manager&, initManager, ()); -- -2.54.0 +2.55.0 diff --git a/patches/0003-original_dst_cluster-Avoid-multiple-hosts-for-the-sa.patch b/patches/0003-original_dst_cluster-Avoid-multiple-hosts-for-the-sa.patch index 6b193872f..7d9703e78 100644 --- a/patches/0003-original_dst_cluster-Avoid-multiple-hosts-for-the-sa.patch +++ b/patches/0003-original_dst_cluster-Avoid-multiple-hosts-for-the-sa.patch @@ -1,7 +1,7 @@ -From 435f56e14770f787d97b91c568b93eecb9206056 Mon Sep 17 00:00:00 2001 +From c560662bfd12e46100972f8c94741d810c04aadf Mon Sep 17 00:00:00 2001 From: Jarno Rajahalme Date: Fri, 24 May 2024 18:27:28 +0200 -Subject: [PATCH 3/7] original_dst_cluster: Avoid multiple hosts for the same +Subject: [PATCH 3/6] original_dst_cluster: Avoid multiple hosts for the same address Connection pool containers use HostSharedPtr as map keys, rather than the @@ -25,12 +25,12 @@ map updates. Signed-off-by: Jarno Rajahalme --- - .../original_dst/original_dst_cluster.cc | 259 +++++++++++------- - .../original_dst/original_dst_cluster.h | 47 ++-- - 2 files changed, 191 insertions(+), 115 deletions(-) + .../original_dst/original_dst_cluster.cc | 293 +++++++++++------- + .../original_dst/original_dst_cluster.h | 47 +-- + 2 files changed, 208 insertions(+), 132 deletions(-) diff --git a/source/extensions/clusters/original_dst/original_dst_cluster.cc b/source/extensions/clusters/original_dst/original_dst_cluster.cc -index 25362925..c8063ed8 100644 +index d8ef2862b1..5a8c3e2820 100644 --- a/source/extensions/clusters/original_dst/original_dst_cluster.cc +++ b/source/extensions/clusters/original_dst/original_dst_cluster.cc @@ -29,6 +29,19 @@ OriginalDstClusterHandle::~OriginalDstClusterHandle() { @@ -99,16 +99,16 @@ index 25362925..c8063ed8 100644 } } // TODO(ramaraochavali): add a stat and move this log line to debug. -@@ -198,7 +178,7 @@ OriginalDstCluster::OriginalDstCluster(const envoy::config::cluster::v3::Cluster +@@ -199,7 +179,7 @@ OriginalDstCluster::OriginalDstCluster( cleanup_interval_ms_( std::chrono::milliseconds(PROTOBUF_GET_MS_OR_DEFAULT(config, cleanup_interval, 5000))), cleanup_timer_(dispatcher_.createTimer([this]() -> void { cleanup(); })), - host_map_(std::make_shared()) { + host_map_(std::make_shared()), updates_map_(std::make_unique()) { - if (config.has_original_dst_lb_config()) { - const auto& lb_config = config.original_dst_lb_config(); - if (lb_config.use_http_header()) { -@@ -216,47 +196,146 @@ OriginalDstCluster::OriginalDstCluster(const envoy::config::cluster::v3::Cluster + if (original_dst_config.use_http_header()) { + http_header_name_ = original_dst_config.http_header_name().empty() + ? Http::Headers::get().EnvoyOriginalDstHost +@@ -214,30 +194,127 @@ OriginalDstCluster::OriginalDstCluster( cleanup_timer_->enableTimer(cleanup_interval_ms_); } @@ -144,9 +144,7 @@ index 25362925..c8063ed8 100644 + if (dst_ip == nullptr) { + ENVOY_LOG(debug, "Cannot create host for non-IP address {}.", address); + return nullptr; - } -- ENVOY_LOG(debug, "addHost() adding {} {}.", *host, address); -- setHostMap(new_host_map); ++ } + + // Scope the lock for reading the host_map_ + { @@ -211,7 +209,7 @@ index 25362925..c8063ed8 100644 + return host; +} + -+// updateHosts updates the host map and the priotiry sets of the cluster. ++// updateHosts updates the host map and the priority sets of the cluster. +void OriginalDstCluster::updateHosts() { + ASSERT_IS_MAIN_OR_TEST_THREAD(); + @@ -220,7 +218,6 @@ index 25362925..c8063ed8 100644 + auto new_host_map = std::make_shared(*getHostMap()); + auto empty_map = std::make_unique(); + HostVector new_hosts; -+ new_hosts.reserve(4); // try avoid allocation while holding locks below + + // Consolidate updates into the new host map + // Loadbalancers can not add any updates while we keep these locks, so keep this short! @@ -242,20 +239,33 @@ index 25362925..c8063ed8 100644 + // Make available for load balancers + host_map_ = new_host_map; + updates_map_.swap(empty_map); -+ } + } +- ENVOY_LOG(debug, "addHost() adding {} {}.", *host, address); +- setHostMap(new_host_map); -- // Given the current config, only EDS clusters support multiple priorities. + // Given the current config, only EDS clusters support multiple priorities. ASSERT(priority_set_.hostSetsPerPriority().size() == 1); const auto& first_host_set = priority_set_.getOrCreateHostSet(0); HostVectorSharedPtr all_hosts(new HostVector(first_host_set.hosts())); - all_hosts->emplace_back(host); -+ for (auto host : new_hosts) { ++ for (const auto& host : new_hosts) { + all_hosts->emplace_back(host); + } - priority_set_.updateHosts(0, - HostSetImpl::partitionHosts(all_hosts, HostsPerLocalityImpl::empty()), -- {}, {std::move(host)}, {}, absl::nullopt, absl::nullopt); -+ {}, {std::move(new_hosts)}, {}, absl::nullopt, absl::nullopt); + if (Runtime::runtimeFeatureEnabled( + "envoy.reloadable_features.skip_partition_original_dst_hosts")) { + // OriginalDstCluster::LoadBalancer selects the exact destination address from host_map_ and +@@ -252,26 +329,28 @@ void OriginalDstCluster::addHost(HostSharedPtr& host) { + std::move(healthy_hosts), HostsPerLocalityImpl::empty(), + std::move(degraded_hosts), HostsPerLocalityImpl::empty(), + std::move(excluded_hosts), HostsPerLocalityImpl::empty()), +- {}, {std::move(host)}, {}, std::nullopt, std::nullopt); ++ {}, {std::move(new_hosts)}, {}, std::nullopt, std::nullopt); + } else { + priority_set_.updateHosts(0, + HostSetImpl::partitionHosts(all_hosts, HostsPerLocalityImpl::empty()), +- {}, {std::move(host)}, {}, std::nullopt, std::nullopt); ++ {}, {std::move(new_hosts)}, {}, std::nullopt, std::nullopt); + } } void OriginalDstCluster::cleanup() { @@ -283,7 +293,7 @@ index 25362925..c8063ed8 100644 // // Using the used_ bit is preserved for backwards compatibility and to // add a delay between load balancers choosing a host and grabbing a -@@ -271,49 +350,41 @@ void OriginalDstCluster::cleanup() { +@@ -286,65 +365,57 @@ void OriginalDstCluster::cleanup() { // 3) will not delete h since it takes at least one cleanup_interval for // the host to set used_ bit for h to false. bool keep = false; @@ -331,6 +341,27 @@ index 25362925..c8063ed8 100644 - HostMultiMapSharedPtr new_host_map = std::make_shared(*host_map); - for (const auto& addr : removed_addresses) { - new_host_map->erase(addr); +- } +- setHostMap(new_host_map); +- if (Runtime::runtimeFeatureEnabled( +- "envoy.reloadable_features.skip_partition_original_dst_hosts")) { +- // OriginalDstCluster::LoadBalancer selects the exact destination address from host_map_ and +- // does not consult HostSet health partitions. Preserve all hosts in healthy_hosts so that +- // host set updates expose the complete routable destination set. +- auto healthy_hosts = std::make_shared(*keeping_hosts); +- auto degraded_hosts = std::make_shared(); +- auto excluded_hosts = std::make_shared(); +- priority_set_.updateHosts( +- 0, +- HostSetImpl::updateHostsParams(std::move(keeping_hosts), HostsPerLocalityImpl::empty(), +- std::move(healthy_hosts), HostsPerLocalityImpl::empty(), +- std::move(degraded_hosts), HostsPerLocalityImpl::empty(), +- std::move(excluded_hosts), HostsPerLocalityImpl::empty()), +- {}, {}, to_be_removed, false, std::nullopt); +- } else { +- priority_set_.updateHosts( +- 0, HostSetImpl::partitionHosts(keeping_hosts, HostsPerLocalityImpl::empty()), {}, {}, +- to_be_removed, false, std::nullopt); + + if (!to_be_removed.empty()) { + auto new_host_map = std::make_shared(); @@ -343,22 +374,34 @@ index 25362925..c8063ed8 100644 + + setHostMap(new_host_map); + -+ priority_set_.updateHosts( -+ 0, HostSetImpl::partitionHosts(keeping_hosts, HostsPerLocalityImpl::empty()), {}, {}, -+ to_be_removed, false, absl::nullopt); ++ if (Runtime::runtimeFeatureEnabled( ++ "envoy.reloadable_features.skip_partition_original_dst_hosts")) { ++ // OriginalDstCluster::LoadBalancer selects the exact destination address from host_map_ and ++ // does not consult HostSet health partitions. Preserve all hosts in healthy_hosts so that ++ // host set updates expose the complete routable destination set. ++ auto healthy_hosts = std::make_shared(*keeping_hosts); ++ auto degraded_hosts = std::make_shared(); ++ auto excluded_hosts = std::make_shared(); ++ priority_set_.updateHosts( ++ 0, ++ HostSetImpl::updateHostsParams(std::move(keeping_hosts), HostsPerLocalityImpl::empty(), ++ std::move(healthy_hosts), HostsPerLocalityImpl::empty(), ++ std::move(degraded_hosts), HostsPerLocalityImpl::empty(), ++ std::move(excluded_hosts), HostsPerLocalityImpl::empty()), ++ {}, {}, to_be_removed, false, std::nullopt); ++ } else { ++ priority_set_.updateHosts( ++ 0, HostSetImpl::partitionHosts(keeping_hosts, HostsPerLocalityImpl::empty()), {}, {}, ++ to_be_removed, false, std::nullopt); ++ } } -- setHostMap(new_host_map); -- priority_set_.updateHosts( -- 0, HostSetImpl::partitionHosts(keeping_hosts, HostsPerLocalityImpl::empty()), {}, {}, -- to_be_removed, false, absl::nullopt); } - cleanup_timer_->enableTimer(cleanup_interval_ms_); diff --git a/source/extensions/clusters/original_dst/original_dst_cluster.h b/source/extensions/clusters/original_dst/original_dst_cluster.h -index 55905560..3152af86 100644 +index ff665dc300..818b8c31ff 100644 --- a/source/extensions/clusters/original_dst/original_dst_cluster.h +++ b/source/extensions/clusters/original_dst/original_dst_cluster.h -@@ -22,25 +22,21 @@ namespace Upstream { +@@ -24,25 +24,21 @@ namespace Upstream { class OriginalDstClusterFactory; class OriginalDstClusterTest; @@ -393,7 +436,7 @@ index 55905560..3152af86 100644 class OriginalDstCluster; -@@ -65,7 +61,8 @@ using OriginalDstClusterHandleSharedPtr = std::shared_ptr& http_header_name_; - const absl::optional& metadata_key_; - const absl::optional port_override_; +@@ -127,7 +124,7 @@ public: + const std::optional& http_header_name_; + const std::optional& metadata_key_; + const std::optional port_override_; - HostMultiMapConstSharedPtr host_map_; + HostUseMapConstSharedPtr host_map_; + Common::CallbackHandlePtr member_update_cb_; }; - const absl::optional& httpHeaderName() { return http_header_name_; } -@@ -158,17 +155,23 @@ private: +@@ -169,17 +166,23 @@ private: const OriginalDstClusterHandleSharedPtr cluster_; }; @@ -441,7 +484,7 @@ index 55905560..3152af86 100644 void cleanup(); // ClusterImplBase -@@ -179,7 +182,9 @@ private: +@@ -190,7 +193,9 @@ private: Event::TimerPtr cleanup_timer_; absl::Mutex host_map_lock_; @@ -449,9 +492,9 @@ index 55905560..3152af86 100644 + HostUseMapConstSharedPtr host_map_ ABSL_GUARDED_BY(host_map_lock_); + absl::Mutex updates_map_lock_ ABSL_ACQUIRED_AFTER(host_map_lock_); + HostUseMapUniquePtr updates_map_ ABSL_GUARDED_BY(updates_map_lock_); - absl::optional http_header_name_; - absl::optional metadata_key_; - absl::optional port_override_; + std::optional http_header_name_; + std::optional metadata_key_; + std::optional port_override_; -- -2.54.0 +2.55.0 diff --git a/patches/0004-thread_local-reset-slot-in-worker-threads-first.patch b/patches/0004-thread_local-reset-slot-in-worker-threads-first.patch index 8465b6a6c..7a98cd2a9 100644 --- a/patches/0004-thread_local-reset-slot-in-worker-threads-first.patch +++ b/patches/0004-thread_local-reset-slot-in-worker-threads-first.patch @@ -1,7 +1,7 @@ -From 2c201ef341cf0090b5bf2507351c923fd7d37173 Mon Sep 17 00:00:00 2001 +From f8c8cff10b0e498cf0e4f5edce427bf93774da38 Mon Sep 17 00:00:00 2001 From: Jarno Rajahalme Date: Mon, 23 Dec 2024 22:43:15 +0100 -Subject: [PATCH 4/7] thread_local: reset slot in worker threads first +Subject: [PATCH 4/6] thread_local: reset slot in worker threads first Thread local slots refer to their data via shared pointers. Reset the shared pointer first in the worker threads, and last in the main thread @@ -18,7 +18,7 @@ Signed-off-by: Jarno Rajahalme 4 files changed, 36 insertions(+), 2 deletions(-) diff --git a/envoy/thread_local/thread_local.h b/envoy/thread_local/thread_local.h -index 13ff7496..da982cce 100644 +index 13ff7496ff..da982ccea5 100644 --- a/envoy/thread_local/thread_local.h +++ b/envoy/thread_local/thread_local.h @@ -248,6 +248,13 @@ public: @@ -36,7 +36,7 @@ index 13ff7496..da982cce 100644 } // namespace ThreadLocal diff --git a/source/common/thread_local/thread_local_impl.cc b/source/common/thread_local/thread_local_impl.cc -index 2a49789a..e57b2fd7 100644 +index 1231c044f7..f0c3696bd1 100644 --- a/source/common/thread_local/thread_local_impl.cc +++ b/source/common/thread_local/thread_local_impl.cc @@ -165,7 +165,8 @@ void InstanceImpl::removeSlot(uint32_t slot) { @@ -87,7 +87,7 @@ index 2a49789a..e57b2fd7 100644 if (thread_local_data_.data_.size() <= index) { thread_local_data_.data_.resize(index + 1); diff --git a/source/common/thread_local/thread_local_impl.h b/source/common/thread_local/thread_local_impl.h -index 71941899..685457af 100644 +index a2aa9351f0..01af0cc83a 100644 --- a/source/common/thread_local/thread_local_impl.h +++ b/source/common/thread_local/thread_local_impl.h @@ -29,6 +29,7 @@ public: @@ -99,7 +99,7 @@ index 71941899..685457af 100644 private: // On destruction returns the slot index to the deferred delete queue (detaches it). This allows diff --git a/test/mocks/thread_local/mocks.h b/test/mocks/thread_local/mocks.h -index 09dff237..88d7cea1 100644 +index 7ca4fa8147..171bc4a1bb 100644 --- a/test/mocks/thread_local/mocks.h +++ b/test/mocks/thread_local/mocks.h @@ -27,6 +27,10 @@ public: @@ -114,5 +114,5 @@ index 09dff237..88d7cea1 100644 SlotPtr allocateSlotMock() { return SlotPtr{new SlotImpl(*this, current_slot_++)}; } void runOnAllThreads1(std::function cb) { cb(); } -- -2.54.0 +2.55.0 diff --git a/patches/0005-http-header-expose-attribute.patch b/patches/0005-http-header-expose-attribute.patch index 0e316db87..2b156eaff 100644 --- a/patches/0005-http-header-expose-attribute.patch +++ b/patches/0005-http-header-expose-attribute.patch @@ -1,7 +1,7 @@ -From 3266c2e33a7f93ad72570c65ad592dee2e8470b0 Mon Sep 17 00:00:00 2001 +From c14ff1ecf9ca8108848e4b9e663dbac7e6537963 Mon Sep 17 00:00:00 2001 From: Tam Mach Date: Wed, 19 Mar 2025 21:07:05 +1100 -Subject: [PATCH 5/7] Expose HTTP Header matcher attribute +Subject: [PATCH 5/6] Expose HTTP Header matcher attribute Signed-off-by: Tam Mach --- @@ -9,7 +9,7 @@ Signed-off-by: Tam Mach 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/source/common/http/header_utility.h b/source/common/http/header_utility.h -index 095cb4a1..0577910a 100644 +index 31d7cfb356..7a114bfac1 100644 --- a/source/common/http/header_utility.h +++ b/source/common/http/header_utility.h @@ -96,7 +96,6 @@ public: @@ -140,5 +140,5 @@ index 095cb4a1..0577910a 100644 using HeaderDataPtr = std::unique_ptr; -- -2.54.0 +2.55.0 diff --git a/patches/0007-config-add-grpc-mux-stream-event-callback.patch b/patches/0006-config-add-grpc-mux-stream-event-callback.patch similarity index 94% rename from patches/0007-config-add-grpc-mux-stream-event-callback.patch rename to patches/0006-config-add-grpc-mux-stream-event-callback.patch index da422f16a..e8ca40d18 100644 --- a/patches/0007-config-add-grpc-mux-stream-event-callback.patch +++ b/patches/0006-config-add-grpc-mux-stream-event-callback.patch @@ -1,7 +1,7 @@ -From 2d2b52988e04e060ef04f8b3127f992c1c6fc100 Mon Sep 17 00:00:00 2001 -From: Kateryna Nezdolii -Date: Mon, 17 Aug 2026 12:38:28 +0000 -Subject: [PATCH] config: add gRPC mux stream event callback +From 9d7d0dbed5d3219608e06fb2e05556dfd3acc2bb Mon Sep 17 00:00:00 2001 +From: Jarno Rajahalme +Date: Mon, 17 Aug 2026 18:52:54 +1000 +Subject: [PATCH 6/6] config: add grpc mux stream event callback --- envoy/config/BUILD | 1 + @@ -23,10 +23,10 @@ Subject: [PATCH] config: add gRPC mux stream event callback create mode 100644 source/common/config/grpc_mux_stream_event_tracker.h diff --git a/envoy/config/BUILD b/envoy/config/BUILD -index 35eacb93dd..c4c412fbd8 100644 +index ad9f959847..616c47916c 100644 --- a/envoy/config/BUILD +++ b/envoy/config/BUILD -@@ -66,6 +66,7 @@ envoy_cc_library( +@@ -65,6 +65,7 @@ envoy_cc_library( name = "grpc_mux_interface", hdrs = ["grpc_mux.h"], deps = [ @@ -35,7 +35,7 @@ index 35eacb93dd..c4c412fbd8 100644 ":subscription_interface", "//envoy/stats:stats_macros", diff --git a/envoy/config/grpc_mux.h b/envoy/config/grpc_mux.h -index c97d734be5..24fc287ddf 100644 +index f8aa1b3e4b..ca8c6f1c62 100644 --- a/envoy/config/grpc_mux.h +++ b/envoy/config/grpc_mux.h @@ -1,8 +1,10 @@ @@ -81,7 +81,7 @@ index c97d734be5..24fc287ddf 100644 * Pause discovery requests for a given API type. This is useful when we're processing an update * for LDS or CDS and don't want a flood of updates for RDS or EDS respectively. Discovery diff --git a/source/common/config/BUILD b/source/common/config/BUILD -index 96ac327983..21e12f6ab4 100644 +index ba2ee65f1f..d772e8a95e 100644 --- a/source/common/config/BUILD +++ b/source/common/config/BUILD @@ -87,11 +87,20 @@ envoy_cc_library( @@ -148,7 +148,7 @@ index 0000000000..686348502d +} // namespace Config +} // namespace Envoy diff --git a/source/common/config/null_grpc_mux_impl.h b/source/common/config/null_grpc_mux_impl.h -index 9797edb644..a5a3bdd09b 100644 +index 590d32f30b..34ebb0e6da 100644 --- a/source/common/config/null_grpc_mux_impl.h +++ b/source/common/config/null_grpc_mux_impl.h @@ -1,6 +1,7 @@ @@ -172,7 +172,7 @@ index 9797edb644..a5a3bdd09b 100644 ScopedResume pause(const std::string&) override { return std::make_unique([] {}); } -@@ -43,6 +50,9 @@ public: +@@ -44,6 +51,9 @@ public: void onEstablishmentFailure(bool) override {} void onDiscoveryResponse(std::unique_ptr&&, ControlPlaneStats&) override {} @@ -183,7 +183,7 @@ index 9797edb644..a5a3bdd09b 100644 } // namespace Config diff --git a/source/extensions/config_subscription/grpc/BUILD b/source/extensions/config_subscription/grpc/BUILD -index e3ba49ab04..5465bc31c4 100644 +index 3d5ef9dee2..7bc6f98a62 100644 --- a/source/extensions/config_subscription/grpc/BUILD +++ b/source/extensions/config_subscription/grpc/BUILD @@ -30,6 +30,7 @@ envoy_cc_extension( @@ -203,10 +203,10 @@ index e3ba49ab04..5465bc31c4 100644 ":watch_map_lib", "//envoy/config:custom_config_validators_interface", diff --git a/source/extensions/config_subscription/grpc/grpc_mux_impl.cc b/source/extensions/config_subscription/grpc/grpc_mux_impl.cc -index ccc8200b41..dd1fd0c360 100644 +index e8be9a58ce..29b04801c7 100644 --- a/source/extensions/config_subscription/grpc/grpc_mux_impl.cc +++ b/source/extensions/config_subscription/grpc/grpc_mux_impl.cc -@@ -571,6 +571,7 @@ void GrpcMuxImpl::onStreamEstablished() { +@@ -575,6 +575,7 @@ void GrpcMuxImpl::onStreamEstablished() { for (const auto& type_url : subscriptions_) { queueDiscoveryRequest(type_url); } @@ -214,7 +214,7 @@ index ccc8200b41..dd1fd0c360 100644 } void GrpcMuxImpl::onEstablishmentFailure(bool) { -@@ -590,6 +591,7 @@ void GrpcMuxImpl::onEstablishmentFailure(bool) { +@@ -594,6 +595,7 @@ void GrpcMuxImpl::onEstablishmentFailure(bool) { api_state.second->previously_fetched_data_ = true; } } @@ -223,7 +223,7 @@ index ccc8200b41..dd1fd0c360 100644 void GrpcMuxImpl::queueDiscoveryRequest(absl::string_view queue_item) { diff --git a/source/extensions/config_subscription/grpc/grpc_mux_impl.h b/source/extensions/config_subscription/grpc/grpc_mux_impl.h -index 604a91b787..4dbf8d0f48 100644 +index eb47c049ff..f02dfc2a01 100644 --- a/source/extensions/config_subscription/grpc/grpc_mux_impl.h +++ b/source/extensions/config_subscription/grpc/grpc_mux_impl.h @@ -3,6 +3,7 @@ @@ -256,7 +256,7 @@ index 604a91b787..4dbf8d0f48 100644 // GrpcMux ScopedResume pause(const std::string& type_url) override; ScopedResume pause(const std::vector type_urls) override; -@@ -294,6 +303,7 @@ private: +@@ -327,6 +336,7 @@ private: const bool skip_subsequent_node_; CustomConfigValidatorsPtr config_validators_; XdsConfigTrackerOptRef xds_config_tracker_; @@ -265,7 +265,7 @@ index 604a91b787..4dbf8d0f48 100644 EdsResourcesCachePtr eds_resources_cache_; const std::string target_xds_authority_; diff --git a/source/extensions/config_subscription/grpc/new_grpc_mux_impl.cc b/source/extensions/config_subscription/grpc/new_grpc_mux_impl.cc -index d3ca46be52..55fdb395c1 100644 +index 0a59a4abcf..46659f5d43 100644 --- a/source/extensions/config_subscription/grpc/new_grpc_mux_impl.cc +++ b/source/extensions/config_subscription/grpc/new_grpc_mux_impl.cc @@ -198,6 +198,7 @@ void NewGrpcMuxImpl::onStreamEstablished() { @@ -285,7 +285,7 @@ index d3ca46be52..55fdb395c1 100644 void NewGrpcMuxImpl::onWriteable() { trySendDiscoveryRequests(); } diff --git a/source/extensions/config_subscription/grpc/new_grpc_mux_impl.h b/source/extensions/config_subscription/grpc/new_grpc_mux_impl.h -index e94d7ef317..fab4639dc6 100644 +index 45b8a3fc59..1320833b90 100644 --- a/source/extensions/config_subscription/grpc/new_grpc_mux_impl.h +++ b/source/extensions/config_subscription/grpc/new_grpc_mux_impl.h @@ -1,6 +1,7 @@ @@ -318,7 +318,7 @@ index e94d7ef317..fab4639dc6 100644 GrpcMuxWatchPtr addWatch(const std::string& type_url, const absl::flat_hash_set& resources, -@@ -220,6 +229,7 @@ private: +@@ -221,6 +230,7 @@ private: const LocalInfo::LocalInfo& local_info_; CustomConfigValidatorsPtr config_validators_; @@ -339,10 +339,10 @@ index a0fa33be75..912f49b39c 100644 "//source/extensions/config_subscription/grpc:pausable_ack_queue_lib", "//source/extensions/config_subscription/grpc:watch_map_lib", diff --git a/source/extensions/config_subscription/grpc/xds_mux/grpc_mux_impl.cc b/source/extensions/config_subscription/grpc/xds_mux/grpc_mux_impl.cc -index cd35df6869..67253b9e5c 100644 +index cb221ac889..010d6bc2bc 100644 --- a/source/extensions/config_subscription/grpc/xds_mux/grpc_mux_impl.cc +++ b/source/extensions/config_subscription/grpc/xds_mux/grpc_mux_impl.cc -@@ -323,6 +323,7 @@ void GrpcMuxImpl::handleEstablishedStream() { +@@ -339,6 +339,7 @@ void GrpcMuxImpl::handleEstablishedStream() { maybeUpdateQueueSizeStat(0); pausable_ack_queue_.clear(); trySendDiscoveryRequests(); @@ -350,7 +350,7 @@ index cd35df6869..67253b9e5c 100644 } template -@@ -347,6 +348,7 @@ void GrpcMuxImpl::handleStreamEstablishmentFailure( +@@ -363,6 +364,7 @@ void GrpcMuxImpl::handleStreamEstablishmentFailure( } } while (all_subscribed.size() != subscriptions_.size()); should_send_initial_resource_versions_ = next_attempt_may_send_initial_resource_version; @@ -359,7 +359,7 @@ index cd35df6869..67253b9e5c 100644 template diff --git a/source/extensions/config_subscription/grpc/xds_mux/grpc_mux_impl.h b/source/extensions/config_subscription/grpc/xds_mux/grpc_mux_impl.h -index d391f3ff18..2959aa738b 100644 +index a268014701..3cea9bc79f 100644 --- a/source/extensions/config_subscription/grpc/xds_mux/grpc_mux_impl.h +++ b/source/extensions/config_subscription/grpc/xds_mux/grpc_mux_impl.h @@ -3,6 +3,7 @@ @@ -392,7 +392,7 @@ index d391f3ff18..2959aa738b 100644 void updateWatch(const std::string& type_url, Watch* watch, const absl::flat_hash_set& resources, const SubscriptionOptions& options); -@@ -241,6 +250,7 @@ private: +@@ -242,6 +251,7 @@ private: // this one is up to GrpcMux. const LocalInfo::LocalInfo& local_info_; Common::CallbackHandlePtr dynamic_update_callback_handle_; @@ -400,7 +400,7 @@ index d391f3ff18..2959aa738b 100644 CustomConfigValidatorsPtr config_validators_; XdsConfigTrackerOptRef xds_config_tracker_; XdsResourcesDelegateOptRef xds_resources_delegate_; -@@ -293,6 +303,13 @@ private: +@@ -294,6 +304,13 @@ private: class NullGrpcMuxImpl : public GrpcMux { public: void start() override {} @@ -414,7 +414,7 @@ index d391f3ff18..2959aa738b 100644 ScopedResume pause(const std::string&) override { return std::make_unique([]() {}); -@@ -319,6 +336,9 @@ public: +@@ -321,6 +338,9 @@ public: Upstream::LoadStatsReporter* loadStatsReporter() const override { return nullptr; } Upstream::LoadStatsReporter* maybeCreateLoadStatsReporter() override { return nullptr; } @@ -472,10 +472,10 @@ index e70750ed4f..b0f84d66f1 100644 // ignore later ones. This allows the nonce to be used. TEST_P(GrpcSubscriptionImplTest, RepeatedNonce) { diff --git a/test/mocks/config/mocks.h b/test/mocks/config/mocks.h -index cb09a4caa5..9c4f8d939a 100644 +index 72b2fd2133..787e52c321 100644 --- a/test/mocks/config/mocks.h +++ b/test/mocks/config/mocks.h -@@ -116,6 +116,10 @@ public: +@@ -161,6 +161,10 @@ public: MOCK_METHOD(ScopedResume, pause, (const std::string& type_url), (override)); MOCK_METHOD(ScopedResume, pause, (const std::vector type_urls), (override)); @@ -487,5 +487,5 @@ index cb09a4caa5..9c4f8d939a 100644 (const absl::flat_hash_set& resources, const std::string& type_url, SubscriptionCallbacks& callbacks, SubscriptionStats& stats, -- -2.51.0 +2.55.0 diff --git a/patches/0006-test-integration-Defer-fake-upstream-read-enable-un.patch b/patches/0006-test-integration-Defer-fake-upstream-read-enable-un.patch deleted file mode 100644 index 4a771123f..000000000 --- a/patches/0006-test-integration-Defer-fake-upstream-read-enable-un.patch +++ /dev/null @@ -1,133 +0,0 @@ -From 50f4d0e3a617d9a3db477c3532da7f5a36c5862b Mon Sep 17 00:00:00 2001 -From: Kateryna Nezdolii -Date: Wed, 20 May 2026 18:23:27 +0000 -Subject: [PATCH 6/7] [test/integration] Defer fake upstream read enable until - initialize (#45029) - ---- - test/integration/fake_upstream.cc | 36 +++++++++++++++++++++++-------- - test/integration/fake_upstream.h | 9 ++++++-- - 2 files changed, 34 insertions(+), 11 deletions(-) - -diff --git a/test/integration/fake_upstream.cc b/test/integration/fake_upstream.cc -index 72ec3fec..f6ea46d6 100644 ---- a/test/integration/fake_upstream.cc -+++ b/test/integration/fake_upstream.cc -@@ -401,8 +401,10 @@ FakeHttpConnection::FakeHttpConnection( - Event::TestTimeSystem& time_system, uint32_t max_request_headers_kb, - uint32_t max_request_headers_count, - envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction -- headers_with_underscores_action) -+ headers_with_underscores_action, -+ bool deferred_read_enable) - : FakeConnectionBase(shared_connection, time_system), type_(type), -+ deferred_read_enable_(deferred_read_enable), - header_validator_factory_( - IntegrationUtil::makeHeaderValidationFactory(fakeUpstreamHeaderValidatorConfig())) { - ASSERT(max_request_headers_count != 0); -@@ -437,6 +439,17 @@ FakeHttpConnection::FakeHttpConnection( - Network::ReadFilterSharedPtr{new ReadFilter(*this)}); - } - -+void FakeHttpConnection::initialize() { -+ FakeConnectionBase::initialize(); -+ if (deferred_read_enable_ && shared_connection_.connected() && -+ !shared_connection_.connection().readEnabled()) { -+ // Re-enable reads that were explicitly deferred by consumeConnection(defer_read_enable=true) -+ // to ensure the HTTP codec and read filter are fully initialized before processing request -+ // bytes. This must not re-enable reads when disable_and_do_not_enable_ is active. -+ shared_connection_.connection().readDisable(false); -+ } -+} -+ - AssertionResult FakeConnectionBase::close(std::chrono::milliseconds timeout) { - ENVOY_LOG(trace, "FakeConnectionBase close"); - if (!shared_connection_.connected()) { -@@ -821,8 +834,10 @@ AssertionResult FakeUpstream::waitForHttpConnection(Event::Dispatcher& client_di - return runOnDispatcherThreadAndWait([&]() { - absl::MutexLock lock(lock_); - connection = std::make_unique( -- *this, consumeConnection(), http_type_, time_system_, config_.max_request_headers_kb_, -- config_.max_request_headers_count_, config_.headers_with_underscores_action_); -+ *this, consumeConnection(/*defer_read_enable=*/true), http_type_, time_system_, -+ config_.max_request_headers_kb_, config_.max_request_headers_count_, -+ config_.headers_with_underscores_action_, -+ /*deferred_read_enable=*/read_disable_on_new_connection_ && !disable_and_do_not_enable_); - connection->initialize(); - return AssertionSuccess(); - }); -@@ -858,9 +873,11 @@ FakeUpstream::waitForHttpConnection(Event::Dispatcher& client_dispatcher, - EXPECT_TRUE(upstream.runOnDispatcherThreadAndWait([&]() { - absl::MutexLock lock(upstream.lock_); - connection = std::make_unique( -- upstream, upstream.consumeConnection(), upstream.http_type_, upstream.timeSystem(), -- Http::DEFAULT_MAX_REQUEST_HEADERS_KB, Http::DEFAULT_MAX_HEADERS_COUNT, -- envoy::config::core::v3::HttpProtocolOptions::ALLOW); -+ upstream, upstream.consumeConnection(/*defer_read_enable=*/true), upstream.http_type_, -+ upstream.timeSystem(), Http::DEFAULT_MAX_REQUEST_HEADERS_KB, -+ Http::DEFAULT_MAX_HEADERS_COUNT, envoy::config::core::v3::HttpProtocolOptions::ALLOW, -+ /*deferred_read_enable=*/upstream.read_disable_on_new_connection_ && -+ !upstream.disable_and_do_not_enable_); - connection->initialize(); - return AssertionSuccess(); - })); -@@ -929,7 +946,7 @@ void FakeUpstream::convertFromRawToHttp(FakeRawConnectionPtr& raw_connection, - raw_connection.release(); - } - --SharedConnectionWrapper& FakeUpstream::consumeConnection() { -+SharedConnectionWrapper& FakeUpstream::consumeConnection(bool defer_read_enable) { - ASSERT(!new_connections_.empty()); - auto* const connection_wrapper = new_connections_.front().get(); - // Skip the thread safety check if the network connection has already been freed since there's no -@@ -939,10 +956,11 @@ SharedConnectionWrapper& FakeUpstream::consumeConnection() { - connection_wrapper->moveBetweenLists(new_connections_, consumed_connections_); - if (read_disable_on_new_connection_ && connection_wrapper->connected() && - http_type_ != Http::CodecType::HTTP3 && !disable_and_do_not_enable_) { -- // Re-enable read and early close detection. - auto& connection = connection_wrapper->connection(); - connection.detectEarlyCloseWhenReadDisabled(true); -- connection.readDisable(false); -+ if (!defer_read_enable) { -+ connection.readDisable(false); -+ } - } - return *connection_wrapper; - } -diff --git a/test/integration/fake_upstream.h b/test/integration/fake_upstream.h -index 277189bd..43cd5f05 100644 ---- a/test/integration/fake_upstream.h -+++ b/test/integration/fake_upstream.h -@@ -545,7 +545,10 @@ public: - Http::CodecType type, Event::TestTimeSystem& time_system, - uint32_t max_request_headers_kb, uint32_t max_request_headers_count, - envoy::config::core::v3::HttpProtocolOptions::HeadersWithUnderscoresAction -- headers_with_underscores_action); -+ headers_with_underscores_action, -+ bool deferred_read_enable = false); -+ -+ void initialize() override; - - ABSL_MUST_USE_RESULT - testing::AssertionResult -@@ -607,6 +610,7 @@ private: - }; - - const Http::CodecType type_; -+ bool deferred_read_enable_; - Http::ServerConnectionPtr codec_; - std::list new_streams_ ABSL_GUARDED_BY(lock_); - testing::NiceMock overload_manager_; -@@ -1002,7 +1006,8 @@ private: - }; - - void threadRoutine(); -- SharedConnectionWrapper& consumeConnection() ABSL_EXCLUSIVE_LOCKS_REQUIRED(lock_); -+ SharedConnectionWrapper& consumeConnection(bool defer_read_enable = false) -+ ABSL_EXCLUSIVE_LOCKS_REQUIRED(lock_); - Network::FilterStatus onRecvDatagram(Network::UdpRecvData& data); - AssertionResult - runOnDispatcherThreadAndWait(std::function cb, --- -2.55.0 - diff --git a/patches/0008-repo-Make-yq-dependency-optional-for-CI-config-parsi.patch b/patches/0008-repo-Make-yq-dependency-optional-for-CI-config-parsi.patch deleted file mode 100644 index de006961d..000000000 --- a/patches/0008-repo-Make-yq-dependency-optional-for-CI-config-parsi.patch +++ /dev/null @@ -1,72 +0,0 @@ -From af2053dc1e3892a9f28d6eebf7f907c3d83ce536 Mon Sep 17 00:00:00 2001 -From: Tam Mach -Date: Sat, 14 Mar 2026 21:00:53 +1100 -Subject: [PATCH] repo: Make yq dependency optional for CI config parsing - -When yq is unavailable (e.g. in WORKSPACE mode due to aspect_bazel_lib -hub repo symlink issues), fall back to placeholder container image -values. This only affects RBE container references which are not needed -for local or Docker-based builds. - -Signed-off-by: Tam Mach ---- - bazel/repo.bzl | 38 +++++++++++++++++++++++++------------- - 1 file changed, 25 insertions(+), 13 deletions(-) - -diff --git a/bazel/repo.bzl b/bazel/repo.bzl -index 561c99b71c..a96fcd3757 100644 ---- a/bazel/repo.bzl -+++ b/bazel/repo.bzl -@@ -66,24 +66,36 @@ def _envoy_repo_impl(repository_ctx): - """ - - # parse container information for use in RBE -+ # Try to use yq, fall back to placeholder values if unavailable -+ # (yq may not resolve in WORKSPACE mode due to aspect_bazel_lib hub repo symlink issues) - json_result = repository_ctx.execute([ - repository_ctx.path(repository_ctx.attr.yq), - repository_ctx.path(repository_ctx.attr.envoy_ci_config), - "-ojson", - ]) -- if json_result.return_code != 0: -- fail("yq failed: {}".format(json_result.stderr)) -- repository_ctx.file("ci-config.json", json_result.stdout) -- config_data = json.decode(repository_ctx.read("ci-config.json")) -- repository_ctx.file("containers.bzl", CONTAINERS.format( -- repo = config_data["build-image"]["repo"], -- repo_gcr = config_data["build-image"]["repo-gcr"], -- sha = config_data["build-image"]["sha"], -- sha_gcc = config_data["build-image"]["sha-gcc"], -- sha_mobile = config_data["build-image"]["sha-mobile"], -- sha_worker = config_data["build-image"]["sha-worker"], -- tag = config_data["build-image"]["tag"], -- )) -+ if json_result.return_code == 0: -+ repository_ctx.file("ci-config.json", json_result.stdout) -+ config_data = json.decode(repository_ctx.read("ci-config.json")) -+ repository_ctx.file("containers.bzl", CONTAINERS.format( -+ repo = config_data["build-image"]["repo"], -+ repo_gcr = config_data["build-image"]["repo-gcr"], -+ sha = config_data["build-image"]["sha"], -+ sha_gcc = config_data["build-image"]["sha-gcc"], -+ sha_mobile = config_data["build-image"]["sha-mobile"], -+ sha_worker = config_data["build-image"]["sha-worker"], -+ tag = config_data["build-image"]["tag"], -+ )) -+ else: -+ # yq unavailable - use placeholder values (RBE container refs won't work) -+ repository_ctx.file("containers.bzl", CONTAINERS.format( -+ repo = "envoyproxy/envoy-build-ubuntu", -+ repo_gcr = "envoyproxy/envoy-build-ubuntu", -+ sha = "0" * 64, -+ sha_gcc = "0" * 64, -+ sha_mobile = "0" * 64, -+ sha_worker = "0" * 64, -+ tag = "unknown", -+ )) - repo_version_path = repository_ctx.path(repository_ctx.attr.envoy_version) - api_version_path = repository_ctx.path(repository_ctx.attr.envoy_api_version) - version = repository_ctx.read(repo_version_path).strip() --- -2.43.0 - diff --git a/proxylib/BUILD b/proxylib/BUILD deleted file mode 100644 index 81bcddcf5..000000000 --- a/proxylib/BUILD +++ /dev/null @@ -1,7 +0,0 @@ -licenses(["notice"]) # Apache 2 - -exports_files([ - "libcilium.h", - "types.h", - "libcilium.so", -]) diff --git a/proxylib/Makefile b/proxylib/Makefile deleted file mode 100644 index 977e25740..000000000 --- a/proxylib/Makefile +++ /dev/null @@ -1,41 +0,0 @@ -# Copyright Authors of Cilium -# SPDX-License-Identifier: Apache-2.0 - -include ../Makefile.defs - -# Support CGO cross-compiling for amd64 and arm64 targets -NATIVE_ARCH = $(shell GOARCH= $(GO) env GOARCH) -CGO_CC = -CROSS_ARCH = -ifneq ($(GOARCH),$(NATIVE_ARCH)) - CROSS_ARCH = $(GOARCH) -endif -ifeq ($(CROSS_ARCH),arm64) - CGO_CC = CC=aarch64-linux-gnu-gcc -else ifeq ($(CROSS_ARCH),amd64) - CGO_CC = CC=x86_64-linux-gnu-gcc -endif -GO_BUILD_FLAGS ?= -GO_BUILD_WITH_CGO = CGO_ENABLED=1 $(CGO_CC) $(GO) build $(GO_BUILD_FLAGS) - -EXTRA_GO_BUILD_LDFLAGS = -extldflags -Wl,-soname,libcilium.so - -TARGET := libcilium.so - -.PHONY: all $(TARGET) clean header libcilium.h test - -all: $(TARGET) - -$(TARGET): - $(QUIET)$(GO_BUILD_WITH_CGO) -ldflags '$(EXTRA_GO_BUILD_LDFLAGS)' -o $@ -buildmode=c-shared - -clean: - -$(QUIET)rm -f $(TARGET) - $(QUIET)$(GO_CLEAN) - -header: libcilium.h -libcilium.h: proxylib.go - $(GO) tool cgo -exportheader libcilium.h proxylib.go - -test: - $(GO) test -mod=vendor -cover ./... diff --git a/proxylib/accesslog/client.go b/proxylib/accesslog/client.go deleted file mode 100644 index 17ad438b4..000000000 --- a/proxylib/accesslog/client.go +++ /dev/null @@ -1,100 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package accesslog - -import ( - "net" - "sync" - "sync/atomic" - - "google.golang.org/protobuf/proto" - "github.com/sirupsen/logrus" - - cilium "github.com/cilium/proxy/go/cilium/api" - "github.com/cilium/proxy/proxylib/proxylib" -) - -type Client struct { - connected uint32 // Accessed atomically without locking - path string - mutex sync.Mutex // Used to protect opening the connection - conn atomic.Pointer[net.UnixConn] // Read atomically without locking -} - -func (cl *Client) connect() *net.UnixConn { - if cl.path == "" { - return nil - } - - if atomic.LoadUint32(&cl.connected) > 0 { - // Guaranteed to be non-nil - return cl.conn.Load() - } - - cl.mutex.Lock() - defer cl.mutex.Unlock() - - conn := cl.conn.Load() - - // Did someone else connect while we were contending on the lock? - // cl.connected may be written to by others concurrently - if atomic.LoadUint32(&cl.connected) > 0 { - return conn - } - - if conn != nil { - conn.Close() // not setting conn to nil! - } - logrus.Debugf("Accesslog: Connecting to Cilium access log socket: %s", cl.path) - conn, err := net.DialUnix("unixpacket", nil, &net.UnixAddr{Name: cl.path, Net: "unixpacket"}) - if err != nil { - logrus.WithError(err).Error("Accesslog: DialUnix() failed") - return nil - } - - cl.conn.Store(conn) - - // Always have a non-nil 'cl.conn' after 'cl.connected' is set for the first time! - atomic.StoreUint32(&cl.connected, 1) - return conn -} - -func (cl *Client) Log(pblog *cilium.LogEntry) { - if conn := cl.connect(); conn != nil { - // Encode - logmsg, err := proto.Marshal(pblog) - if err != nil { - logrus.WithError(err).Error("Accesslog: Protobuf marshaling error") - return - } - - // Write - _, err = conn.Write(logmsg) - if err != nil { - logrus.WithError(err).Error("Accesslog: Write() failed") - atomic.StoreUint32(&cl.connected, 0) // Mark connection as broken - } - } else { - logrus.Debugf("Accesslog: No connection, cannot send: %s", pblog.String()) - } -} - -func (c *Client) Path() string { - return c.path -} - -func NewClient(accessLogPath string) proxylib.AccessLogger { - client := &Client{ - path: accessLogPath, - } - client.connect() - return client -} - -func (cl *Client) Close() { - conn := cl.conn.Load() - if conn != nil { - conn.Close() - } -} diff --git a/proxylib/cassandra/cassandraparser.go b/proxylib/cassandra/cassandraparser.go deleted file mode 100644 index 11653b57e..000000000 --- a/proxylib/cassandra/cassandraparser.go +++ /dev/null @@ -1,703 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package cassandra - -import ( - "bytes" - "encoding/binary" - "fmt" - "regexp" - "strings" - - "github.com/sirupsen/logrus" - - cilium "github.com/cilium/proxy/go/cilium/api" - . "github.com/cilium/proxy/proxylib/proxylib" -) - -// -// Cassandra v3/v4 Parser -// -// Spec: https://github.com/apache/cassandra/blob/trunk/doc/native_protocol_v4.spec -// - -// Current Cassandra parser supports filtering on messages where the opcode is 'query-like' -// (i.e., opcode 'query', 'prepare', 'batch'. In those scenarios, we match on query_action and query_table. -// Examples: -// query_action = 'select', query_table = 'system.*' -// query_action = 'insert', query_table = 'attendance.daily_records' -// query_action = 'select', query_table = 'deathstar.scrum_notes' -// query_action = 'insert', query_table = 'covalent.foo' -// -// Batch requests are logged as invidual queries, but an entire batch request will be allowed -// only if all requests are allowed. - -// Non-query client requests, including 'Options', 'Auth_Response', 'Startup', and 'Register' -// are automatically allowed to simplify the policy language. - -// There are known changes in protocol v2 that are not compatible with this parser, see the -// the "Changes from v2" in https://github.com/apache/cassandra/blob/trunk/doc/native_protocol_v3.spec - -type CassandraRule struct { - queryActionExact string - tableRegexCompiled *regexp.Regexp -} - -const cassHdrLen = 9 -const cassMaxLen = 268435456 // 256 MB, per spec - -const unknownPreparedQueryPath = "/unknown-prepared-query" - -func (rule *CassandraRule) Matches(data interface{}) bool { - // Cast 'data' to the type we give to 'Matches()' - - path, ok := data.(string) - if !ok { - logrus.Warning("Matches() called with type other than string") - return false - } - logrus.Debugf("Policy Match test for '%s'", path) - regexStr := "" - if rule.tableRegexCompiled != nil { - regexStr = rule.tableRegexCompiled.String() - } - - logrus.Debugf("Rule: action '%s', table '%s'", rule.queryActionExact, regexStr) - if path == unknownPreparedQueryPath { - logrus.Warning("Dropping execute for unknown prepared-id") - return false - } - parts := strings.Split(path, "/") - if len(parts) <= 2 { - // this is not a query-like request, just allow - return true - } else if len(parts) < 4 { - // should never happen unless we've messed up internally - // as path is either / or /// - logrus.Errorf("Invalid parsed path: '%s'", path) - return false - } - if rule.queryActionExact != "" && rule.queryActionExact != parts[2] { - logrus.Debugf("CassandraRule: query_action mismatch %v, %s", rule.queryActionExact, parts[1]) - return false - } - if len(parts[3]) > 0 && - rule.tableRegexCompiled != nil && - !rule.tableRegexCompiled.MatchString(parts[3]) { - logrus.Debugf("CassandraRule: table_regex mismatch '%v', '%s'", rule.tableRegexCompiled, parts[3]) - return false - } - - return true -} - -// CassandraRuleParser parses protobuf L7 rules to enforcement objects -// May panic -func CassandraRuleParser(rule *cilium.PortNetworkPolicyRule) []L7NetworkPolicyRule { - l7Rules := rule.GetL7Rules() - if l7Rules == nil { - return nil - } - - allowRules := l7Rules.GetL7AllowRules() - rules := make([]L7NetworkPolicyRule, 0, len(allowRules)) - for _, l7Rule := range allowRules { - var cr CassandraRule - for k, v := range l7Rule.Rule { - switch k { - case "query_action": - cr.queryActionExact = v - case "query_table": - if v != "" { - cr.tableRegexCompiled = regexp.MustCompile(v) - } - default: - ParseError(fmt.Sprintf("Unsupported key: %s", k), rule) - } - } - if len(cr.queryActionExact) > 0 { - // ensure this is a valid query action - res := queryActionMap[cr.queryActionExact] - if res == invalidAction { - ParseError(fmt.Sprintf("Unable to parse L7 cassandra rule with invalid query_action: '%s'", cr.queryActionExact), rule) - } else if res == actionNoTable && cr.tableRegexCompiled != nil { - ParseError(fmt.Sprintf("query_action '%s' is not compatible with a query_table match", cr.queryActionExact), rule) - } - - } - - logrus.Debugf("Parsed CassandraRule pair: %v", cr) - rules = append(rules, &cr) - } - return rules -} - -type CassandraParserFactory struct{} - -var cassandraParserFactory *CassandraParserFactory - -func init() { - logrus.Debug("init(): Registering cassandraParserFactory") - RegisterParserFactory("cassandra", cassandraParserFactory) - RegisterL7RuleParser("cassandra", CassandraRuleParser) -} - -type CassandraParser struct { - connection *Connection - keyspace string // stores current keyspace name from 'use' command - - // stores prepared query string while - // waiting for 'prepared' reply from server - // with a prepared id. - // replies associated via stream-id - preparedQueryPathByStreamID map[uint16]string - - // allowing us to enforce policy on query - // at the time of the execute command. - preparedQueryPathByPreparedID map[string]string // stores query string based on prepared-id, -} - -func (pf *CassandraParserFactory) Create(connection *Connection) interface{} { - logrus.Debugf("CassandraParserFactory: Create: %v", connection) - - p := CassandraParser{connection: connection} - p.preparedQueryPathByStreamID = make(map[uint16]string) - p.preparedQueryPathByPreparedID = make(map[string]string) - return &p -} - -func (p *CassandraParser) OnData(reply, endStream bool, dataArray [][]byte) (OpType, int) { - - // inefficient, but simple for now - data := bytes.Join(dataArray, []byte{}) - - if len(data) < cassHdrLen { - // Partial header received, ask for more - needs := cassHdrLen - len(data) - logrus.Debugf("Did not receive full header, need %d more bytes", needs) - return MORE, needs - } - - // full header available, read full request length - requestLen := binary.BigEndian.Uint32(data[5:9]) - logrus.Debugf("Request length = %d", requestLen) - if requestLen > cassMaxLen { - logrus.Errorf("Request length of %d is greater than 256 MB", requestLen) - return ERROR, int(ERROR_INVALID_FRAME_LENGTH) - } - - dataMissing := (cassHdrLen + int(requestLen)) - len(data) - if dataMissing > 0 { - // full header received, but only partial request - - logrus.Debugf("Hdr received, but need %d more bytes of request", dataMissing) - return MORE, dataMissing - } - - // we parse replies, but only to look for prepared-query-id responses - if reply { - if len(data) == 0 { - logrus.Debugf("ignoring zero length reply call to onData") - return NOP, 0 - - } - cassandraParseReply(p, data[0:(cassHdrLen+requestLen)]) - - logrus.Debugf("reply, passing %d bytes", (cassHdrLen + requestLen)) - return PASS, (cassHdrLen + int(requestLen)) - } - - err, paths := cassandraParseRequest(p, data[0:(cassHdrLen+requestLen)]) - if err != 0 { - logrus.Errorf("Parsing error %d", err) - return ERROR, int(err) - } - - logrus.Debugf("Request paths = %s", paths) - - matches := true - access_log_entry_type := cilium.EntryType_Request - unpreparedQuery := false - - for i := 0; i < len(paths); i++ { - if strings.HasPrefix(paths[i], "/query/use/") || - strings.HasPrefix(paths[i], "/batch/use/") || - strings.HasPrefix(paths[i], "/prepare/use/") { - // do not count a "use" query as a deny - continue - } - - if paths[i] == unknownPreparedQueryPath { - matches = false - unpreparedQuery = true - access_log_entry_type = cilium.EntryType_Denied - break - } - - if !p.connection.Matches(paths[i]) { - matches = false - access_log_entry_type = cilium.EntryType_Denied - break - } - } - - for i := 0; i < len(paths); i++ { - parts := strings.Split(paths[i], "/") - fields := map[string]string{} - - if len(parts) >= 3 && parts[2] == "use" { - // do not log 'use' queries - continue - } else if len(parts) == 4 { - fields["query_action"] = parts[2] - fields["query_table"] = parts[3] - } else if unpreparedQuery { - fields["error"] = "unknown prepared query id" - } else { - // do not log non-query accesses - continue - } - - p.connection.Log(access_log_entry_type, - &cilium.LogEntry_GenericL7{ - GenericL7: &cilium.L7LogEntry{ - Proto: "cassandra", - Fields: fields, - }, - }) - - } - - if !matches { - - // If we have already sent another error to the client, - // do not send unauthorized message - if !unpreparedQuery { - unauthMsg := make([]byte, len(unauthMsgBase)) - copy(unauthMsg, unauthMsgBase) - // We want to use the same protocol and stream ID - // as the incoming request. - // update the protocol to match the request - unauthMsg[0] = 0x80 | (data[0] & 0x07) - // update the stream ID to match the request - unauthMsg[2] = data[2] - unauthMsg[3] = data[3] - p.connection.Inject(true, unauthMsg) - } - return DROP, int(cassHdrLen + requestLen) - } - - return PASS, int(cassHdrLen + requestLen) -} - -// A full response (header + body) to be used as an -// "unauthorized" error to be sent to cassandra client as part of policy -// deny. Array must be updated to ensure that reply has -// protocol version and stream-id that matches the request. - -var unauthMsgBase = []byte{ - 0x0, // version (uint8) - must be set before injection - 0x0, // flags, (uint8) - 0x0, 0x0, // stream-id (uint16) - must be set before injection - 0x0, // opcode error (uint8) - 0x0, 0x0, 0x0, 0x1a, // request length (uint32) - update if text changes - 0x0, 0x0, 0x21, 0x00, // 'unauthorized error code' 0x2100 (uint32) - 0x0, 0x14, // length of error msg (uint16) - update if text changes - 'R', 'e', 'q', 'u', 'e', 's', 't', ' ', 'U', 'n', 'a', 'u', 't', 'h', 'o', 'r', 'i', 'z', 'e', 'd', -} - -// A full response (header + body) to be used as a -// "unprepared" error to be sent to cassandra client if proxy -// does not have the path for this prepare-query-id cached - -var unpreparedMsgBase = []byte{ - 0x0, // version (uint8) - must be set before injection - 0x0, // flags, (uint8) - 0x0, 0x0, // stream-id (uint16) - must be set before injection - 0x0, // opcode error (uint8) - 0x0, 0x0, 0x0, 0x0, // request length (uint32) - must be set based on - // of length of prepared query id - 0x0, 0x0, 0x25, 0x00, // 'unprepared error code' 0x2500 (uint32) - // must append [short bytes] array of prepared query id. -} - -// create reply byte buffer with error code 'unprepared' with code 0x2500 -// followed by a [short bytes] indicating the unknown ID -// must set stream-id of the response to match the request -func createUnpreparedMsg(version byte, streamID []byte, preparedID string) []byte { - - unpreparedMsg := make([]byte, len(unpreparedMsgBase)) - copy(unpreparedMsg, unpreparedMsgBase) - unpreparedMsg[0] = 0x80 | version - unpreparedMsg[2] = streamID[0] - unpreparedMsg[3] = streamID[1] - - idLen := len(preparedID) - idLenBytes := make([]byte, 2) - binary.BigEndian.PutUint16(idLenBytes, uint16(idLen)) - - reqLen := 4 + 2 + idLen - reqLenBytes := make([]byte, 4) - binary.BigEndian.PutUint32(reqLenBytes, uint32(reqLen)) - unpreparedMsg[5] = reqLenBytes[0] - unpreparedMsg[6] = reqLenBytes[1] - unpreparedMsg[7] = reqLenBytes[2] - unpreparedMsg[8] = reqLenBytes[3] - - res := append(unpreparedMsg, idLenBytes...) - return append(res, []byte(preparedID)...) -} - -var opcodeMap = map[byte]string{ - 0x00: "error", - 0x01: "startup", - 0x02: "ready", - 0x03: "authenticate", - 0x05: "options", - 0x06: "supported", - 0x07: "query", - 0x08: "result", - 0x09: "prepare", - 0x0A: "execute", - 0x0B: "register", - 0x0C: "event", - 0x0D: "batch", - 0x0E: "auth_challenge", - 0x0F: "auth_response", - 0x10: "auth_success", -} - -// map to test whether a 'query_action' is valid or not - -const invalidAction = 0 -const actionWithTable = 1 -const actionNoTable = 2 - -var queryActionMap = map[string]int{ - "select": actionWithTable, - "delete": actionWithTable, - "insert": actionWithTable, - "update": actionWithTable, - "create-table": actionWithTable, - "drop-table": actionWithTable, - "alter-table": actionWithTable, - "truncate-table": actionWithTable, - - // these queries take a keyspace - // and match against query_table - "use": actionWithTable, - "create-keyspace": actionWithTable, - "alter-keyspace": actionWithTable, - "drop-keyspace": actionWithTable, - - "drop-index": actionNoTable, - "create-index": actionNoTable, // TODO: we could tie this to table if we want - "create-materialized-view": actionNoTable, - "drop-materialized-view": actionNoTable, - - // TODO: these admin ops could be bundled into meta roles - // (e.g., role-mgmt, permission-mgmt) - "create-role": actionNoTable, - "alter-role": actionNoTable, - "drop-role": actionNoTable, - "grant-role": actionNoTable, - "revoke-role": actionNoTable, - "list-roles": actionNoTable, - "grant-permission": actionNoTable, - "revoke-permission": actionNoTable, - "list-permissions": actionNoTable, - "create-user": actionNoTable, - "alter-user": actionNoTable, - "drop-user": actionNoTable, - "list-users": actionNoTable, - - "create-function": actionNoTable, - "drop-function": actionNoTable, - "create-aggregate": actionNoTable, - "drop-aggregate": actionNoTable, - "create-type": actionNoTable, - "alter-type": actionNoTable, - "drop-type": actionNoTable, - "create-trigger": actionNoTable, - "drop-trigger": actionNoTable, -} - -func parseQuery(p *CassandraParser, query string) (string, string) { - var action string - var table string - - query = strings.TrimRight(query, ";") // remove potential trailing ; - fields := strings.Fields(strings.ToLower(query)) // handles all whitespace - - // we currently do not strip comments. It seems like cqlsh does - // strip comments, but its not clear if that can be assumed of all clients - // It should not be possible to "spoof" the 'action' as this is assumed to be - // the first token (leaving no room for a comment to start), but it could potentially - // trick this parser into thinking we're accessing table X, when in fact the - // query accesses table Y, which would obviously be a security vulnerability - // As a result, we look at each token here, and if any of them match the comment - // characters for cassandra, we fail parsing. - for i := 0; i < len(fields); i++ { - if len(fields[i]) >= 2 && - (fields[i][:2] == "--" || - fields[i][:2] == "/*" || - fields[i][:2] == "//") { - - logrus.Warnf("Unable to safely parse query with comments '%s'", query) - return "", "" - } - } - if len(fields) < 2 { - goto invalidQuery - } - - action = fields[0] - switch action { - case "select", "delete": - for i := 1; i < len(fields); i++ { - if fields[i] == "from" { - table = strings.ToLower(fields[i+1]) - } - } - if len(table) == 0 { - logrus.Warnf("Unable to parse table name from query '%s'", query) - return "", "" - } - case "insert": - // INSERT into - if len(fields) < 3 { - goto invalidQuery - } - table = strings.ToLower(fields[2]) - case "update": - // UPDATE - table = strings.ToLower(fields[1]) - case "use": - p.keyspace = strings.Trim(fields[1], "\"\\'") - logrus.Debugf("Saving keyspace '%s'", p.keyspace) - table = p.keyspace - case "alter", "create", "drop", "truncate", "list": - - action = strings.Join([]string{action, fields[1]}, "-") - if fields[1] == "table" || fields[1] == "keyspace" { - - if len(fields) < 3 { - goto invalidQuery - } - table = fields[2] - if table == "if" { - if action == "create-table" { - if len(fields) < 6 { - goto invalidQuery - } - // handle optional "IF NOT EXISTS" - table = fields[5] - } else if action == "drop-table" || action == "drop-keyspace" { - if len(fields) < 5 { - goto invalidQuery - } - // handle optional "IF EXISTS" - table = fields[4] - } - } - } - if action == "truncate" && len(fields) == 2 { - // special case, truncate can just be passed table name - table = fields[1] - } - if fields[1] == "materialized" { - action = action + "-view" - } else if fields[1] == "custom" { - action = "create-index" - } - default: - goto invalidQuery - } - - if len(table) > 0 && !strings.Contains(table, ".") && action != "use" { - table = p.keyspace + "." + table - } - return action, table - -invalidQuery: - - logrus.Errorf("Unable to parse query: '%s'", query) - return "", "" -} - -func cassandraParseRequest(p *CassandraParser, data []byte) (OpError, []string) { - - direction := data[0] & 0x80 // top bit - if direction != 0 { - logrus.Errorf("Direction bit is 'reply', but we are trying to parse a request") - return ERROR_INVALID_FRAME_TYPE, nil - } - - compressionFlag := data[1] & 0x01 - if compressionFlag == 1 { - logrus.Errorf("Compression flag set, unable to parse request beyond the header") - return ERROR_INVALID_FRAME_TYPE, nil - } - - opcode := data[4] - path := opcodeMap[opcode] - - // parse query string from query/prepare/batch requests - - // NOTE: parsing only prepare statements and passing all execute - // statements requires that we 'invalidate' all execute statements - // anytime policy changes, to ensure that no execute statements are - // allowed that correspond to prepared queries that would no longer - // be valid. A better option might be to cache all prepared queries, - // mapping the execution ID to allow/deny each time policy is changed. - if opcode == 0x07 || opcode == 0x09 { - // query || prepare - queryLen := binary.BigEndian.Uint32(data[9:13]) - endIndex := 13 + queryLen - query := string(data[13:endIndex]) - action, table := parseQuery(p, query) - - if action == "" { - return ERROR_INVALID_FRAME_TYPE, nil - } - - path = "/" + path + "/" + action + "/" + table - if opcode == 0x09 { - // stash 'path' for this prepared query based on stream id - // rewrite 'opcode' portion of the path to be 'execute' rather than 'prepare' - streamID := binary.BigEndian.Uint16(data[2:4]) - logrus.Debugf("Prepare query path '%s' with stream-id %d", path, streamID) - p.preparedQueryPathByStreamID[streamID] = strings.Replace(path, "prepare", "execute", 1) - } - return 0, []string{path} - } else if opcode == 0x0d { - // batch - - numQueries := binary.BigEndian.Uint16(data[10:12]) - paths := make([]string, numQueries) - logrus.Debugf("batch query count = %d", numQueries) - offset := 12 - for i := 0; i < int(numQueries); i++ { - kind := data[offset] - if kind == 0 { - // full query string - queryLen := int(binary.BigEndian.Uint32(data[offset+1 : offset+5])) - - query := string(data[offset+5 : offset+5+queryLen]) - action, table := parseQuery(p, query) - - if action == "" { - return ERROR_INVALID_FRAME_TYPE, nil - } - path = "/" + path + "/" + action + "/" + table - paths[i] = path - path = "batch" // reset for next item - offset = offset + 5 + queryLen - offset = readPastBatchValues(data, offset) - } else if kind == 1 { - // prepared query id - - idLen := int(binary.BigEndian.Uint16(data[offset+1 : offset+3])) - preparedID := string(data[offset+3 : (offset + 3 + idLen)]) - logrus.Debugf("Batch entry with prepared-id = '%s'", preparedID) - path := p.preparedQueryPathByPreparedID[preparedID] - if len(path) > 0 { - paths[i] = path - } else { - logrus.Warnf("No cached entry for prepared-id = '%s' in batch", preparedID) - unpreparedMsg := createUnpreparedMsg(data[0], data[2:4], preparedID) - p.connection.Inject(true, unpreparedMsg) - return 0, []string{unknownPreparedQueryPath} - } - offset = offset + 3 + idLen - - offset = readPastBatchValues(data, offset) - } else { - logrus.Errorf("unexpected value of 'kind' in batch query: %d", kind) - return ERROR_INVALID_FRAME_TYPE, nil - } - } - return 0, paths - } else if opcode == 0x0a { - // execute - - // parse out prepared query id, and then look up our - // cached query path for policy evaluation. - idLen := binary.BigEndian.Uint16(data[9:11]) - preparedID := string(data[11:(11 + idLen)]) - logrus.Debugf("Execute with prepared-id = '%s'", preparedID) - path := p.preparedQueryPathByPreparedID[preparedID] - - if len(path) == 0 { - logrus.Warnf("No cached entry for prepared-id = '%s'", preparedID) - unpreparedMsg := createUnpreparedMsg(data[0], data[2:4], preparedID) - p.connection.Inject(true, unpreparedMsg) - - // this path is special-cased in Matches() so that unknown - // prepared IDs are dropped if any rules are defined - return 0, []string{unknownPreparedQueryPath} - } - - return 0, []string{path} - } else { - // other opcode, just return type of opcode - - return 0, []string{"/" + path} - } - -} - -func readPastBatchValues(data []byte, initialOffset int) int { - numValues := int(binary.BigEndian.Uint16(data[initialOffset : initialOffset+2])) - offset := initialOffset + 2 - for i := 0; i < numValues; i++ { - valueLen := int(binary.BigEndian.Uint32(data[offset : offset+4])) - // handle 'null' (-1) and 'not set' (-2) case, where 0 bytes follow - if valueLen >= 0 { - offset = offset + 4 + valueLen - } - } - return offset -} - -// reply parsing is very basic, just focusing on parsing RESULT messages that -// contain prepared query IDs so that we can later enforce policy on "execute" requests. -func cassandraParseReply(p *CassandraParser, data []byte) { - - direction := data[0] & 0x80 // top bit - if direction != 0x80 { - logrus.Errorf("Direction bit is 'request', but we are trying to parse a reply") - return - } - - compressionFlag := data[1] & 0x01 - if compressionFlag == 1 { - logrus.Errorf("Compression flag set, unable to parse reply beyond the header") - return - } - - streamID := binary.BigEndian.Uint16(data[2:4]) - logrus.Debugf("Reply with opcode %d and stream-id %d", data[4], streamID) - // if this is an opcode == RESULT message of type 'prepared', associate the prepared - // statement id with the full query string that was included in the - // associated PREPARE request. The stream-id in this reply allows us to - // find the associated prepare query string. - if data[4] == 0x08 { - resultKind := binary.BigEndian.Uint32(data[9:13]) - logrus.Debugf("resultKind = %d", resultKind) - if resultKind == 0x0004 { - idLen := binary.BigEndian.Uint16(data[13:15]) - preparedID := string(data[15 : 15+idLen]) - logrus.Debugf("Result with prepared-id = '%s' for stream-id %d", preparedID, streamID) - path := p.preparedQueryPathByStreamID[streamID] - if len(path) > 0 { - // found cached query path to associate with this preparedID - p.preparedQueryPathByPreparedID[preparedID] = path - logrus.Debugf("Associating query path '%s' with prepared-id %s as part of stream-id %d", path, preparedID, streamID) - } else { - logrus.Warnf("Unable to find prepared query path associated with stream-id %d", streamID) - } - } - } -} diff --git a/proxylib/cassandra/cassandraparser_test.go b/proxylib/cassandra/cassandraparser_test.go deleted file mode 100644 index e02f36b90..000000000 --- a/proxylib/cassandra/cassandraparser_test.go +++ /dev/null @@ -1,789 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package cassandra - -import ( - "encoding/hex" - "testing" - - "github.com/stretchr/testify/require" - - "github.com/cilium/proxy/proxylib/accesslog" - "github.com/cilium/proxy/proxylib/proxylib" - "github.com/cilium/proxy/proxylib/test" -) - -type CassandraSuite struct { - logServer *test.AccessLogServer - ins *proxylib.Instance -} - -// Set up access log server and Library instance for all the test cases -func setUpCassandraSuite(tb testing.TB) *CassandraSuite { - s := &CassandraSuite{} - s.logServer = test.StartAccessLogServer("access_log.sock", 10) - require.NotNil(tb, s.logServer) - s.ins = proxylib.NewInstance("node1", accesslog.NewClient(s.logServer.Path)) - require.NotNil(tb, s.ins) - - tb.Cleanup(func() { - s.logServer.Clear() - s.logServer.Close() - }) - return s -} - -func (s *CassandraSuite) checkAccessLogs(tb testing.TB, expPasses, expDrops int) { - passes, drops := s.logServer.Clear() - require.Equal(tb, expPasses, passes) - require.Equal(tb, expDrops, drops) -} - -// util function used for Cassandra tests, as we have cassandra requests -// as hex strings -func hexData(tb testing.TB, dataHex ...string) [][]byte { - data := make([][]byte, 0, len(dataHex)) - for i := range dataHex { - dataRaw, err := hex.DecodeString(dataHex[i]) - require.NoError(tb, err) - data = append(data, dataRaw) - } - return data -} - -func TestCassandraOnDataNoHeader(t *testing.T) { - s := setUpCassandraSuite(t) - conn := s.ins.CheckNewConnectionOK(t, "cassandra", true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "no-policy") - data := hexData(t, "0400") - conn.CheckOnDataOK(t, false, false, &data, []byte{}, - proxylib.MORE, 9-len(data[0])) -} - -func TestCassandraOnDataOptionsReq(t *testing.T) { - s := setUpCassandraSuite(t) - s.ins.CheckInsertPolicyText(t, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 1 - remote_policies: 3 - remote_policies: 4 - l7_proto: "cassandra" - l7_rules: < - l7_allow_rules: < - rule: < - key: "query_action" - value: "select" - > - > - > - > - > - `}) - conn := s.ins.CheckNewConnectionOK(t, "cassandra", true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1") - - data := hexData(t, "040000000500000000") - conn.CheckOnDataOK(t, false, false, &data, []byte{}, - proxylib.PASS, len(data[0]), - proxylib.MORE, 9) -} - -// this passes a large query request that is missing just the last byte -func TestCassandraOnDataPartialReq(t *testing.T) { - s := setUpCassandraSuite(t) - s.ins.CheckInsertPolicyText(t, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 1 - remote_policies: 3 - remote_policies: 4 - l7_proto: "cassandra" - l7_rules: < - l7_allow_rules: < - rule: < - key: "query_table" - value: ".*" - > - > - > - > - > - `}) - conn := s.ins.CheckNewConnectionOK(t, "cassandra", true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1") - data := hexData(t, "0400000407000000760000006f53454c45435420636c75737465725f6e616d652c20646174615f63656e7465722c207261636b2c20746f6b656e732c20706172746974696f6e65722c20736368656d615f76657273696f6e2046524f4d2073797374656d2e6c6f63616c205748455245206b65793d276c6f63616c270001") - conn.CheckOnDataOK(t, false, false, &data, []byte{}, - proxylib.MORE, 1) -} - -func TestCassandraOnDataQueryReq(t *testing.T) { - s := setUpCassandraSuite(t) - s.ins.CheckInsertPolicyText(t, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 1 - remote_policies: 3 - remote_policies: 4 - l7_proto: "cassandra" - l7_rules: < - l7_allow_rules: < - rule: < - key: "query_table" - value: ".*" - > - > - > - > - > - `}) - conn := s.ins.CheckNewConnectionOK(t, "cassandra", true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1") - data := hexData(t, "0400000407000000760000006f53454c45435420636c75737465725f6e616d652c20646174615f63656e7465722c207261636b2c20746f6b656e732c20706172746974696f6e65722c20736368656d615f76657273696f6e2046524f4d2073797374656d2e6c6f63616c205748455245206b65793d276c6f63616c27000100") - conn.CheckOnDataOK(t, false, false, &data, []byte{}, - proxylib.PASS, len(data[0]), - proxylib.MORE, 9) -} - -func TestCassandraOnDataSplitQueryReq(t *testing.T) { - s := setUpCassandraSuite(t) - s.ins.CheckInsertPolicyText(t, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 1 - remote_policies: 3 - remote_policies: 4 - l7_proto: "cassandra" - l7_rules: < - l7_allow_rules: < - rule: < - key: "query_table" - value: ".*" - > - > - > - > - > - `}) - conn := s.ins.CheckNewConnectionOK(t, "cassandra", true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1") - data := hexData(t, "04000004070000007600", "00006f53454c45435420636c75737465725f6e616d652c20646174615f63656e7465722c207261636b2c20746f6b656e732c20706172746974696f6e65722c20736368656d615f76657273696f6e2046524f4d2073797374656d2e6c6f63616c205748455245206b65793d276c6f63616c27000100") - conn.CheckOnDataOK(t, false, false, &data, []byte{}, - proxylib.PASS, len(data[0])+len(data[1]), - proxylib.MORE, 9) -} - -func TestCassandraOnDataMultiReq(t *testing.T) { - s := setUpCassandraSuite(t) - s.ins.CheckInsertPolicyText(t, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 1 - remote_policies: 3 - remote_policies: 4 - l7_proto: "cassandra" - l7_rules: < - l7_allow_rules: < - rule: < - key: "query_table" - value: ".*" - > - > - > - > - > - `}) - - conn := s.ins.CheckNewConnectionOK(t, "cassandra", true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1") - - data := hexData(t, "040000000500000000", - "0400000407000000760000006f53454c45435420636c75737465725f6e616d652c20646174615f63656e7465722c207261636b2c20746f6b656e732c20706172746974696f6e65722c20736368656d615f76657273696f6e2046524f4d2073797374656d2e6c6f63616c205748455245206b65793d276c6f63616c27000100") - conn.CheckOnDataOK(t, false, false, &data, []byte{}, - proxylib.PASS, len(data[0]), - proxylib.PASS, len(data[1]), - proxylib.MORE, 9) -} - -func TestSimpleCassandraPolicy(t *testing.T) { - s := setUpCassandraSuite(t) - s.ins.CheckInsertPolicyText(t, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 1 - remote_policies: 3 - remote_policies: 4 - l7_proto: "cassandra" - l7_rules: < - l7_allow_rules: < - rule: < - key: "query_table" - value: "no-match" - > - > - > - > - > - `}) - - conn := s.ins.CheckNewConnectionOK(t, "cassandra", true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1") - - unauthMsg := createUnauthMsg(0x4) - data := hexData(t, "040000000500000000", - "0400000407000000760000006f53454c45435420636c75737465725f6e616d652c20646174615f63656e7465722c207261636b2c20746f6b656e732c20706172746974696f6e65722c20736368656d615f76657273696f6e2046524f4d2073797374656d2e6c6f63616c205748455245206b65793d276c6f63616c27000100") - conn.CheckOnDataOK(t, false, false, &data, unauthMsg, - proxylib.PASS, len(data[0]), - proxylib.DROP, len(data[1]), - proxylib.MORE, 9) - - // All passes are not access-logged - s.checkAccessLogs(t, 0, 1) -} - -func createUnauthMsg(streamID byte) []byte { - unauthMsg := make([]byte, len(unauthMsgBase)) - copy(unauthMsg, unauthMsgBase) - unauthMsg[0] = 0x84 - unauthMsg[2] = 0x0 - unauthMsg[3] = streamID - return unauthMsg -} - -// this test confirms that we correctly parse and allow a valid batch requests -func TestCassandraBatchRequestPolicy(t *testing.T) { - s := setUpCassandraSuite(t) - s.ins.CheckInsertPolicyText(t, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 1 - remote_policies: 3 - remote_policies: 4 - l7_proto: "cassandra" - l7_rules: < - l7_allow_rules: < - rule: < - key: "query_table" - value: "db1.*" - > - > - > - > - > - `}) - - conn := s.ins.CheckNewConnectionOK(t, "cassandra", true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1") - - batchMsg := []byte{ - 0x04, // version - 0x0, // flags, (uint8) - 0x0, 0x4, // stream-id (uint16) (test request uses 0x0004 as stream ID) - 0x0d, // opcode batch (uint8) - 0x0, 0x0, 0x0, 0x3c, // request length of 60 (uint32) - update if body changes - 0x0, // batch type == logged - 0x0, 0x2, // two batch messages - - // first batch message - 0x0, // type: non-prepared query - 0x0, 0x0, 0x0, 0x14, // [long string] length (20) - 'S', 'E', 'L', 'E', 'C', 'T', ' ', '*', ' ', 'F', 'R', 'O', 'M', ' ', 'd', 'b', '1', '.', 't', '1', - 0x0, 0x0, // # of bound values - - // second batch message - 0x0, // type: non-prepared query - 0x0, 0x0, 0x0, 0x14, // [long string] length (20) - 'S', 'E', 'L', 'E', 'C', 'T', ' ', '*', ' ', 'F', 'R', 'O', 'M', ' ', 'd', 'b', '1', '.', 't', '2', - 0x0, 0x0, // # of bound values - - 0x0, 0x0, // consistency level [short] - 0x0, // batch flags - } - data := [][]byte{batchMsg} - - conn.CheckOnDataOK(t, false, false, &data, []byte{}, - proxylib.PASS, len(data[0]), - proxylib.MORE, 9) - - // batch requests are access-logged individually - s.checkAccessLogs(t, 2, 0) -} - -// this test confirms that we correctly parse and deny a batch request -// if any of the requests are denied. -func (s *CassandraSuite) TestCassandraBatchRequestPolicyDenied(c *testing.T) { - s.ins.CheckInsertPolicyText(c, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 1 - remote_policies: 3 - remote_policies: 4 - l7_proto: "cassandra" - l7_rules: < - l7_allow_rules: < - rule: < - key: "query_table" - value: "db1.*" - > - > - > - > - > - `}) - - conn := s.ins.CheckNewConnectionOK(c, "cassandra", true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1") - - batchMsg := []byte{ - 0x04, // version - 0x0, // flags, (uint8) - 0x0, 0x4, // stream-id (uint16) (test request uses 0x0004 as stream ID) - 0x0d, // opcode batch (uint8) - 0x0, 0x0, 0x0, 0x3c, // request length of 60 (uint32) - update if body changes - 0x0, // batch type == logged - 0x0, 0x2, // two batch messages - - // first batch message - 0x0, // type: non-prepared query - 0x0, 0x0, 0x0, 0x14, // [long string] length (20) - 'S', 'E', 'L', 'E', 'C', 'T', ' ', '*', ' ', 'F', 'R', 'O', 'M', ' ', 'd', 'b', '1', '.', 't', '1', - 0x0, 0x0, // # of bound values - - // second batch message (accesses db2.t2, which should be denied) - 0x0, // type: non-prepared query - 0x0, 0x0, 0x0, 0x14, // [long string] length (20) - 'S', 'E', 'L', 'E', 'C', 'T', ' ', '*', ' ', 'F', 'R', 'O', 'M', ' ', 'd', 'b', '2', '.', 't', '2', - 0x0, 0x0, // # of bound values - - 0x0, 0x0, // consistency level [short] - 0x0, // batch flags - } - data := [][]byte{batchMsg} - - unauthMsg := createUnauthMsg(0x4) - conn.CheckOnDataOK(c, false, false, &data, unauthMsg, - proxylib.DROP, len(data[0]), - proxylib.MORE, 9) - - // batch requests are access-logged individually - // Note: in this case, both accesses are denied, as a batch - // request is either entirely allowed or denied - s.checkAccessLogs(c, 0, 2) -} - -// test batch requests with prepared statements -func TestCassandraBatchRequestPreparedStatement(t *testing.T) { - s := setUpCassandraSuite(t) - s.ins.CheckInsertPolicyText(t, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 1 - remote_policies: 3 - remote_policies: 4 - l7_proto: "cassandra" - l7_rules: < - l7_allow_rules: < - rule: < - key: "query_table" - value: "db3.*" - > - > - > - > - > - `}) - - conn := s.ins.CheckNewConnectionOK(t, "cassandra", true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1") - - cassParser, ok := (conn.Parser).(*CassandraParser) - if !ok { - panic("failed to cast conn.Parser to *CassandraParser\n") - } - preparedQueryID1 := "aaaa" - cassParser.preparedQueryPathByPreparedID[preparedQueryID1] = "/batch/select/db3.t1" - preparedQueryID2 := "bbbb" - cassParser.preparedQueryPathByPreparedID[preparedQueryID2] = "/batch/select/db3.t2" - - batchMsg := []byte{ - 0x04, // version - 0x0, // flags, (uint8) - 0x0, 0x4, // stream-id (uint16) (test request uses 0x0004 as stream ID) - 0x0d, // opcode batch (uint8) - 0x0, 0x0, 0x0, 0x18, // request length of 60 (uint32) - update if body changes - 0x0, // batch type == logged - 0x0, 0x2, // two batch messages - - // first batch message - 0x1, // type: prepared query - 0x0, 0x4, // [short] length (4) - 'a', 'a', 'a', 'a', - 0x0, 0x0, // # of bound values - - // second batch message - 0x1, // type: non-prepared query - 0x0, 0x4, // [short] length (4) - 'b', 'b', 'b', 'b', - 0x0, 0x0, // # of bound values - - 0x0, 0x0, // consistency level [short] - 0x0, // batch flags - } - data := [][]byte{batchMsg} - - conn.CheckOnDataOK(t, false, false, &data, []byte{}, - proxylib.PASS, len(data[0]), - proxylib.MORE, 9) - - // batch requests are access-logged individually - s.checkAccessLogs(t, 2, 0) -} - -// test batch requests with prepared statements, including a deny -func TestCassandraBatchRequestPreparedStatementDenied(t *testing.T) { - s := setUpCassandraSuite(t) - s.ins.CheckInsertPolicyText(t, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 1 - remote_policies: 3 - remote_policies: 4 - l7_proto: "cassandra" - l7_rules: < - l7_allow_rules: < - rule: < - key: "query_table" - value: "db3.*" - > - > - > - > - > - `}) - - conn := s.ins.CheckNewConnectionOK(t, "cassandra", true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1") - - cassParser, ok := (conn.Parser).(*CassandraParser) - if !ok { - panic("failed to cast conn.Parser to *CassandraParser\n") - } - preparedQueryID1 := "aaaa" - cassParser.preparedQueryPathByPreparedID[preparedQueryID1] = "/batch/select/db3.t1" - preparedQueryID2 := "bbbb" - cassParser.preparedQueryPathByPreparedID[preparedQueryID2] = "/batch/select/db4.t2" - - batchMsg := []byte{ - 0x04, // version - 0x0, // flags, (uint8) - 0x0, 0x4, // stream-id (uint16) (test request uses 0x0004 as stream ID) - 0x0d, // opcode batch (uint8) - 0x0, 0x0, 0x0, 0x18, // request length of 60 (uint32) - update if body changes - 0x0, // batch type == logged - 0x0, 0x2, // two batch messages - - // first batch message - 0x1, // type: prepared query - 0x0, 0x4, // [short] length (4) - 'a', 'a', 'a', 'a', - 0x0, 0x0, // # of bound values - - // second batch message (accesses table db4, which should be denied) - 0x1, // type: non-prepared query - 0x0, 0x4, // [short] length (4) - 'b', 'b', 'b', 'b', - 0x0, 0x0, // # of bound values - - 0x0, 0x0, // consistency level [short] - 0x0, // batch flags - } - data := [][]byte{batchMsg} - - unauthMsg := createUnauthMsg(0x4) - conn.CheckOnDataOK(t, false, false, &data, unauthMsg, - proxylib.DROP, len(data[0]), - proxylib.MORE, 9) - - // batch requests are access-logged individually - s.checkAccessLogs(t, 0, 2) -} - -// test execute statement, allow request -func TestCassandraExecutePreparedStatement(t *testing.T) { - s := setUpCassandraSuite(t) - s.ins.CheckInsertPolicyText(t, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 1 - remote_policies: 3 - remote_policies: 4 - l7_proto: "cassandra" - l7_rules: < - l7_allow_rules: < - rule: < - key: "query_table" - value: "db3.*" - > - > - > - > - > - `}) - - conn := s.ins.CheckNewConnectionOK(t, "cassandra", true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1") - - cassParser, ok := (conn.Parser).(*CassandraParser) - if !ok { - panic("failed to cast conn.Parser to *CassandraParser\n") - } - preparedQueryID1 := "aaaa" - cassParser.preparedQueryPathByPreparedID[preparedQueryID1] = "/query/select/db3.t1" - - executeMsg := []byte{ - 0x04, // version - 0x0, // flags, (uint8) - 0x0, 0x4, // stream-id (uint16) (test request uses 0x0004 as stream ID) - 0x0a, // opcode execute (uint8) - 0x0, 0x0, 0x0, 0x09, // request length (uint32) - update if body changes - - // Execute request - 0x0, 0x4, // short bytes len (4) - 'a', 'a', 'a', 'a', - - // the rest of this is values that can be ignored by our parser, - // but we add some here to make sure that we're properly passing - // based on total request length. - 'x', 'y', 'z', - } - data := [][]byte{executeMsg} - - conn.CheckOnDataOK(t, false, false, &data, []byte{}, - proxylib.PASS, len(data[0]), - proxylib.MORE, 9) - - s.checkAccessLogs(t, 1, 0) -} - -// test execute statement with unknown prepared-id -func TestCassandraExecutePreparedStatementUnknownID(t *testing.T) { - s := setUpCassandraSuite(t) - conn := s.ins.CheckNewConnectionOK(t, "cassandra", true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "cp1") - - executeMsg := []byte{ - 0x04, // version - 0x0, // flags, (uint8) - 0x0, 0x4, // stream-id (uint16) (test request uses 0x0004 as stream ID) - 0x0a, // opcode execute (uint8) - 0x0, 0x0, 0x0, 0x06, // request length (uint32) - update if body changes - - // Execute request - 0x0, 0x4, // short bytes len (4) - 'a', 'a', 'a', 'a', - } - data := [][]byte{executeMsg} - - unpreparedMsg := createUnpreparedMsg(0x04, []byte{0x0, 0x4}, "aaaa") - - conn.CheckOnDataOK(t, false, false, &data, unpreparedMsg, - proxylib.DROP, len(data[0]), - proxylib.MORE, 9) - - s.checkAccessLogs(t, 0, 1) -} - -// test parsing of a prepared query reply -func TestCassandraPreparedResultReply(t *testing.T) { - s := setUpCassandraSuite(t) - conn := s.ins.CheckNewConnectionOK(t, "cassandra", true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "cp1") - - cassParser, ok := (conn.Parser).(*CassandraParser) - if !ok { - panic("failed to cast conn.Parser to *CassandraParser\n") - } - - // make sure there is a stream-id (4) that matches the request below - // this would have been populated by a "prepare" request - cassParser.preparedQueryPathByStreamID[uint16(4)] = "/query/select/db3.t1" - - preparedResultMsg := []byte{ - 0x84, // reply + version - 0x0, // flags, (uint8) - 0x0, 0x4, // stream-id (uint16) (test request uses 0x0004 as stream ID) - 0x08, // opcode result (uint8) - 0x0, 0x0, 0x0, 0x16, // request length 22 (uint32) - update if body changes - - // Prepared Result request - 0x0, 0x0, 0x0, 0x4, // [int] result type - 0x0, 0x4, // prepared-id len (short) - 'a', 'a', 'a', 'a', // prepared-id - 0x0, 0x0, 0x0, 0x0, // prepared results flags - 0x0, 0x0, 0x0, 0x0, // column-count - 0x0, 0x0, 0x0, 0x0, // pk-count - } - data := [][]byte{preparedResultMsg} - - conn.CheckOnDataOK(t, true, false, &data, []byte{}, - proxylib.PASS, len(data[0]), - proxylib.MORE, 9) - - // these replies are not access logged - s.checkAccessLogs(t, 0, 0) -} - -// test additional queries -func TestCassandraAdditionalQueries(t *testing.T) { - s := setUpCassandraSuite(t) - s.ins.CheckInsertPolicyText(t, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 1 - remote_policies: 3 - remote_policies: 4 - l7_proto: "cassandra" - l7_rules: < - l7_allow_rules: < - rule: < - key: "query_table" - value: "db4.t1" - > - > - > - > - > - `}) - - conn := s.ins.CheckNewConnectionOK(t, "cassandra", true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1") - - queries := []string{"CREATE TABLE db4.t1 (f1 varchar, f2 timeuuid, PRIMARY KEY ((f1), f2))", - "INSERT INTO db4.t1 (f1, f2, f3) values ('dan', now(), 'Cilium!')", - "UPDATE db4.t1 SET f1 = 'donald' where f2 in (1,2,3)", - "DROP TABLE db4.t1", - "TRUNCATE db4.t1", - "CREATE TABLE IF NOT EXISTS db4.t1 (f1 varchar, PRIMARY KEY(f1))", - } - - queryMsgBase := []byte{ - 0x04, // version - 0x0, // flags, (uint8) - 0x0, 0x5, // stream-id (uint16) (test request uses 0x0005 as stream ID) - 0x07, // opcode query (uint8) - 0x0, 0x0, 0x0, 0x0, // length of request - must be set - - // Query Req - 0x0, 0x0, 0x0, 0x0, // length of query (int) - must be set - // query string goes here - } - - data := make([][]byte, len(queries)) - for i := 0; i < len(queries); i++ { - queryLen := len(queries[i]) - - queryMsg := append(queryMsgBase, []byte(queries[i])...) - - // this works as long as query is less than 251 bytes - queryMsg[8] = byte(4 + queryLen) - queryMsg[12] = byte(queryLen) - - data[i] = queryMsg - } - conn.CheckOnDataOK(t, false, false, &data, []byte{}, - proxylib.PASS, len(data[0]), - proxylib.PASS, len(data[1]), - proxylib.PASS, len(data[2]), - proxylib.PASS, len(data[3]), - proxylib.PASS, len(data[4]), - proxylib.PASS, len(data[5]), - proxylib.MORE, 9) - - s.checkAccessLogs(t, 6, 0) -} - -// test use query, following by query that does not include the keyspace -func TestCassandraUseQuery(t *testing.T) { - s := setUpCassandraSuite(t) - s.ins.CheckInsertPolicyText(t, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 1 - remote_policies: 3 - remote_policies: 4 - l7_proto: "cassandra" - l7_rules: < - l7_allow_rules: < - rule: < - key: "query_table" - value: "db5.t1" - > - > - > - > - > - `}) - - conn := s.ins.CheckNewConnectionOK(t, "cassandra", true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1") - - // note: the second insert command intentionally does not include a keyspace, so that it will only - // be allowed if we properly propagate the keyspace from the previous use command - queries := []string{"USE db5", "INSERT INTO t1 (f1, f2, f3) values ('dan', now(), 'Cilium!')"} - - queryMsgBase := []byte{ - 0x04, // version - 0x0, // flags, (uint8) - 0x0, 0x5, // stream-id (uint16) (test request uses 0x0005 as stream ID) - 0x07, // opcode query (uint8) - 0x0, 0x0, 0x0, 0x0, // length of request - must be set - - // Query Req - 0x0, 0x0, 0x0, 0x0, // length of query (int) - must be set - // query string goes here - } - - data := make([][]byte, len(queries)) - for i := 0; i < len(queries); i++ { - queryLen := len(queries[i]) - - queryMsg := append(queryMsgBase, []byte(queries[i])...) - - // this works as long as query is less than 251 bytes - queryMsg[8] = byte(4 + queryLen) - queryMsg[12] = byte(queryLen) - - data[i] = queryMsg - } - conn.CheckOnDataOK(t, false, false, &data, []byte{}, - proxylib.PASS, len(data[0]), - proxylib.PASS, len(data[1]), - proxylib.MORE, 9) - - // use command will not show up in access log, so only expect one msg - s.checkAccessLogs(t, 1, 0) -} diff --git a/proxylib/kafka/kafkalib/doc.go b/proxylib/kafka/kafkalib/doc.go deleted file mode 100644 index 17b7623e7..000000000 --- a/proxylib/kafka/kafkalib/doc.go +++ /dev/null @@ -1,6 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -// Package kafkalib provides a library to parse Kafka requests and responses and -// apply policy rules -package kafkalib diff --git a/proxylib/kafka/kafkalib/error.go b/proxylib/kafka/kafkalib/error.go deleted file mode 100644 index d9491f3a2..000000000 --- a/proxylib/kafka/kafkalib/error.go +++ /dev/null @@ -1,43 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package kafkalib - -// List of possible Kafka error codes -// Reference: https://kafka.apache.org/protocol#protocol_error_codes -const ( - ErrUnknown = -1 - ErrNone = 0 - ErrOffsetOutOfRange = 1 - ErrInvalidMessage = 2 - ErrUnknownTopicOrPartition = 3 - ErrInvalidMessageSize = 4 - ErrLeaderNotAvailable = 5 - ErrNotLeaderForPartition = 6 - ErrRequestTimeout = 7 - ErrBrokerNotAvailable = 8 - ErrReplicaNotAvailable = 9 - ErrMessageSizeTooLarge = 10 - ErrScaleControllerEpoch = 11 - ErrOffsetMetadataTooLarge = 12 - ErrNetwork = 13 - ErrOffsetLoadInProgress = 14 - ErrNoCoordinator = 15 - ErrNotCoordinator = 16 - ErrInvalidTopic = 17 - ErrRecordListTooLarge = 18 - ErrNotEnoughReplicas = 19 - ErrNotEnoughReplicasAfterAppend = 20 - ErrInvalidRequiredAcks = 21 - ErrIllegalGeneration = 22 - ErrInconsistentPartitionAssignmentStrategy = 23 - ErrUnknownParititonAssignmentStrategy = 24 - ErrUnknownConsumerID = 25 - ErrInvalidSessionTimeout = 26 - ErrRebalanceInProgress = 27 - ErrInvalidCommitOffsetSize = 28 - ErrTopicAuthorizationFailed = 29 - ErrGroupAuthorizationFailed = 30 - ErrClusterAuthorizationFailed = 31 - ErrInvalidTimeStamp = 32 -) diff --git a/proxylib/kafka/kafkalib/policy.go b/proxylib/kafka/kafkalib/policy.go deleted file mode 100644 index 3ed17b0db..000000000 --- a/proxylib/kafka/kafkalib/policy.go +++ /dev/null @@ -1,162 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package kafkalib - -import ( - "github.com/sirupsen/logrus" - - api "github.com/cilium/proxy/pkg/policy/api/kafka" -) - -type Rule struct { - // ApiVersion is the allowed version, or < 0 if all versions - // are to be allowed - APIVersion int16 - - // ApiKeys is the set of all numerical apiKeys that are allowed. - // If empty, all API keys are allowed. - APIKeys map[int16]struct{} - - // ClientID is the client identifier as provided in the request. - // - // From Kafka protocol documentation: - // This is a user supplied identifier for the client application. The - // user can use any identifier they like and it will be used when - // logging errors, monitoring aggregates, etc. For example, one might - // want to monitor not just the requests per second overall, but the - // number coming from each client application (each of which could - // reside on multiple servers). This id acts as a logical grouping - // across all requests from a particular client. - // - // If empty, all client identifiers are allowed. - ClientID string - - // Topic is the topic name contained in the message. If a Kafka request - // contains multiple topics, then all topics must be allowed or the - // message will be rejected. - // - // This constraint is ignored if the matched request message type - // doesn't contain any topic. Maximum size of Topic can be 249 - // characters as per recent Kafka spec and allowed characters are - // a-z, A-Z, 0-9, -, . and _ - // Older Kafka versions had longer topic lengths of 255, but in Kafka 0.10 - // version the length was changed from 255 to 249. For compatibility - // reasons we are allowing 255. - // - // If empty, all topics are allowed. - Topic string -} - -// NewRule creates a new rule from already sanitized inputs -func NewRule(apiVersion int32, apiKeys []int32, clientID, topic string) Rule { - r := Rule{ - APIVersion: int16(apiVersion), - ClientID: clientID, - Topic: topic, - APIKeys: make(map[int16]struct{}, len(apiKeys)), - } - for _, key := range apiKeys { - r.APIKeys[int16(key)] = struct{}{} - } - return r -} - -// CheckAPIKeyRole checks the apiKey value in the request, and returns true if -// it is allowed else false -func (r *Rule) CheckAPIKeyRole(kind int16) bool { - // wildcard expression - if len(r.APIKeys) == 0 { - return true - } - - // Check kind - _, ok := r.APIKeys[kind] - return ok -} - -// CheckAPIVersion returns true if 'apiVersion' is allowed -func (r *Rule) CheckAPIVersion(apiVersion int16) bool { - return r.APIVersion < 0 || apiVersion == r.APIVersion -} - -// CheckClientID returns true if 'clientID' is allowed -func (r *Rule) CheckClientID(clientID string) bool { - return r.ClientID == "" || clientID == r.ClientID -} - -// isTopicAPIKey returns true if kind is apiKey message type which contains a -// topic in its request. -func isTopicAPIKey(kind int16) bool { - switch kind { - case api.ProduceKey, - api.FetchKey, - api.OffsetsKey, - api.MetadataKey, - api.LeaderAndIsr, - api.StopReplica, - api.UpdateMetadata, - api.OffsetCommitKey, - api.OffsetFetchKey, - api.CreateTopicsKey, - api.DeleteTopicsKey, - api.DeleteRecordsKey, - api.OffsetForLeaderEpochKey, - api.AddPartitionsToTxnKey, - api.WriteTxnMarkersKey, - api.TxnOffsetCommitKey, - api.AlterReplicaLogDirsKey, - api.DescribeLogDirsKey, - api.CreatePartitionsKey: - - return true - } - return false -} - -// Matches returns true if Rule matches the request and and all required topics have matched. -func (r Rule) Matches(data interface{}) bool { - req, ok := data.(*RequestMessage) - if !ok { - logrus.Warningf("Matches() called with type other than Kafka RequestMessage: %v", data) - return false - } - - logrus.Debugf("Matching Kafka request %s against rule %v", req.String(), r) - - if !r.CheckAPIKeyRole(req.kind) { - return false - } - - if !r.CheckAPIVersion(req.version) { - return false - } - - if !r.CheckClientID(req.clientID) { - return false - } - - // Last step, check topic if applicable. - // Rule without a topic allows all topics and request types without topics - // are allowed regardless the rule's topic. - if r.Topic != "" && isTopicAPIKey(req.kind) { - // Rule has a topic constraint and the request type carries topics. - // - // Check it this rule's topic is in the request, but keep matching - // other rules (by returning false) even if this rule is satisfied - // if there are other topics in the request not matched yet. - // - // (req.topics is initialized with all the topics in the request - // before any rules are matched.) - if _, exists := req.topics[r.Topic]; exists { - delete(req.topics, r.Topic) - if len(req.topics) == 0 { - return true // all topics have matched - } - } - return false // more topic matches needed - } - - // All rule's constraints are satisfied - return true -} diff --git a/proxylib/kafka/kafkalib/policy_test.go b/proxylib/kafka/kafkalib/policy_test.go deleted file mode 100644 index 110583728..000000000 --- a/proxylib/kafka/kafkalib/policy_test.go +++ /dev/null @@ -1,132 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package kafkalib - -import ( - "testing" - "time" - - "github.com/cilium/kafka/proto" - "github.com/stretchr/testify/require" - - "github.com/cilium/proxy/pkg/policy/api/kafka" -) - -type kafkaTestSuite struct{} - -var messages = make([]*proto.Message, 100) - -func setUpKafkaTestSuite(tb testing.TB) *kafkaTestSuite { - tb.Helper() - for i := range messages { - messages[i] = &proto.Message{ - Offset: int64(i), - Crc: uint32(i), - Key: nil, - Value: []byte(`Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec a diam lectus. Sed sit amet ipsum mauris. Maecenas congue ligula ac quam viverra nec consectetur ante hendrerit. Donec et mollis dolor. Praesent et diam eget libero egestas mattis sit amet vitae augue. Nam tincidunt congue enim, ut porta lorem lacinia consectetur.`), - } - } - return &kafkaTestSuite{} -} - -// MatchesRule validates the Kafka request message against the provided list of -// rules. The function will return true if the policy allows the message, -// otherwise false is returned. -func (req *RequestMessage) MatchesRule(rules []Rule) bool { - for _, rule := range rules { - if rule.Matches(req) { - return true - } - } - return false -} - -func TestProduceRequest(c *testing.T) { - setUpKafkaTestSuite(c) - req := &proto.ProduceReq{ - CorrelationID: 241, - ClientID: "test", - Compression: proto.CompressionNone, - RequiredAcks: proto.RequiredAcksAll, - Timeout: time.Second, - Topics: []proto.ProduceReqTopic{ - { - Name: "foo", - Partitions: []proto.ProduceReqPartition{ - { - ID: 0, - Messages: messages, - }, - }, - }, - { - Name: "bar", - Partitions: []proto.ProduceReqPartition{ - { - ID: 0, - Messages: messages, - }, - }, - }, - }, - } - - reqMsg := RequestMessage{ - request: req, - } - - // empty rules should match nothing - reqMsg.setTopics() - require.False(c, reqMsg.MatchesRule([]Rule{})) - - // wildcard rule matches everything - reqMsg.setTopics() - require.True(c, reqMsg.MatchesRule([]Rule{{}})) - - reqMsg.setTopics() - require.False(c, reqMsg.MatchesRule([]Rule{ - NewRule(-1, nil, "", "foo"), - })) - - reqMsg.setTopics() - require.True(c, reqMsg.MatchesRule([]Rule{ - NewRule(-1, nil, "", "foo"), NewRule(-1, nil, "", "bar"), - })) - - reqMsg.setTopics() - require.False(c, reqMsg.MatchesRule([]Rule{ - NewRule(-1, nil, "", "foo"), NewRule(-1, nil, "", "baz"), - })) - - reqMsg.setTopics() - require.False(c, reqMsg.MatchesRule([]Rule{ - NewRule(-1, nil, "", "baz"), NewRule(-1, nil, "", "foo2"), - })) - - reqMsg.setTopics() - require.True(c, reqMsg.MatchesRule([]Rule{ - NewRule(-1, nil, "", "bar"), NewRule(-1, nil, "", "foo"), - })) - - reqMsg.setTopics() - require.True(c, reqMsg.MatchesRule([]Rule{ - NewRule(-1, nil, "", "bar"), NewRule(-1, nil, "", "foo"), NewRule(-1, nil, "", "baz"), - })) -} - -func TestUnknownRequest(t *testing.T) { - setUpKafkaTestSuite(t) - reqMsg := RequestMessage{kind: 18} // ApiVersions request - - // Empty rule should disallow - require.False(t, reqMsg.MatchesRule([]Rule{})) - - // Whitelisting of unknown message - rule1 := NewRule(-1, []int32{int32(kafka.MetadataKey)}, "", "") - rule2 := NewRule(-1, []int32{int32(kafka.APIVersionsKey)}, "", "") - require.True(t, reqMsg.MatchesRule([]Rule{rule1, rule2})) - - reqMsg = RequestMessage{kind: 19} - require.False(t, reqMsg.MatchesRule([]Rule{rule1, rule2})) -} diff --git a/proxylib/kafka/kafkalib/request.go b/proxylib/kafka/kafkalib/request.go deleted file mode 100644 index 873383abc..000000000 --- a/proxylib/kafka/kafkalib/request.go +++ /dev/null @@ -1,254 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package kafkalib - -import ( - "bytes" - "encoding/binary" - "encoding/json" - "fmt" - "io" - - "github.com/cilium/kafka/proto" - "github.com/sirupsen/logrus" -) - -// RequestMessage represents a Kafka request message -type RequestMessage struct { - kind int16 - version int16 - clientID string - rawMsg []byte - request interface{} - // Maintain a map of all topics in the request. We should - // allow the request only if all topics in the request are - // allowed by the rules. - topics map[string]struct{} -} - -// CorrelationID represents the correlation id as defined in the Kafka protocol -// specification -type CorrelationID uint32 - -// GetAPIKey returns the kind of Kafka request -func (req *RequestMessage) GetAPIKey() int16 { - return req.kind -} - -// GetRaw returns the raw Kafka request -func (req *RequestMessage) GetRaw() []byte { - return req.rawMsg -} - -// GetVersion returns the version Kafka request -func (req *RequestMessage) GetVersion() int16 { - return req.version -} - -// GetCorrelationID returns the Kafka request correlationID -func (req *RequestMessage) GetCorrelationID() CorrelationID { - if len(req.rawMsg) >= 12 { - return CorrelationID(binary.BigEndian.Uint32(req.rawMsg[8:12])) - } - - return CorrelationID(0) -} - -// SetCorrelationID modified the correlation ID of the Kafka request -func (req *RequestMessage) SetCorrelationID(id CorrelationID) { - if len(req.rawMsg) >= 12 { - binary.BigEndian.PutUint32(req.rawMsg[8:12], uint32(id)) - } -} - -func (req *RequestMessage) extractVersion() int16 { - return int16(binary.BigEndian.Uint16(req.rawMsg[6:8])) -} - -func (req *RequestMessage) extractClientID() string { - if req.version == 0 || len(req.rawMsg) < 14 { - return "" // 0 version has no client ID - } - // ref. https://kafka.apache.org/protocol#protocol_details - length := int16(binary.BigEndian.Uint16(req.rawMsg[12:14])) - if length <= 0 || len(req.rawMsg) < 14+int(length) { - return "" - } - return string(req.rawMsg[14 : 14+int(length)]) -} - -// String returns a human readable representation of the request message -func (req *RequestMessage) String() string { - b, err := json.Marshal(req.request) - if err != nil { - return err.Error() - } - - return fmt.Sprintf("apiKey=%d,apiVersion=%d,len=%d: %s", - req.kind, req.version, len(req.rawMsg), string(b)) -} - -// GetTopics returns the Kafka request list of topics -func (req *RequestMessage) GetTopics() []string { - if req.request == nil { - return nil - } - topics := make([]string, 0, len(req.topics)) - for topic := range req.topics { - topics = append(topics, topic) - } - return topics -} - -func (req *RequestMessage) setTopics() { - var topics []string - switch val := req.request.(type) { - case *proto.ProduceReq: - topics = produceTopics(val) - case *proto.FetchReq: - topics = fetchTopics(val) - case *proto.OffsetReq: - topics = offsetTopics(val) - case *proto.MetadataReq: - topics = metadataTopics(val) - case *proto.OffsetCommitReq: - topics = offsetCommitTopics(val) - case *proto.OffsetFetchReq: - topics = offsetFetchTopics(val) - } - req.topics = make(map[string]struct{}, len(topics)) - for _, topic := range topics { - req.topics[topic] = struct{}{} - } -} - -func produceTopics(req *proto.ProduceReq) []string { - topics := make([]string, len(req.Topics)) - for k, topic := range req.Topics { - topics[k] = topic.Name - } - return topics -} - -func fetchTopics(req *proto.FetchReq) []string { - topics := make([]string, len(req.Topics)) - for k, topic := range req.Topics { - topics[k] = topic.Name - } - return topics -} - -func offsetTopics(req *proto.OffsetReq) []string { - topics := make([]string, len(req.Topics)) - for k, topic := range req.Topics { - topics[k] = topic.Name - } - return topics -} - -func metadataTopics(req *proto.MetadataReq) []string { - topics := req.Topics - return topics -} - -func offsetCommitTopics(req *proto.OffsetCommitReq) []string { - topics := make([]string, len(req.Topics)) - for k, topic := range req.Topics { - topics[k] = topic.Name - } - return topics -} - -func offsetFetchTopics(req *proto.OffsetFetchReq) []string { - topics := make([]string, len(req.Topics)) - for k, topic := range req.Topics { - topics[k] = topic.Name - } - return topics -} - -// CreateResponse creates a response message based on the provided request -// message. The response will have the specified error code set in all topics -// and embedded partitions. -func (req *RequestMessage) CreateResponse(err error) (*ResponseMessage, error) { - switch val := req.request.(type) { - case *proto.ProduceReq: - return createProduceResponse(val, err) - case *proto.FetchReq: - return createFetchResponse(val, err) - case *proto.OffsetReq: - return createOffsetResponse(val, err) - case *proto.MetadataReq: - return createMetadataResponse(val, err) - case *proto.ConsumerMetadataReq: - return createConsumerMetadataResponse(val, err) - case *proto.OffsetCommitReq: - return createOffsetCommitResponse(val, err) - case *proto.OffsetFetchReq: - return createOffsetFetchResponse(val, err) - case nil: - return nil, fmt.Errorf("unsupported request API key %d", req.kind) - default: - // The switch cases above must correspond exactly to the switch cases - // in ReadRequest. - logrus.Panic(fmt.Sprintf("Kafka API key not handled: %d", req.kind)) - } - return nil, nil -} - -// CreateAuthErrorResponse creates Authorization error response message for 'req' -func (req *RequestMessage) CreateAuthErrorResponse() (*ResponseMessage, error) { - return req.CreateResponse(proto.ErrTopicAuthorizationFailed) -} - -// ReadRequest will read a Kafka request from an io.Reader and return the -// message or an error. -func ReadRequest(reader io.Reader) (*RequestMessage, error) { - req := &RequestMessage{} - var err error - - req.kind, req.rawMsg, err = proto.ReadReq(reader) - if err != nil { - return nil, err - } - - if len(req.rawMsg) < 12 { - return nil, fmt.Errorf("unexpected end of request (length < 12 bytes)") - } - req.version = req.extractVersion() - req.clientID = req.extractClientID() - - var nilSlice []byte - buf := bytes.NewBuffer(append(nilSlice, req.rawMsg...)) - - switch req.kind { - case proto.ProduceReqKind: - req.request, err = proto.ReadProduceReq(buf) - case proto.FetchReqKind: - req.request, err = proto.ReadFetchReq(buf) - case proto.OffsetReqKind: - req.request, err = proto.ReadOffsetReq(buf) - case proto.MetadataReqKind: - req.request, err = proto.ReadMetadataReq(buf) - case proto.ConsumerMetadataReqKind: - req.request, err = proto.ReadConsumerMetadataReq(buf) - case proto.OffsetCommitReqKind: - req.request, err = proto.ReadOffsetCommitReq(buf) - case proto.OffsetFetchReqKind: - req.request, err = proto.ReadOffsetFetchReq(buf) - default: - logrus.Debugf("Unknown Kafka request API key: %d in %s", req.kind, req.String()) - } - - if err != nil { - if logrus.IsLevelEnabled(logrus.DebugLevel) { - logrus.WithError(err).Debugf("Ignoring Kafka message %s due to parse error", req.String()) - } - return nil, err - } - - req.setTopics() - - return req, nil -} diff --git a/proxylib/kafka/kafkalib/response.go b/proxylib/kafka/kafkalib/response.go deleted file mode 100644 index fa2dd081a..000000000 --- a/proxylib/kafka/kafkalib/response.go +++ /dev/null @@ -1,304 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package kafkalib - -import ( - "encoding/binary" - "encoding/json" - "fmt" - "io" - - "github.com/cilium/kafka/proto" -) - -// ResponseMessage represents a Kafka response message. -type ResponseMessage struct { - rawMsg []byte - response interface{} -} - -// GetCorrelationID returns the Kafka request correlationID -func (res *ResponseMessage) GetCorrelationID() CorrelationID { - if len(res.rawMsg) >= 8 { - return CorrelationID(binary.BigEndian.Uint32(res.rawMsg[4:8])) - } - - return CorrelationID(0) -} - -// SetCorrelationID modified the correlation ID of the Kafka request -func (res *ResponseMessage) SetCorrelationID(id CorrelationID) { - if len(res.rawMsg) >= 8 { - binary.BigEndian.PutUint32(res.rawMsg[4:8], uint32(id)) - } -} - -// GetRaw returns the raw Kafka response -func (res *ResponseMessage) GetRaw() []byte { - return res.rawMsg -} - -// String returns a human readable representation of the response message -func (res *ResponseMessage) String() string { - b, err := json.Marshal(res.response) - if err != nil { - return err.Error() - } - return string(b) -} - -// ReadResponse will read a Kafka response from an io.Reader and return the -// message or an error. -func ReadResponse(reader io.Reader) (*ResponseMessage, error) { - rsp := &ResponseMessage{} - var err error - - _, rsp.rawMsg, err = proto.ReadResp(reader) - if err != nil { - return nil, err - } - - if len(rsp.rawMsg) < 6 { - return nil, - fmt.Errorf("unexpected end of response (length < 6 bytes)") - } - - return rsp, nil -} - -func createProduceResponse(req *proto.ProduceReq, err error) (*ResponseMessage, error) { - if req == nil { - return nil, fmt.Errorf("request is nil") - } - - resp := &proto.ProduceResp{ - CorrelationID: req.CorrelationID, - Topics: make([]proto.ProduceRespTopic, len(req.Topics)), - } - - for k, topic := range req.Topics { - resp.Topics[k] = proto.ProduceRespTopic{ - Name: topic.Name, - Partitions: make([]proto.ProduceRespPartition, len(topic.Partitions)), - } - - for k2, partition := range topic.Partitions { - resp.Topics[k].Partitions[k2] = proto.ProduceRespPartition{ - ID: partition.ID, - Err: err, - } - } - } - - b, err := resp.Bytes(req.Version) - if err != nil { - return nil, err - } - - return &ResponseMessage{ - response: resp, - rawMsg: b, - }, nil -} - -func createFetchResponse(req *proto.FetchReq, err error) (*ResponseMessage, error) { - if req == nil { - return nil, fmt.Errorf("request is nil") - } - - resp := &proto.FetchResp{ - CorrelationID: req.CorrelationID, - Topics: make([]proto.FetchRespTopic, len(req.Topics)), - } - - for k, topic := range req.Topics { - resp.Topics[k] = proto.FetchRespTopic{ - Name: topic.Name, - Partitions: make([]proto.FetchRespPartition, len(topic.Partitions)), - } - - for k2, partition := range topic.Partitions { - resp.Topics[k].Partitions[k2] = proto.FetchRespPartition{ - ID: partition.ID, - Err: err, - AbortedTransactions: nil, // nullable - } - } - } - - b, err := resp.Bytes(req.Version) - if err != nil { - return nil, err - } - - return &ResponseMessage{ - response: resp, - rawMsg: b, - }, nil -} - -func createOffsetResponse(req *proto.OffsetReq, err error) (*ResponseMessage, error) { - if req == nil { - return nil, fmt.Errorf("request is nil") - } - - resp := &proto.OffsetResp{ - CorrelationID: req.CorrelationID, - Topics: make([]proto.OffsetRespTopic, len(req.Topics)), - } - - for k, topic := range req.Topics { - resp.Topics[k] = proto.OffsetRespTopic{ - Name: topic.Name, - Partitions: make([]proto.OffsetRespPartition, len(topic.Partitions)), - } - - for k2, partition := range topic.Partitions { - resp.Topics[k].Partitions[k2] = proto.OffsetRespPartition{ - ID: partition.ID, - Err: err, - Offsets: make([]int64, 0), // Not nullable, so must never be nil. - } - } - } - - b, err := resp.Bytes(req.Version) - if err != nil { - return nil, err - } - - return &ResponseMessage{ - response: resp, - rawMsg: b, - }, nil -} - -func createMetadataResponse(req *proto.MetadataReq, err error) (*ResponseMessage, error) { - if req == nil { - return nil, fmt.Errorf("request is nil") - } - - var topics []proto.MetadataRespTopic - if req.Topics != nil { - topics = make([]proto.MetadataRespTopic, len(req.Topics)) - } - resp := &proto.MetadataResp{ - CorrelationID: req.CorrelationID, - Brokers: make([]proto.MetadataRespBroker, 0), // Not nullable, so must never be nil. - Topics: topics, - } - - for k, topic := range req.Topics { - resp.Topics[k] = proto.MetadataRespTopic{ - Name: topic, - Err: err, - Partitions: make([]proto.MetadataRespPartition, 0), // Not nullable, so must never be nil. - } - } - - b, err := resp.Bytes(req.Version) - if err != nil { - return nil, err - } - - return &ResponseMessage{ - response: resp, - rawMsg: b, - }, nil -} - -func createConsumerMetadataResponse(req *proto.ConsumerMetadataReq, err error) (*ResponseMessage, error) { - if req == nil { - return nil, fmt.Errorf("request is nil") - } - - resp := &proto.ConsumerMetadataResp{ - CorrelationID: req.CorrelationID, - Err: err, - } - - b, err := resp.Bytes(req.Version) - if err != nil { - return nil, err - } - - return &ResponseMessage{ - response: resp, - rawMsg: b, - }, nil -} - -func createOffsetCommitResponse(req *proto.OffsetCommitReq, err error) (*ResponseMessage, error) { - if req == nil { - return nil, fmt.Errorf("request is nil") - } - - resp := &proto.OffsetCommitResp{ - CorrelationID: req.CorrelationID, - Topics: make([]proto.OffsetCommitRespTopic, len(req.Topics)), - } - - for k, topic := range req.Topics { - resp.Topics[k] = proto.OffsetCommitRespTopic{ - Name: topic.Name, - Partitions: make([]proto.OffsetCommitRespPartition, len(topic.Partitions)), - } - - for k2, partition := range topic.Partitions { - resp.Topics[k].Partitions[k2] = proto.OffsetCommitRespPartition{ - ID: partition.ID, - Err: err, - } - } - } - - b, err := resp.Bytes(req.Version) - if err != nil { - return nil, err - } - - return &ResponseMessage{ - response: resp, - rawMsg: b, - }, nil -} - -func createOffsetFetchResponse(req *proto.OffsetFetchReq, err error) (*ResponseMessage, error) { - if req == nil { - return nil, fmt.Errorf("request is nil") - } - - var topics []proto.OffsetFetchRespTopic - if req.Topics != nil { - topics = make([]proto.OffsetFetchRespTopic, len(req.Topics)) - } - resp := &proto.OffsetFetchResp{ - CorrelationID: req.CorrelationID, - Topics: topics, - } - - for k, topic := range req.Topics { - resp.Topics[k] = proto.OffsetFetchRespTopic{ - Name: topic.Name, - Partitions: make([]proto.OffsetFetchRespPartition, len(topic.Partitions)), - } - - for k2, partition := range topic.Partitions { - resp.Topics[k].Partitions[k2] = proto.OffsetFetchRespPartition{ - ID: partition, - Err: err, - } - } - } - - b, err := resp.Bytes(req.Version) - if err != nil { - return nil, err - } - - return &ResponseMessage{ - response: resp, - rawMsg: b, - }, nil -} diff --git a/proxylib/kafka/parser.go b/proxylib/kafka/parser.go deleted file mode 100644 index 64b5271ba..000000000 --- a/proxylib/kafka/parser.go +++ /dev/null @@ -1,122 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package kafka - -import ( - "encoding/binary" - - "github.com/sirupsen/logrus" - - cilium "github.com/cilium/proxy/go/cilium/api" - "github.com/cilium/proxy/proxylib/kafka/kafkalib" - . "github.com/cilium/proxy/proxylib/proxylib" -) - -const ( - parserName = "kafka" -) - -// KafkaRuleParser parses protobuf L7 rules to enforcement objects -// May panic -func KafkaRuleParser(rule *cilium.PortNetworkPolicyRule) []L7NetworkPolicyRule { - l7Rules := rule.GetKafkaRules() - if l7Rules == nil { - return nil - } - - allowRules := l7Rules.GetKafkaRules() - rules := make([]L7NetworkPolicyRule, 0, len(allowRules)) - for _, r := range allowRules { - rules = append(rules, kafkalib.NewRule(r.ApiVersion, r.ApiKeys, r.ClientId, r.Topic)) - } - return rules -} - -type KafkaParserFactory struct{} - -var kafkaParserFactory *KafkaParserFactory - -func init() { - logrus.Info("init(): Registering kafkaParserFactory") - RegisterParserFactory(parserName, kafkaParserFactory) - RegisterL7RuleParser(parserName, KafkaRuleParser) -} - -type KafkaParser struct { - connection *Connection -} - -func (pf *KafkaParserFactory) Create(connection *Connection) interface{} { - p := KafkaParser{connection: connection} - return &p -} - -func (p *KafkaParser) OnData(reply bool, reader *Reader) (OpType, int) { - length := reader.Length() - if length == 0 { - return NOP, 0 - } - - correlationID := int32(0) - framelength := 4 // account for the length field - lenbuf := make([]byte, 8) // Peek the first eight bytes - n, err := reader.PeekFull(lenbuf) - if err == nil { - framelength += int(binary.BigEndian.Uint32(lenbuf[:4])) - correlationID = int32(binary.BigEndian.Uint32(lenbuf[4:])) - } else { - // Need more data - return MORE, 8 - n - } - - if reply { - // Replies are always passed as-is. No need to parse them - // on top of the frame length and correlation ID. - p.connection.Log(cilium.EntryType_Response, - &cilium.LogEntry_Kafka{Kafka: &cilium.KafkaLogEntry{ - CorrelationId: correlationID, - }}) - return PASS, framelength - } - - // Ask for more if full frame has not been received yet - if length < framelength { - // Not enough data, ask for more and try again - return MORE, framelength - length - } - - req, err := kafkalib.ReadRequest(reader) - if err != nil { - logrus.WithError(err).Debug("Unable to parse Kafka request; closing Kafka connection") - p.connection.Log(cilium.EntryType_Denied, - &cilium.LogEntry_Kafka{Kafka: &cilium.KafkaLogEntry{ - CorrelationId: correlationID, - ErrorCode: kafkalib.ErrInvalidMessage, - }}) - return ERROR, int(ERROR_INVALID_FRAME_TYPE) - } - - logEntry := &cilium.LogEntry_Kafka{Kafka: &cilium.KafkaLogEntry{ - CorrelationId: correlationID, - ApiVersion: int32(req.GetVersion()), - ApiKey: int32(req.GetAPIKey()), - Topics: req.GetTopics(), - }} - if p.connection.Matches(req) { - p.connection.Log(cilium.EntryType_Request, logEntry) - return PASS, framelength - } - logEntry.Kafka.ErrorCode = kafkalib.ErrTopicAuthorizationFailed - - resp, err := req.CreateAuthErrorResponse() - if err != nil { - logrus.WithError(err).Debug("Unable to create Kafka response") - } else { - // inject response - p.connection.Inject(!reply, resp.GetRaw()) - } - - p.connection.Log(cilium.EntryType_Denied, logEntry) - return DROP, framelength -} diff --git a/proxylib/kafka/parser_test.go b/proxylib/kafka/parser_test.go deleted file mode 100644 index 1e7cc5058..000000000 --- a/proxylib/kafka/parser_test.go +++ /dev/null @@ -1,471 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package kafka - -import ( - "encoding/hex" - "testing" - - "github.com/sirupsen/logrus" - "github.com/stretchr/testify/require" - - "github.com/cilium/proxy/proxylib/accesslog" - "github.com/cilium/proxy/proxylib/proxylib" - "github.com/cilium/proxy/proxylib/test" -) - -type KafkaSuite struct { - logServer *test.AccessLogServer - ins *proxylib.Instance -} - -// Set up access log server and Library instance for all the test cases -func setUpKafkaSuite(tb testing.TB) *KafkaSuite { - logrus.SetLevel(logrus.DebugLevel) - s := &KafkaSuite{} - s.logServer = test.StartAccessLogServer("access_log.sock", 10) - require.NotNil(tb, s.logServer) - s.ins = proxylib.NewInstance("node1", accesslog.NewClient(s.logServer.Path)) - require.NotNil(tb, s.ins) - tb.Cleanup(func() { - s.logServer.Clear() - s.logServer.Close() - }) - return s -} - -func (s *KafkaSuite) checkAccessLogs(tb testing.TB, expPasses, expDrops int) { - passes, drops := s.logServer.Clear() - require.Equal(tb, expPasses, passes, "Unxpected number of passed access log messages") - require.Equal(tb, expDrops, drops, "Unxpected number of dropped access log messages") -} - -// util function used for Kafka tests, as we may have Kafka requests -// as hex strings -func hexData(tb testing.TB, dataHex ...string) [][]byte { - data := make([][]byte, 0, len(dataHex)) - for i := range dataHex { - dataRaw, err := hex.DecodeString(dataHex[i]) - require.NoError(tb, err) - data = append(data, dataRaw) - } - return data -} - -func TestKafkaOnDataNoHeader(t *testing.T) { - s := setUpKafkaSuite(t) - conn := s.ins.CheckNewConnectionOK(t, "kafka", true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "no-policy") - data := hexData(t, "") - conn.CheckOnDataOK(t, false, false, &data, []byte{}) - data = hexData(t, "00") - conn.CheckOnDataOK(t, false, false, &data, []byte{}, proxylib.MORE, 7) - data = hexData(t, "0000") - conn.CheckOnDataOK(t, false, false, &data, []byte{}, proxylib.MORE, 6) - data = hexData(t, "000001") - conn.CheckOnDataOK(t, false, false, &data, []byte{}, proxylib.MORE, 5) - data = hexData(t, "00000100") - conn.CheckOnDataOK(t, false, false, &data, []byte{}, proxylib.MORE, 4) - data = hexData(t, "00010000010203") - conn.CheckOnDataOK(t, false, false, &data, []byte{}, proxylib.MORE, 1) - data = hexData(t, "000100000102030405060708") - conn.CheckOnDataOK(t, false, false, &data, []byte{}, proxylib.MORE, 65536-8) -} - -var testMessage1 = "0000" // length = 42 (0x2a), first half - -var testMessage2 = "002a" + // length = 42 (0x2a), 2nd half - "0000" + // APIkey = 0 (Produce) - "0003" + // Version = 3 (KafkaV3) - "00010001" + // CorrelationID = 65537 - "0003414243" + // ClientID (string) "ABC" - "000144" + // TransactionalID (string) "D" - "0000" + // RequiredAcks = 0 - "000003" // Timeout = 1000 ms, first 3 bytes - -var testMessage3 = "E8" + // Timeout = 1000 ms, last byte - "00000002" + // Array length = 2 - "00024546" + // - TopicName (string) "EF" - "00000000" + // ProduceReqPartition array length = 0 - "00024748" + // - TopicName (string) "GH" - "00000000" // ProduceReqPartition array length = 0 - -var testMessage3Fail = "E8" + // Timeout = 1000 ms, last byte - "20000002" + // Array length = 0x20000002 (should cause failure - "00024546" + // - TopicName (string) "EF" - "00000000" + // ProduceReqPartition array length = 0 - "00024748" + // - TopicName (string) "GH" - "00000000" // ProduceReqPartition array length = 0 - -func TestKafkaOnDataSimpleHeaderMinimalPolicy(t *testing.T) { - s := setUpKafkaSuite(t) - s.ins.CheckInsertPolicyText(t, "1", []string{` - endpoint_ips: "face::feed" - endpoint_ips: "1.1.1.1" - endpoint_id: 2000 - ingress_per_port_policies: < - port: 80 - > - `}) - - data := hexData(t, testMessage1, testMessage2, testMessage3) - - conn := s.ins.CheckNewConnectionOK(t, "kafka", true, 1000, 2000, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1") - conn.CheckOnDataOK(t, false, false, &data, []byte{}, proxylib.PASS, 4+42) -} - -func TestKafkaOnDataInvalidMessage(t *testing.T) { - s := setUpKafkaSuite(t) - s.ins.CheckInsertPolicyText(t, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2000 - ingress_per_port_policies: < - port: 80 - > - `}) - - data := hexData(t, testMessage1, testMessage2, testMessage3Fail) - - conn := s.ins.CheckNewConnectionOK(t, "kafka", true, 1000, 2000, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1") - conn.CheckOnDataOK(t, false, false, &data, []byte{}, proxylib.ERROR, int(proxylib.ERROR_INVALID_FRAME_TYPE)) - s.checkAccessLogs(t, 0, 1) -} - -func TestKafkaOnDataSimpleHeaderSimplePolicy(t *testing.T) { - s := setUpKafkaSuite(t) - s.ins.CheckInsertPolicyText(t, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2000 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 1000 - l7_proto: "kafka" - > - > - `}) - - data := hexData(t, testMessage1, testMessage2, testMessage3) - - conn := s.ins.CheckNewConnectionOK(t, "kafka", true, 1000, 2000, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1") - conn.CheckOnDataOK(t, false, false, &data, []byte{}, proxylib.PASS, 4+42) - s.checkAccessLogs(t, 1, 0) -} - -func TestKafkaOnDataSimpleHeaderWithPolicyDrop(t *testing.T) { - s := setUpKafkaSuite(t) - s.ins.CheckInsertPolicyText(t, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2000 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 1000 - l7_proto: "kafka" - kafka_rules: < - kafka_rules: < - api_version: -1 - topic: "EF" - > - > - > - > - `}) - - data := hexData(t, testMessage1, testMessage2, testMessage3, "0000") - - conn := s.ins.CheckNewConnectionOK(t, "kafka", true, 1000, 2000, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1") - conn.CheckOnDataOK(t, false, false, &data, - // Error response: - []byte{0x0, 0x0, 0x0, 0x1c, // length - 0x0, 0x1, 0x0, 0x1, // Correlation ID (65537) - 0x0, 0x0, 0x0, 0x2, // 2 topics - 0x0, 0x2, 0x45, 0x46, // name: "EF" - 0x0, 0x0, 0x0, 0x0, // 0 partitions - 0x0, 0x2, 0x47, 0x48, // name: "GH" - 0x0, 0x0, 0x0, 0x0, // 0 partitions - 0x0, 0x0, 0x0, 0x0}, // ThrottleTime - proxylib.DROP, 4+42, - proxylib.MORE, 6) - s.checkAccessLogs(t, 0, 1) -} - -func TestKafkaOnDataSimpleHeaderWithPolicyAllow(t *testing.T) { - s := setUpKafkaSuite(t) - s.ins.CheckInsertPolicyText(t, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2000 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 1000 - l7_proto: "kafka" - kafka_rules: < - kafka_rules: < - api_version: -1 - topic: "EF" - > - kafka_rules: < - api_version: -1 - topic: "GH" - > - > - > - > - `}) - - data := hexData(t, testMessage1, testMessage2, testMessage3) - - conn := s.ins.CheckNewConnectionOK(t, "kafka", true, 1000, 2000, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1") - conn.CheckOnDataOK(t, false, false, &data, []byte{}, proxylib.PASS, 4+42) - s.checkAccessLogs(t, 1, 0) -} - -func TestKafkaOnDataSimpleHeaderWithClientIDAllow(t *testing.T) { - s := setUpKafkaSuite(t) - s.ins.CheckInsertPolicyText(t, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2000 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 1000 - l7_proto: "kafka" - kafka_rules: < - kafka_rules: < - api_version: -1 - topic: "EF" - client_id: "ABC" - > - kafka_rules: < - api_version: -1 - topic: "GH" - > - > - > - > - `}) - - data := hexData(t, testMessage1, testMessage2, testMessage3) - - conn := s.ins.CheckNewConnectionOK(t, "kafka", true, 1000, 2000, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1") - conn.CheckOnDataOK(t, false, false, &data, []byte{}, proxylib.PASS, 4+42) - s.checkAccessLogs(t, 1, 0) -} - -func TestKafkaOnDataSimpleHeaderWithClientID(t *testing.T) { - s := setUpKafkaSuite(t) - s.ins.CheckInsertPolicyText(t, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2000 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 1000 - l7_proto: "kafka" - kafka_rules: < - kafka_rules: < - api_version: -1 - client_id: "ABC" - > - > - > - > - `}) - - data := hexData(t, testMessage1, testMessage2, testMessage3) - - conn := s.ins.CheckNewConnectionOK(t, "kafka", true, 1000, 2000, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1") - conn.CheckOnDataOK(t, false, false, &data, []byte{}, proxylib.PASS, 4+42) - s.checkAccessLogs(t, 1, 0) -} - -func TestKafkaOnDataSimpleHeaderWithApiKeys(t *testing.T) { - s := setUpKafkaSuite(t) - s.ins.CheckInsertPolicyText(t, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2000 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 1000 - l7_proto: "kafka" - kafka_rules: < - kafka_rules: < - api_version: -1 - api_keys: 0 - client_id: "ABC" - > - > - > - > - `}) - - data := hexData(t, testMessage1, testMessage2, testMessage3) - - conn := s.ins.CheckNewConnectionOK(t, "kafka", true, 1000, 2000, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1") - conn.CheckOnDataOK(t, false, false, &data, []byte{}, proxylib.PASS, 4+42) - s.checkAccessLogs(t, 1, 0) -} - -func TestKafkaOnDataSimpleHeaderWithApiKeysMismatch(t *testing.T) { - s := setUpKafkaSuite(t) - s.ins.CheckInsertPolicyText(t, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2000 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 1000 - l7_proto: "kafka" - kafka_rules: < - kafka_rules: < - api_version: -1 - api_keys: 1 - client_id: "ABC" - > - > - > - > - `}) - - data := hexData(t, testMessage1, testMessage2, testMessage3) - - conn := s.ins.CheckNewConnectionOK(t, "kafka", true, 1000, 2000, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1") - conn.CheckOnDataOK(t, false, false, &data, - // Error response: - []byte{0x0, 0x0, 0x0, 0x1c, // length - 0x0, 0x1, 0x0, 0x1, // Correlation ID (65537) - 0x0, 0x0, 0x0, 0x2, // 2 topics - 0x0, 0x2, 0x45, 0x46, // name: "EF" - 0x0, 0x0, 0x0, 0x0, // 0 partitions - 0x0, 0x2, 0x47, 0x48, // name: "GH" - 0x0, 0x0, 0x0, 0x0, // 0 partitions - 0x0, 0x0, 0x0, 0x0}, // ThrottleTime - proxylib.DROP, 4+42) - s.checkAccessLogs(t, 0, 1) -} - -func TestKafkaOnDataSimpleHeaderWithApiVersion(t *testing.T) { - s := setUpKafkaSuite(t) - s.ins.CheckInsertPolicyText(t, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2000 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 1000 - l7_proto: "kafka" - kafka_rules: < - kafka_rules: < - api_version: 3 - client_id: "ABC" - > - > - > - > - `}) - - data := hexData(t, testMessage1, testMessage2, testMessage3) - - conn := s.ins.CheckNewConnectionOK(t, "kafka", true, 1000, 2000, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1") - conn.CheckOnDataOK(t, false, false, &data, []byte{}, proxylib.PASS, 4+42) - s.checkAccessLogs(t, 1, 0) -} - -func TestKafkaOnDataSimpleHeaderWithApiVersionMismatch(t *testing.T) { - s := setUpKafkaSuite(t) - s.ins.CheckInsertPolicyText(t, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2000 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 1000 - l7_proto: "kafka" - kafka_rules: < - kafka_rules: < - api_version: 0 - client_id: "ABC" - > - > - > - > - `}) - - data := hexData(t, testMessage1, testMessage2, testMessage3) - - conn := s.ins.CheckNewConnectionOK(t, "kafka", true, 1000, 2000, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1") - conn.CheckOnDataOK(t, false, false, &data, - // Error response: - []byte{0x0, 0x0, 0x0, 0x1c, // length - 0x0, 0x1, 0x0, 0x1, // Correlation ID (65537) - 0x0, 0x0, 0x0, 0x2, // 2 topics - 0x0, 0x2, 0x45, 0x46, // name: "EF" - 0x0, 0x0, 0x0, 0x0, // 0 partitions - 0x0, 0x2, 0x47, 0x48, // name: "GH" - 0x0, 0x0, 0x0, 0x0, // 0 partitions - 0x0, 0x0, 0x0, 0x0}, // ThrottleTime - proxylib.DROP, 4+42) - s.checkAccessLogs(t, 0, 1) -} - -func TestKafkaOnDataSimpleHeaderWithClientIDDeny(t *testing.T) { - s := setUpKafkaSuite(t) - s.ins.CheckInsertPolicyText(t, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2000 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 1000 - l7_proto: "kafka" - kafka_rules: < - kafka_rules: < - api_version: -1 - topic: "EF" - client_id: "ABCD" - > - kafka_rules: < - api_version: -1 - topic: "GH" - > - > - > - > - `}) - - data := hexData(t, testMessage1, testMessage2, testMessage3) - - conn := s.ins.CheckNewConnectionOK(t, "kafka", true, 1000, 2000, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1") - conn.CheckOnDataOK(t, false, false, &data, - // Error response: - []byte{0x0, 0x0, 0x0, 0x1c, // length - 0x0, 0x1, 0x0, 0x1, // Correlation ID (65537) - 0x0, 0x0, 0x0, 0x2, // 2 topics - 0x0, 0x2, 0x45, 0x46, // name: "EF" - 0x0, 0x0, 0x0, 0x0, // 0 partitions - 0x0, 0x2, 0x47, 0x48, // name: "GH" - 0x0, 0x0, 0x0, 0x0, // 0 partitions - 0x0, 0x0, 0x0, 0x0}, // ThrottleTime - proxylib.DROP, 4+42) - s.checkAccessLogs(t, 0, 1) -} - -func TestKafkaOnDataResponse(t *testing.T) { - s := setUpKafkaSuite(t) - data := [][]byte{ - {0x0, 0x0, 0x0, 0x1c}, // length - {0x0, 0x1, 0x0, 0x1}, // Correlation ID (65537) - {0x0, 0x0, 0x0, 0x2}, // 2 topics - {0x0, 0x2, 0x45, 0x46, // name: "EF" - 0x0, 0x0, 0x0, 0x0}, // 0 partitions - {0x0, 0x2, 0x47, 0x48, // name: "GH" - 0x0, 0x0, 0x0, 0x0}, // 0 partitions - {0x0, 0x0, 0x0, 0x0}, // ThrottleTime - } - - conn := s.ins.CheckNewConnectionOK(t, "kafka", true, 1000, 2000, "1.1.1.1:34567", "10.0.0.2:80", "") - conn.CheckOnDataOK(t, true, false, &data, []byte{}, proxylib.PASS, 4+28) - s.checkAccessLogs(t, 1, 0) -} diff --git a/proxylib/libcilium.h b/proxylib/libcilium.h deleted file mode 100644 index 181b3466b..000000000 --- a/proxylib/libcilium.h +++ /dev/null @@ -1,135 +0,0 @@ -/* Code generated by cmd/cgo; DO NOT EDIT. */ - -/* package github.com/cilium/proxy/proxylib */ - - -#line 1 "cgo-builtin-export-prolog" - -#include - -#ifndef GO_CGO_EXPORT_PROLOGUE_H -#define GO_CGO_EXPORT_PROLOGUE_H - -#ifndef GO_CGO_GOSTRING_TYPEDEF -typedef struct { const char *p; ptrdiff_t n; } _GoString_; -extern size_t _GoStringLen(_GoString_ s); -extern const char *_GoStringPtr(_GoString_ s); -#endif - -#endif - -/* Start of preamble from import "C" comments. */ - - -#line 9 "proxylib.go" - -#include "types.h" - -#line 1 "cgo-generated-wrapper" - - -/* End of preamble from import "C" comments. */ - - -/* Start of boilerplate cgo prologue. */ -#line 1 "cgo-gcc-export-header-prolog" - -#ifndef GO_CGO_PROLOGUE_H -#define GO_CGO_PROLOGUE_H - -typedef signed char GoInt8; -typedef unsigned char GoUint8; -typedef short GoInt16; -typedef unsigned short GoUint16; -typedef int GoInt32; -typedef unsigned int GoUint32; -typedef long long GoInt64; -typedef unsigned long long GoUint64; -typedef GoInt64 GoInt; -typedef GoUint64 GoUint; -typedef size_t GoUintptr; -typedef float GoFloat32; -typedef double GoFloat64; -#ifdef _MSC_VER -#if !defined(__cplusplus) || _MSVC_LANG <= 201402L -#include -typedef _Fcomplex GoComplex64; -typedef _Dcomplex GoComplex128; -#else -#include -typedef std::complex GoComplex64; -typedef std::complex GoComplex128; -#endif -#else -typedef float _Complex GoComplex64; -typedef double _Complex GoComplex128; -#endif - -/* - static assertion to make sure the file is being used on architecture - at least with matching size of GoInt. -*/ -typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1]; - -#ifndef GO_CGO_GOSTRING_TYPEDEF -typedef _GoString_ GoString; -#endif -typedef void *GoMap; -typedef void *GoChan; -typedef struct { void *t; void *v; } GoInterface; -typedef struct { void *data; GoInt len; GoInt cap; } GoSlice; - -#endif - -/* End of boilerplate cgo prologue. */ - -#ifdef __cplusplus -extern "C" { -#endif - - -// OnNewConnection is used to register a new connection of protocol 'proto'. -// Note that the 'origBuf' and replyBuf' type '*[]byte' corresponds to 'InjectBuf' type, but due to -// cgo export restrictions we can't use the go type in the prototype. -// -extern FilterResult OnNewConnection(GoUint64 instanceId, GoString proto, GoUint64 connectionId, GoUint8 ingress, GoUint32 srcId, GoUint32 dstId, GoString srcAddr, GoString dstAddr, GoString policyName, GoSlice* origBuf, GoSlice* replyBuf); - -// Each connection is assumed to be called from a single thread, so accessing connection metadata -// does not need protection. -// -// OnData gets all the unparsed data the datapath has received so far. The data is provided to the parser -// associated with the connection, and the parser is expected to find if the data frame contains enough data -// to make a PASS/DROP decision for the whole data frame. Note that the whole data frame need not be received, -// if the decision including the length of the data frame in bytes can be determined based on the beginning of -// the data frame only (e.g., headers including the length of the data frame). The parser returns a decision -// with the number of bytes on which the decision applies. If more data is available, then the parser will be -// called again with the remaining data. Parser needs to return MORE if a decision can't be made with -// the available data, including the minimum number of additional bytes that is needed before the parser is -// called again. -// -// The parser can also inject at arbitrary points in the data stream. This is indecated by an INJECT operation -// with the number of bytes to be injected. The actual bytes to be injected are provided via an Inject() -// callback prior to returning the INJECT operation. The Inject() callback operates on a limited size buffer -// provided by the datapath, and multiple INJECT operations may be needed to inject large amounts of data. -// Since we get the data on one direction at a time, any frames to be injected in the reverse direction -// are placed in the reverse direction buffer, from where the datapath injects the data before calling -// us again for the reverse direction input. -// -extern FilterResult OnData(GoUint64 connectionId, GoUint8 reply, GoUint8 endStream, GoSlice* data, GoSlice* filterOps); - -// Make this more general connection event callback -// -extern void Close(GoUint64 connectionId); - -// OpenModule is called before any other APIs. -// Called concurrently by different filter instances. -// Returns a library instance ID that must be passed to all other API calls. -// Calls with the same parameters will return the same instance. -// Zero return value indicates an error. -// -extern GoUint64 OpenModule(GoSlice params, GoUint8 debug); -extern void CloseModule(GoUint64 id); - -#ifdef __cplusplus -} -#endif diff --git a/proxylib/libcilium.so b/proxylib/libcilium.so new file mode 100644 index 000000000..a1d5f4cb1 Binary files /dev/null and b/proxylib/libcilium.so differ diff --git a/proxylib/libcilium/helpers_test.go b/proxylib/libcilium/helpers_test.go deleted file mode 100644 index 7370ff5c9..000000000 --- a/proxylib/libcilium/helpers_test.go +++ /dev/null @@ -1,133 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package libcilium - -// These helpers must be defined in the main package so that the exported shared library functions -// can be called, as the C types used in the prototypes are only available from within the main -// package. -// -// These can not be defined in '_test.go' files, as Go test is not compatible with Cgo. - -import ( - "testing" - - . "github.com/cilium/proxy/proxylib/proxylib" -) - -func numConnections() int { - mutex.Lock() - defer mutex.Unlock() - return len(connections) -} - -func checkConnectionCount(t *testing.T, expConns int) { - t.Helper() - nConns := numConnections() - if nConns != expConns { - t.Errorf("Number of connections does not match (have %d, but should be %d)", nConns, expConns) - } -} - -func checkConnections(t *testing.T, res, expected FilterResult, expConns int) { - t.Helper() - if res != expected { - t.Errorf("OnNewConnection(): Invalid result, have %s, expected %s", res.Error(), expected.Error()) - } - checkConnectionCount(t, expConns) -} - -func CheckOnNewConnection(t *testing.T, instanceId uint64, proto string, connectionId uint64, ingress bool, srcId, dstId uint32, srcAddr, dstAddr, policyName string, bufSize int, expResult FilterResult, expNumConnections int) *byte { - t.Helper() - origBuf := make([]byte, 0, bufSize) - replyBuf := make([]byte, 1, bufSize) - replyBufAddr := &replyBuf[0] - replyBuf = replyBuf[:0] // make the buffer empty again - - res := FilterResult(OnNewConnection(instanceId, proto, connectionId, ingress, srcId, dstId, srcAddr, dstAddr, policyName, &origBuf, &replyBuf)) - checkConnections(t, res, expResult, expNumConnections) - - return replyBufAddr -} - -func CheckClose(t *testing.T, connectionId uint64, replyBufAddr *byte, n int) { - t.Helper() - checkConnectionCount(t, n) - - // Find the connection - mutex.Lock() - connection, ok := connections[connectionId] - mutex.Unlock() - if !ok { - t.Errorf("OnData(): Connection %d not found!", connectionId) - } else if replyBufAddr != nil && len(*connection.ReplyBuf) > 0 && replyBufAddr != &(*connection.ReplyBuf)[0] { - t.Error("OnData(): Reply injection buffer reallocated while it must not be!") - } - - Close(connectionId) - - checkConnectionCount(t, n-1) -} - -type ExpFilterOp struct { - op OpType - n_bytes int -} - -func checkOps(ops [][2]int64, exp []ExpFilterOp) bool { - if len(ops) != len(exp) { - return false - } else { - for i, op := range ops { - if op[0] != int64(exp[i].op) || op[1] != int64(exp[i].n_bytes) { - return false - } - } - } - return true -} - -func checkBuf(t *testing.T, buf InjectBuf, expected string) { - t.Helper() - if len(*buf) < len(expected) { - t.Log("Inject buffer too small, data truncated") - expected = expected[:len(*buf)] // truncate to buffer length - } - if string(*buf) != expected { - t.Errorf("OnData(): Expected inject buffer to be %s, buf have: %s", expected, *buf) - } -} - -func checkOnData(t *testing.T, res, expected FilterResult, ops [][2]int64, expOps []ExpFilterOp) { - t.Helper() - if res != expected { - t.Errorf("OnData(): Invalid result, have %s, expected %s", res.Error(), expected.Error()) - } - if !checkOps(ops, expOps) { - t.Errorf("OnData(): Unexpected filter operations: %v, expected %v", ops, expOps) - } -} - -func CheckOnData(t *testing.T, connectionId uint64, reply, endStream bool, data *[][]byte, expOps []ExpFilterOp, expResult FilterResult, expReplyBuf string) { - t.Helper() - - // Find the connection - mutex.Lock() - connection, ok := connections[connectionId] - mutex.Unlock() - if !ok && expResult != UNKNOWN_CONNECTION { - t.Errorf("OnData(): Connection %d not found!", connectionId) - } - - ops := make([][2]int64, 0, 1+len(expOps)*2) - - res := FilterResult(OnData(connectionId, reply, endStream, data, &ops)) - - checkOnData(t, res, expResult, ops, expOps) - - if ok { - replyBuf := connection.ReplyBuf - checkBuf(t, replyBuf, expReplyBuf) - *replyBuf = (*replyBuf)[:0] // make empty again - } -} diff --git a/proxylib/libcilium/proxylib.go b/proxylib/libcilium/proxylib.go deleted file mode 100644 index 6ee1e29b9..000000000 --- a/proxylib/libcilium/proxylib.go +++ /dev/null @@ -1,102 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package libcilium - -import ( - "sync" - - "github.com/sirupsen/logrus" - - "github.com/cilium/proxy/proxylib/accesslog" - _ "github.com/cilium/proxy/proxylib/cassandra" - _ "github.com/cilium/proxy/proxylib/kafka" - _ "github.com/cilium/proxy/proxylib/memcached" - "github.com/cilium/proxy/proxylib/npds" - "github.com/cilium/proxy/proxylib/proxylib" - _ "github.com/cilium/proxy/proxylib/r2d2" - _ "github.com/cilium/proxy/proxylib/testparsers" -) - -var ( - // mutex protects connections - mutex sync.RWMutex - // Key uint64 is a connection ID allocated by Envoy, practically a monotonically increasing number - connections map[uint64]*proxylib.Connection = make(map[uint64]*proxylib.Connection) -) - -// Copy value string from C-memory to Go-memory. -// Go strings are immutable, but byte slices are not. Converting to a byte slice will thus -// copy the memory. -func strcpy(str string) string { - return string(([]byte(str))[0:]) -} - -func OnNewConnection(instanceId uint64, proto string, connectionId uint64, ingress bool, srcId, dstId uint32, srcAddr, dstAddr, policyName string, origBuf, replyBuf *[]byte) proxylib.FilterResult { - instance := proxylib.FindInstance(instanceId) - if instance == nil { - return proxylib.INVALID_INSTANCE - } - - err, conn := proxylib.NewConnection(instance, strcpy(proto), connectionId, ingress, srcId, dstId, strcpy(srcAddr), strcpy(dstAddr), strcpy(policyName), origBuf, replyBuf) - if err == nil { - mutex.Lock() - connections[connectionId] = conn - mutex.Unlock() - return proxylib.OK - } - if res, ok := err.(proxylib.FilterResult); ok { - return res - } - return proxylib.UNKNOWN_ERROR -} - -func OnData(connectionId uint64, reply, endStream bool, data *[][]byte, filterOps *[][2]int64) proxylib.FilterResult { - // Find the connection - mutex.RLock() - connection, ok := connections[connectionId] - mutex.RUnlock() - if !ok { - return proxylib.UNKNOWN_CONNECTION - } - - return connection.OnData(reply, endStream, data, filterOps) -} - -func Close(connectionId uint64) { - mutex.Lock() - delete(connections, connectionId) - mutex.Unlock() -} - -func OpenModule(params [][2]string, debug bool) uint64 { - var accessLogPath, xdsPath, nodeID string - for i := range params { - key := params[i][0] - value := strcpy(params[i][1]) - - switch key { - case "access-log-path": - accessLogPath = value - case "xds-path": - xdsPath = value - case "node-id": - nodeID = value - default: - return 0 - } - } - - if debug { - mutex.Lock() - logrus.SetLevel(logrus.DebugLevel) - mutex.Unlock() - } - // Copy strings from C-memory to Go-memory so that the string remains valid - // also after this function returns - return proxylib.OpenInstance(nodeID, xdsPath, npds.NewClient, accessLogPath, accesslog.NewClient) -} - -func CloseModule(id uint64) { - proxylib.CloseInstance(id) -} diff --git a/proxylib/libcilium/proxylib_memcached_test.go b/proxylib/libcilium/proxylib_memcached_test.go deleted file mode 100644 index a1a2a1b22..000000000 --- a/proxylib/libcilium/proxylib_memcached_test.go +++ /dev/null @@ -1,718 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package libcilium - -import ( - "fmt" - "testing" - - _ "github.com/cilium/proxy/proxylib/memcached" - binarymemcache "github.com/cilium/proxy/proxylib/memcached/binary" - textmemcache "github.com/cilium/proxy/proxylib/memcached/text" - "github.com/cilium/proxy/proxylib/proxylib" - "github.com/cilium/proxy/proxylib/test" -) - -var setHelloText = []byte("set key 0 0 5\r\nhello\r\n") - -var getKeysText = []byte("get key1 key2 key3\r\n") -var gatKeysText = []byte("gat 5 key1 key2 key3\r\n") -var getResponse = []byte( - "VALUE key3 0 4\r\n" + - "xDDD\r\n" + - "VALUE key4 0 3\r\n" + - "xDD\r\n" + - "END\r\n") - -var deleteText = []byte("delete key\r\n") -var incrText = []byte("incr key 5\r\n") -var touchText = []byte("touch key 55\r\n") -var slabsText = []byte("slabs automove 1\r\n") -var okText = []byte("OK\r\n") -var lruCrawlerText = []byte("lru_crawler metadump all\r\n") -var statsText = []byte("stats\r\n") -var flushAllText = []byte("flush_all 15\r\n") -var watchText = []byte("watch mutations\r\n") - -var watchReply = []byte( - "OK\r\n" + - "ts=1538135970.404892 gid=5 type=item_store key=key3 status=stored cmd=set ttl=500 clsid=1\r\n" + - "ts=1538135970.404898 gid=6 type=item_store key=key4 status=stored cmd=set ttl=500 clsid=1\r\n" + - "ts=1538135974.340708 gid=7 type=item_store key=key3 status=stored cmd=set ttl=500 clsid=1\r\n" + - "ts=1538135974.340714 gid=8 type=item_store key=key4 status=stored cmd=set ttl=500 clsid=1\r\n" + - "ts=1538135976.436863 gid=9 type=item_store key=key3 status=stored cmd=set ttl=500 clsid=1\r\n") - -var lruCrawlerResponse = []byte( - "key=key3 exp=1538047402 la=1538046902 cas=1 fetch=no cls=1 size=67\r\n" + - "key=key4 exp=1538047402 la=1538046902 cas=2 fetch=no cls=1 size=66\r\n" + - "END\r\n") - -var statsResponse = []byte( - "STAT evictions 0\r\n" + - "STAT reclaimed 2\r\n" + - "STAT crawler_reclaimed\r\n" + - "STAT crawler_items_checked 18\r\n" + - "STAT lrutail_reflocked 0\r\n" + - "STAT moves_to_cold 6\r\n" + - "STAT moves_to_warm 0\r\n" + - "STAT moves_within_lru 0\r\n" + - "STAT direct_reclaims 0\r\n" + - "STAT lru_bumps_dropped 0\r\n" + - "END\r\n") - -var notFound = []byte("NOT_FOUND\r\n") - -var stored = []byte("STORED\r\n") - -// binary packets -var getHello = []byte{ - 128, 0, 0, 5, - 0, 0, 0, 0, - 0, 0, 0, 5, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 'H', 'e', 'l', 'l', - 'o', -} - -var getHelloResp = []byte{ - 129, 0, 0, 0, - 4, 0, 0, 0, - 0, 0, 0, 9, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 'W', 'o', 'r', 'l', - 'd', -} - -var setHello = []byte{ - 128, 1, 0, 5, - 8, 0, 0, 0, - 0, 0, 0, 18, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 'H', 'e', 'l', 'l', - 'o', 'W', 'o', 'r', - 'l', 'd', -} - -func TestMemcache(t *testing.T) { - for _, tc := range append(textTestCases, binaryTestCases...) { - t.Run(tc.name, func(t *testing.T) { - - logServer := test.StartAccessLogServer("access_log.sock", 10) - defer logServer.Close() - - mod := OpenModule([][2]string{{"access-log-path", logServer.Path}}, false) - if mod == 0 { - t.Errorf("OpenModule() with access log path %s failed", logServer.Path) - } else { - defer CloseModule(mod) - } - - insertPolicyText(t, mod, "1", []string{fmt.Sprintf(` - endpoint_ips: "1.1.1.1" - endpoint_id: 2 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 1 - remote_policies: 3 - remote_policies: 4 - l7_proto: "memcache" - l7_rules: < - l7_allow_rules: < -%s - > - > - > - > - `, tc.policy)}) - - buf := CheckOnNewConnection(t, mod, "memcache", 1, true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1", - 30, proxylib.OK, 1) - - tc.onDataChecks(t) - - CheckClose(t, 1, buf, 1) - }) - } -} - -type testCase struct { - name string - policy string - onDataChecks func(*testing.T) -} - -var textTestCases = []testCase{ - { - "text set pass", - ` rule: < - key: "keyExact" - value: "" - > - rule: < - key: "command" - value: "set" - > - `, - func(t *testing.T) { - CheckOnData(t, 1, false, false, &[][]byte{setHelloText}, []ExpFilterOp{ - {proxylib.PASS, len(setHelloText)}, {proxylib.MORE, 2}, - }, proxylib.OK, "") - - CheckOnData(t, 1, true, false, &[][]byte{stored}, []ExpFilterOp{ - {proxylib.PASS, len(stored)}, - }, proxylib.OK, "") - }, - }, - { - "text set drop", - ` rule: < - key: "keyExact" - value: "trolo" - > - rule: < - key: "command" - value: "set" - > - `, - func(t *testing.T) { - CheckOnData(t, 1, false, false, &[][]byte{setHelloText}, []ExpFilterOp{ - {proxylib.DROP, len(setHelloText)}, {proxylib.MORE, 2}, - }, proxylib.OK, string(textmemcache.DeniedMsg)) - }, - }, - { - "text get pass", - ` rule: < - key: "keyExact" - value: "" - > - rule: < - key: "command" - value: "get" - > - `, - func(t *testing.T) { - CheckOnData(t, 1, false, false, &[][]byte{getKeysText, getKeysText}, []ExpFilterOp{ - {proxylib.PASS, len(getKeysText)}, {proxylib.PASS, len(getKeysText)}, {proxylib.MORE, 2}, - }, proxylib.OK, "") - CheckOnData(t, 1, true, false, &[][]byte{getResponse, getResponse}, []ExpFilterOp{ - {proxylib.PASS, len(getResponse)}, {proxylib.PASS, len(getResponse)}, - }, proxylib.OK, "") - }, - }, - { - "text get more", - ` rule: < - key: "keyExact" - value: "" - > - rule: < - key: "command" - value: "get" - > - `, - func(t *testing.T) { - CheckOnData(t, 1, false, false, &[][]byte{getResponse[:5]}, []ExpFilterOp{ - {proxylib.MORE, 2}, - }, proxylib.OK, "") - }, - }, - { - "text get drop", - ` rule: < - key: "keyExact" - value: "" - > - rule: < - key: "command" - value: "set" - > - `, - func(t *testing.T) { - CheckOnData(t, 1, false, false, &[][]byte{getKeysText}, []ExpFilterOp{ - {proxylib.DROP, len(getKeysText)}, {proxylib.MORE, 2}, - }, proxylib.OK, string(textmemcache.DeniedMsg)) - }, - }, - { - "text gat pass", - ` rule: < - key: "keyExact" - value: "" - > - rule: < - key: "command" - value: "gat" - > - `, - func(t *testing.T) { - CheckOnData(t, 1, false, false, &[][]byte{gatKeysText, gatKeysText}, []ExpFilterOp{ - {proxylib.PASS, len(gatKeysText)}, {proxylib.PASS, len(gatKeysText)}, {proxylib.MORE, 2}, - }, proxylib.OK, "") - CheckOnData(t, 1, true, false, &[][]byte{getResponse, getResponse}, []ExpFilterOp{ - {proxylib.PASS, len(getResponse)}, {proxylib.PASS, len(getResponse)}, - }, proxylib.OK, "") - }, - }, - { - "text gat more", - ` rule: < - key: "keyExact" - value: "" - > - rule: < - key: "command" - value: "gat" - > - `, - func(t *testing.T) { - CheckOnData(t, 1, false, false, &[][]byte{getResponse[:5]}, []ExpFilterOp{ - {proxylib.MORE, 2}, - }, proxylib.OK, "") - }, - }, - { - "text gat drop", - ` rule: < - key: "keyExact" - value: "" - > - rule: < - key: "command" - value: "set" - > - `, - func(t *testing.T) { - CheckOnData(t, 1, false, false, &[][]byte{gatKeysText}, []ExpFilterOp{ - {proxylib.DROP, len(gatKeysText)}, {proxylib.MORE, 2}, - }, proxylib.OK, string(textmemcache.DeniedMsg)) - }, - }, - { - "text delete pass", - ` rule: < - key: "keyExact" - value: "" - > - rule: < - key: "command" - value: "delete" - > - `, - func(t *testing.T) { - - CheckOnData(t, 1, false, false, &[][]byte{deleteText}, []ExpFilterOp{ - {proxylib.PASS, len(deleteText)}, {proxylib.MORE, 2}, - }, proxylib.OK, "") - - CheckOnData(t, 1, true, false, &[][]byte{notFound}, []ExpFilterOp{ - {proxylib.PASS, len(notFound)}, - }, proxylib.OK, "") - }, - }, - { - "text delete drop", - ` rule: < - key: "keyExact" - value: "" - > - rule: < - key: "command" - value: "set" - > - `, - func(t *testing.T) { - CheckOnData(t, 1, false, false, &[][]byte{deleteText}, []ExpFilterOp{ - {proxylib.DROP, len(deleteText)}, {proxylib.MORE, 2}, - }, proxylib.OK, string(textmemcache.DeniedMsg)) - }, - }, - { - "text incr pass", - ` rule: < - key: "keyExact" - value: "" - > - rule: < - key: "command" - value: "incr" - > - `, - func(t *testing.T) { - - CheckOnData(t, 1, false, false, &[][]byte{incrText}, []ExpFilterOp{ - {proxylib.PASS, len(incrText)}, {proxylib.MORE, 2}, - }, proxylib.OK, "") - - CheckOnData(t, 1, true, false, &[][]byte{notFound}, []ExpFilterOp{ - {proxylib.PASS, len(notFound)}, - }, proxylib.OK, "") - }, - }, - { - "text incr drop", - ` rule: < - key: "keyExact" - value: "otherKey" - > - rule: < - key: "command" - value: "incr" - > - `, - func(t *testing.T) { - CheckOnData(t, 1, false, false, &[][]byte{incrText}, []ExpFilterOp{ - {proxylib.DROP, len(incrText)}, {proxylib.MORE, 2}, - }, proxylib.OK, string(textmemcache.DeniedMsg)) - }, - }, - { - "text touch pass", - ` rule: < - key: "keyExact" - value: "key" - > - rule: < - key: "command" - value: "touch" - > - `, - func(t *testing.T) { - - CheckOnData(t, 1, false, false, &[][]byte{touchText}, []ExpFilterOp{ - {proxylib.PASS, len(touchText)}, {proxylib.MORE, 2}, - }, proxylib.OK, "") - - CheckOnData(t, 1, true, false, &[][]byte{notFound}, []ExpFilterOp{ - {proxylib.PASS, len(notFound)}, - }, proxylib.OK, "") - }, - }, - { - "text touch drop", - ` rule: < - key: "keyExact" - value: "otherKey" - > - rule: < - key: "command" - value: "touch" - > - `, - func(t *testing.T) { - CheckOnData(t, 1, false, false, &[][]byte{touchText}, []ExpFilterOp{ - {proxylib.DROP, len(touchText)}, {proxylib.MORE, 2}, - }, proxylib.OK, string(textmemcache.DeniedMsg)) - }, - }, - { - "text slabs pass", - ` rule: < - key: "command" - value: "slabs" - > - `, - func(t *testing.T) { - - CheckOnData(t, 1, false, false, &[][]byte{slabsText}, []ExpFilterOp{ - {proxylib.PASS, len(slabsText)}, {proxylib.MORE, 2}, - }, proxylib.OK, "") - - CheckOnData(t, 1, true, false, &[][]byte{okText}, []ExpFilterOp{ - {proxylib.PASS, len(okText)}, - }, proxylib.OK, "") - }, - }, - { - "text slabs drop", - ` rule: < - key: "keyExact" - value: "otherKey" - > - rule: < - key: "command" - value: "touch" - > - `, - func(t *testing.T) { - CheckOnData(t, 1, false, false, &[][]byte{slabsText}, []ExpFilterOp{ - {proxylib.DROP, len(slabsText)}, {proxylib.MORE, 2}, - }, proxylib.OK, string(textmemcache.DeniedMsg)) - }, - }, - { - "text lru_crawler response req more and pass", - ` rule: < - key: "command" - value: "lru_crawler" - > - `, - func(t *testing.T) { - CheckOnData(t, 1, false, false, &[][]byte{lruCrawlerText}, []ExpFilterOp{ - {proxylib.PASS, len(lruCrawlerText)}, {proxylib.MORE, 2}, - }, proxylib.OK, "") - - CheckOnData(t, 1, true, false, &[][]byte{lruCrawlerResponse[:5]}, []ExpFilterOp{ - {proxylib.MORE, 2}, - }, proxylib.OK, "") - CheckOnData(t, 1, true, false, &[][]byte{lruCrawlerResponse}, []ExpFilterOp{ - {proxylib.PASS, len(lruCrawlerResponse)}, - }, proxylib.OK, "") - }, - }, - { - "text stats response req more and pass", - ` rule: < - key: "command" - value: "stats" - > - `, - func(t *testing.T) { - - CheckOnData(t, 1, false, false, &[][]byte{statsText}, []ExpFilterOp{ - {proxylib.PASS, len(statsText)}, {proxylib.MORE, 2}, - }, proxylib.OK, "") - - CheckOnData(t, 1, true, false, &[][]byte{statsResponse[:5]}, []ExpFilterOp{ - {proxylib.MORE, 2}, - }, proxylib.OK, "") - CheckOnData(t, 1, true, false, &[][]byte{statsResponse}, []ExpFilterOp{ - {proxylib.PASS, len(statsResponse)}, - }, proxylib.OK, "") - }, - }, - { - "text flush_all pass", - ` rule: < - key: "command" - value: "flush_all" - > - `, - func(t *testing.T) { - - CheckOnData(t, 1, false, false, &[][]byte{flushAllText}, []ExpFilterOp{ - {proxylib.PASS, len(flushAllText)}, {proxylib.MORE, 2}, - }, proxylib.OK, "") - - }, - }, - { - "text flush_all denied", - ` rule: < - key: "command" - value: "get" - > - `, - func(t *testing.T) { - - CheckOnData(t, 1, false, false, &[][]byte{flushAllText}, []ExpFilterOp{ - {proxylib.DROP, len(flushAllText)}, {proxylib.MORE, 2}, - }, proxylib.OK, string(textmemcache.DeniedMsg)) - - }, - }, - { - "text watch passed", - ` rule: < - key: "command" - value: "watch" - > - `, - func(t *testing.T) { - - CheckOnData(t, 1, false, false, &[][]byte{watchText}, []ExpFilterOp{ - {proxylib.PASS, len(watchText)}, {proxylib.MORE, 2}, - }, proxylib.OK, "") - - CheckOnData(t, 1, true, false, &[][]byte{watchReply}, []ExpFilterOp{ - {proxylib.PASS, 4}, {proxylib.PASS, 91}, {proxylib.PASS, 91}, {proxylib.PASS, 91}, {proxylib.PASS, 91}, {proxylib.PASS, 91}, - }, proxylib.OK, "") - }, - }, - { - "text partial linefeed", - ` rule: < - key: "keyExact" - value: "" - > - rule: < - key: "command" - value: "set" - > - `, - func(t *testing.T) { - - CheckOnData(t, 1, false, false, &[][]byte{getKeysText[:len(getKeysText)-1]}, []ExpFilterOp{ - {proxylib.MORE, 1}, - }, proxylib.OK, "") - }, - }, - { - "text set pass on empty rule", - "", - func(t *testing.T) { - CheckOnData(t, 1, false, false, &[][]byte{setHelloText}, []ExpFilterOp{ - {proxylib.PASS, len(setHelloText)}, {proxylib.MORE, 2}, - }, proxylib.OK, "") - - CheckOnData(t, 1, true, false, &[][]byte{stored}, []ExpFilterOp{ - {proxylib.PASS, len(stored)}, - }, proxylib.OK, "") - }, - }, -} - -var binaryTestCases = []testCase{ - { - "bin get pass exact key", - ` rule: < - key: "keyExact" - value: "Hello" - > - rule: < - key: "command" - value: "get" - > - `, - func(t *testing.T) { - CheckOnData(t, 1, false, false, &[][]byte{getHello}, []ExpFilterOp{ - {proxylib.PASS, len(getHello)}, {proxylib.MORE, 24}, - }, proxylib.OK, "") - }, - }, - { - "bin get pass prefix key", - ` rule: < - key: "keyPrefix" - value: "Hell" - > - rule: < - key: "command" - value: "get" - > - `, - func(t *testing.T) { - CheckOnData(t, 1, false, false, &[][]byte{getHello}, []ExpFilterOp{ - {proxylib.PASS, len(getHello)}, {proxylib.MORE, 24}, - }, proxylib.OK, "") - }, - }, - { - "bin get pass regex key", - ` rule: < - key: "keyRegex" - value: "^.el.o$" - > - rule: < - key: "command" - value: "get" - > - `, - func(t *testing.T) { - CheckOnData(t, 1, false, false, &[][]byte{getHello}, []ExpFilterOp{ - {proxylib.PASS, len(getHello)}, {proxylib.MORE, 24}, - }, proxylib.OK, "") - }, - }, - { - "bin get drop", - ` rule: < - key: "keyExact" - value: "" - > - rule: < - key: "command" - value: "set" - > - `, - func(t *testing.T) { - CheckOnData(t, 1, false, false, &[][]byte{getHello}, []ExpFilterOp{ - {proxylib.DROP, len(getHello)}, {proxylib.MORE, 24}, - }, proxylib.OK, string(binarymemcache.DeniedMsgBase)) - }, - }, - { - "bin get more", - ` rule: < - key: "keyExact" - value: "" - > - rule: < - key: "command" - value: "get" - > - `, - func(t *testing.T) { - data := getHello[:10] - CheckOnData(t, 1, false, false, &[][]byte{data}, []ExpFilterOp{{proxylib.MORE, 14}}, proxylib.OK, "") - }, - }, - { - "bin get split", - ` rule: < - key: "keyExact" - value: "" - > - rule: < - key: "command" - value: "get" - > - `, - func(t *testing.T) { - data := getHello - CheckOnData(t, 1, false, false, &[][]byte{data[:10], data[10:]}, []ExpFilterOp{ - {proxylib.PASS, len(data)}, {proxylib.MORE, 24}, - }, proxylib.OK, "") - }, - }, - { - "bin get remaining key", - ` rule: < - key: "keyExact" - value: "" - > - rule: < - key: "command" - value: "get" - > - `, - func(t *testing.T) { - data := getHello[:26] - CheckOnData(t, 1, false, false, &[][]byte{data}, []ExpFilterOp{ - {proxylib.MORE, 3}, - }, proxylib.OK, "") - }, - }, - { - "bin set drop and allow", - ` rule: < - key: "keyExact" - value: "" - > - rule: < - key: "command" - value: "set" - > - `, - func(t *testing.T) { - CheckOnData(t, 1, false, false, &[][]byte{setHello, getHello}, []ExpFilterOp{ - {proxylib.PASS, len(setHello)}, {proxylib.DROP, len(getHello)}, {proxylib.MORE, 24}, - }, proxylib.OK, string(binarymemcache.DeniedMsgBase)) - - CheckOnData(t, 1, true, false, &[][]byte{getHelloResp}, []ExpFilterOp{ - {proxylib.PASS, len(getHelloResp)}, {proxylib.INJECT, len(binarymemcache.DeniedMsgBase)}, - }, proxylib.OK, string(binarymemcache.DeniedMsgBase)) - }, - }, -} diff --git a/proxylib/libcilium/proxylib_test.go b/proxylib/libcilium/proxylib_test.go deleted file mode 100644 index 4aa8b021b..000000000 --- a/proxylib/libcilium/proxylib_test.go +++ /dev/null @@ -1,834 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package libcilium - -import ( - "fmt" - "testing" - "time" - - "github.com/sirupsen/logrus" - - cilium "github.com/cilium/proxy/go/cilium/api" - "github.com/cilium/proxy/proxylib/proxylib" - "github.com/cilium/proxy/proxylib/test" - _ "github.com/cilium/proxy/proxylib/testparsers" -) - -const debug = false - -func TestOpenModule(t *testing.T) { - mod1 := OpenModule([][2]string{}, debug) - if mod1 == 0 { - t.Error("OpenModule() with empty params failed") - } else { - defer CloseModule(mod1) - } - mod2 := OpenModule([][2]string{}, debug) - if mod2 == 0 { - t.Error("OpenModule() with empty params failed") - } else { - defer CloseModule(mod2) - } - if mod2 != mod1 { - t.Error("OpenModule() with empty params called again opened a new module") - } - - mod3 := OpenModule([][2]string{{"dummy-key", "dummy-value"}, {"key2", "value2"}}, debug) - if mod3 != 0 { - t.Error("OpenModule() with unknown params accepted") - defer CloseModule(mod3) - } - - logServer := test.StartAccessLogServer("access_log.sock", 10) - defer logServer.Close() - - mod4 := OpenModule([][2]string{{"access-log-path", logServer.Path}}, debug) - if mod4 == 0 { - t.Errorf("OpenModule() with access log path %s failed", logServer.Path) - } else { - defer CloseModule(mod4) - } - if mod4 == mod1 { - t.Error("OpenModule() should have returned a different module") - } - - mod5 := OpenModule([][2]string{{"access-log-path", logServer.Path}, {"node-id", "host~127.0.0.1~libcilium~localdomain"}}, debug) - if mod5 == 0 { - t.Errorf("OpenModule() with access log path %s failed", logServer.Path) - } else { - defer CloseModule(mod5) - } - if mod5 == mod1 || mod5 == mod2 || mod5 == mod3 || mod5 == mod4 { - t.Error("OpenModule() should have returned a different module") - } -} - -func TestOnNewConnection(t *testing.T) { - mod := OpenModule([][2]string{}, debug) - if mod == 0 { - t.Error("OpenModule() with empty params failed") - } else { - defer CloseModule(mod) - } - - // Unkhown parser - CheckOnNewConnection(t, mod, "invalid-parser-should-not-exist", 1, true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1", 80, proxylib.UNKNOWN_PARSER, 0) - - // Non-numeric destination port - CheckOnNewConnection(t, mod, "test.passer", 1, true, 1, 2, "1.1.1.1:34567", "10.0.0.2:XYZ", "1.1.1.1", - 80, proxylib.INVALID_ADDRESS, 0) - - // Missing Destination port - CheckOnNewConnection(t, mod, "test.passer", 1, true, 1, 2, "1.1.1.1:34567", "10.0.0.2", "1.1.1.1", - 80, proxylib.INVALID_ADDRESS, 0) - - // Zero Destination port is reserved for wildcarding - CheckOnNewConnection(t, mod, "test.passer", 1, true, 1, 2, "1.1.1.1:34567", "10.0.0.2:0", "1.1.1.1", - 80, proxylib.INVALID_ADDRESS, 0) - - // L7 parser rejecting the connection based on connection metadata - CheckOnNewConnection(t, mod, "test.passer", 1, true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "invalid-policy", - 80, proxylib.POLICY_DROP, 0) - - // Using test parser - CheckOnNewConnection(t, mod, "test.passer", 1, true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1", - 80, proxylib.OK, 1) - - // 2nd connection - CheckOnNewConnection(t, mod, "test.passer", 12345678901234567890, false, 2, 1, "10.0.0.2:80", "1.1.1.1:34567", "2.2.2.2", - 80, proxylib.OK, 2) - - CheckClose(t, 1, nil, 2) - - CheckClose(t, 12345678901234567890, nil, 1) -} - -func checkAccessLogs(t *testing.T, logServer *test.AccessLogServer, expPasses, expDrops int) { - t.Helper() - passes, drops := 0, 0 - nWaits := 0 - done := false - // Loop until done or when the timeout has ticked 100 times without any logs being received - for !done && nWaits < 100 { - select { - case entryType := <-logServer.Logs: - if entryType == cilium.EntryType_Denied { - drops++ - } else { - passes++ - } - // Start the timeout again (for upto 5 seconds) - nWaits = 0 - case <-time.After(50 * time.Millisecond): - // Count the number of times we have waited since the last log was received - nWaits++ - // Finish when expected number of passes and drops have been collected - // and there are no more logs in the channel for 50 milliseconds - if passes == expPasses && drops == expDrops { - done = true - } - } - } - - if !(passes == expPasses && drops == expDrops) { - t.Errorf("OnData: Unexpected access log entries, expected %d passes (got %d) and %d drops (got %d).", expPasses, passes, expDrops, drops) - } -} - -func TestOnDataNoPolicy(t *testing.T) { - logServer := test.StartAccessLogServer("access_log.sock", 10) - defer logServer.Close() - - mod := OpenModule([][2]string{{"access-log-path", logServer.Path}}, debug) - if mod == 0 { - t.Errorf("OpenModule() with access log path %s failed", logServer.Path) - } else { - defer CloseModule(mod) - } - - // Using headertester parser - buf := CheckOnNewConnection(t, mod, "test.headerparser", 1, true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1", - 30, proxylib.OK, 1) - - // Original direction data, drops with remaining data - line1, line2, line3 := "No policy\n", "Dropped\n", "foo" - CheckOnData(t, 1, false, false, &[][]byte{[]byte(line1), []byte(line2 + line3)}, []ExpFilterOp{ - {proxylib.DROP, len(line1)}, - {proxylib.DROP, len(line2)}, - {proxylib.MORE, 1}, - }, proxylib.OK, "Line dropped: "+line1+"Line dropped: "+line2) - - // No new input - CheckOnData(t, 1, false, false, &[][]byte{[]byte(line3)}, []ExpFilterOp{ - {proxylib.MORE, 1}, - }, proxylib.OK, "") - - // Empty - CheckOnData(t, 1, false, false, &[][]byte{}, []ExpFilterOp{}, proxylib.OK, "") - - expPasses, expDrops := 0, 2 - checkAccessLogs(t, logServer, expPasses, expDrops) - - CheckClose(t, 1, buf, 1) -} - -type PanicParserFactory struct{} - -var panicParserFactory *PanicParserFactory - -type PanicParser struct { - connection *proxylib.Connection -} - -func (p *PanicParserFactory) Create(connection *proxylib.Connection) interface{} { - logrus.Debugf("PanicParserFactory: Create: %v", connection) - return &PanicParser{connection: connection} -} - -// Parses individual lines and verifies them against the policy -func (p *PanicParser) OnData(reply, endStream bool, data [][]byte) (proxylib.OpType, int) { - if !reply { - panic(fmt.Errorf("PanicParser OnData(reply=%t, endStream=%t, data=%v) panicing...", reply, endStream, data)) - } - return proxylib.NOP, 0 -} - -func TestOnDataPanic(t *testing.T) { - logServer := test.StartAccessLogServer("access_log.sock", 10) - defer logServer.Close() - - mod := OpenModule([][2]string{{"access-log-path", logServer.Path}}, debug) - if mod == 0 { - t.Errorf("OpenModule() with access log path %s failed", logServer.Path) - } else { - defer CloseModule(mod) - } - - // This registation will remain after this test. - proxylib.RegisterParserFactory("test.panicparser", panicParserFactory) - - // Using headertester parser - buf := CheckOnNewConnection(t, mod, "test.panicparser", 11, true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1", - 30, proxylib.OK, 1) - - // Original direction data, drops with remaining data - CheckOnData(t, 11, false, false, &[][]byte{[]byte("foo")}, []ExpFilterOp{}, proxylib.PARSER_ERROR, "") - - expPasses, expDrops := 0, 1 - checkAccessLogs(t, logServer, expPasses, expDrops) - - CheckClose(t, 11, buf, 1) -} - -func insertPolicyText(t *testing.T, mod uint64, version string, policies []string) bool { - return insertPolicyTextRaw(t, mod, version, policies, "") == nil -} - -func insertPolicyTextRaw(t *testing.T, mod uint64, version string, policies []string, expectFail string) error { - instance := proxylib.FindInstance(mod) - if instance == nil { - t.Errorf("Policy Update failed to get the library instance.") - } else { - return instance.InsertPolicyText(version, policies, expectFail) - } - return nil -} - -func TestUnsupportedL7Drops(t *testing.T) { - logServer := test.StartAccessLogServer("access_log.sock", 10) - defer logServer.Close() - - mod := OpenModule([][2]string{{"access-log-path", logServer.Path}}, debug) - if mod == 0 { - t.Errorf("OpenModule() with access log path %s failed", logServer.Path) - } else { - defer CloseModule(mod) - } - - insertPolicyText(t, mod, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 1 - remote_policies: 3 - remote_policies: 4 - kafka_rules: < - kafka_rules: < - topic: "Topic" - > - > - > - > - `}) - - // Using headertester parser - buf := CheckOnNewConnection(t, mod, "test.headerparser", 1, true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1", - 256, proxylib.OK, 1) - - // Original direction data, drops with remaining data - line1, line2, line3, line4 := "Beginning----\n", "foo\n", "----End\n", "\n" - data := line1 + line2 + line3 + line4 - CheckOnData(t, 1, false, false, &[][]byte{[]byte(data)}, []ExpFilterOp{ - {proxylib.DROP, len(line1)}, - {proxylib.DROP, len(line2)}, - {proxylib.DROP, len(line3)}, - {proxylib.DROP, len(line4)}, - }, proxylib.OK, "Line dropped: "+line1+"Line dropped: "+line2+"Line dropped: "+line3+"Line dropped: "+line4) - - expPasses, expDrops := 0, 4 - checkAccessLogs(t, logServer, expPasses, expDrops) - - CheckClose(t, 1, buf, 1) -} - -func TestUnsupportedL7DropsGeneric(t *testing.T) { - logServer := test.StartAccessLogServer("access_log.sock", 10) - defer logServer.Close() - - mod := OpenModule([][2]string{{"access-log-path", logServer.Path}}, debug) - if mod == 0 { - t.Errorf("OpenModule() with access log path %s failed", logServer.Path) - } else { - defer CloseModule(mod) - } - - insertPolicyText(t, mod, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 1 - remote_policies: 3 - remote_policies: 4 - l7_proto: "this-parser-does-not-exist" - l7_rules: < - l7_allow_rules: < - rule: < - key: "prefix" - value: "Beginning" - > - > - > - > - > - `}) - - // Using headertester parser - buf := CheckOnNewConnection(t, mod, "test.headerparser", 1, true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1", - 256, proxylib.OK, 1) - - // Original direction data, drops with remaining data - line1, line2, line3, line4 := "Beginning----\n", "foo\n", "----End\n", "\n" - data := line1 + line2 + line3 + line4 - CheckOnData(t, 1, false, false, &[][]byte{[]byte(data)}, []ExpFilterOp{ - {proxylib.DROP, len(line1)}, - {proxylib.DROP, len(line2)}, - {proxylib.DROP, len(line3)}, - {proxylib.DROP, len(line4)}, - }, proxylib.OK, "Line dropped: "+line1+"Line dropped: "+line2+"Line dropped: "+line3+"Line dropped: "+line4) - - expPasses, expDrops := 0, 4 - checkAccessLogs(t, logServer, expPasses, expDrops) - - CheckClose(t, 1, buf, 1) -} - -func TestEnvoyL7DropsGeneric(t *testing.T) { - logServer := test.StartAccessLogServer("access_log.sock", 10) - defer logServer.Close() - - mod := OpenModule([][2]string{{"access-log-path", logServer.Path}}, debug) - if mod == 0 { - t.Errorf("OpenModule() with access log path %s failed", logServer.Path) - } else { - defer CloseModule(mod) - } - - insertPolicyText(t, mod, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 1 - remote_policies: 3 - remote_policies: 4 - l7_proto: "envoy.filter.network.test" - l7_rules: < - l7_allow_rules: < - rule: < - key: "action" - value: "drop" - > - > - > - > - > - `}) - - // Using headertester parser - buf := CheckOnNewConnection(t, mod, "test.headerparser", 1, true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1", - 256, proxylib.OK, 1) - - // Original direction data, drops with remaining data - line1, line2, line3, line4 := "Beginning----\n", "foo\n", "----End\n", "\n" - data := line1 + line2 + line3 + line4 - CheckOnData(t, 1, false, false, &[][]byte{[]byte(data)}, []ExpFilterOp{ - {proxylib.DROP, len(line1)}, - {proxylib.DROP, len(line2)}, - {proxylib.DROP, len(line3)}, - {proxylib.DROP, len(line4)}, - }, proxylib.OK, "Line dropped: "+line1+"Line dropped: "+line2+"Line dropped: "+line3+"Line dropped: "+line4) - - expPasses, expDrops := 0, 4 - checkAccessLogs(t, logServer, expPasses, expDrops) - - CheckClose(t, 1, buf, 1) -} - -func TestTwoRulesOnSamePortFirstNoL7(t *testing.T) { - logServer := test.StartAccessLogServer("access_log.sock", 10) - defer logServer.Close() - - mod := OpenModule([][2]string{{"access-log-path", logServer.Path}}, debug) - if mod == 0 { - t.Errorf("OpenModule() with access log path %s failed", logServer.Path) - } else { - defer CloseModule(mod) - } - - insertPolicyText(t, mod, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 11 - > - rules: < - remote_policies: 11 - http_rules: < - http_rules: < - headers: < - name: ":path" - exact_match: "/allowed" - > - > - > - > - > - `}) -} - -func TestTwoRulesOnSamePortFirstNoL7Generic(t *testing.T) { - logServer := test.StartAccessLogServer("access_log.sock", 10) - defer logServer.Close() - - mod := OpenModule([][2]string{{"access-log-path", logServer.Path}}, debug) - if mod == 0 { - t.Errorf("OpenModule() with access log path %s failed", logServer.Path) - } else { - defer CloseModule(mod) - } - - insertPolicyText(t, mod, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 11 - > - rules: < - remote_policies: 1 - remote_policies: 3 - remote_policies: 4 - l7_proto: "test.headerparser" - l7_rules: < - l7_allow_rules: < - rule: < - key: "prefix" - value: "Beginning" - > - > - l7_allow_rules: < - rule: < - key: "suffix" - value: "End" - > - > - > - > - > - `}) -} - -func TestTwoRulesOnSamePortMismatchingL7(t *testing.T) { - logServer := test.StartAccessLogServer("access_log.sock", 10) - defer logServer.Close() - - mod := OpenModule([][2]string{{"access-log-path", logServer.Path}}, debug) - if mod == 0 { - t.Errorf("OpenModule() with access log path %s failed", logServer.Path) - } else { - defer CloseModule(mod) - } - - // This registation will remain after this test. - proxylib.RegisterL7RuleParser("PortNetworkPolicyRule_HttpRules", func(*cilium.PortNetworkPolicyRule) []proxylib.L7NetworkPolicyRule { - return nil - }) - - err := insertPolicyTextRaw(t, mod, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 11 - http_rules: < - http_rules: < - headers: < - name: ":path" - exact_match: "/allowed" - > - > - > - > - rules: < - remote_policies: 1 - remote_policies: 3 - remote_policies: 4 - l7_proto: "test.headerparser" - l7_rules: < - l7_allow_rules: < - rule: < - key: "prefix" - value: "Beginning" - > - > - l7_allow_rules: < - rule: < - key: "suffix" - value: "End" - > - > - > - > - > - `}, "update") - if err == nil { - t.Errorf("Expected Policy Update to fail due to mismatching L7 protocols on the same port, but it succeeded") - } else { - logrus.Debugf("Expected error: %s", err) - } -} - -func TestSimplePolicy(t *testing.T) { - logServer := test.StartAccessLogServer("access_log.sock", 10) - defer logServer.Close() - - mod := OpenModule([][2]string{{"access-log-path", logServer.Path}}, debug) - if mod == 0 { - t.Errorf("OpenModule() with access log path %s failed", logServer.Path) - } else { - defer CloseModule(mod) - } - - insertPolicyText(t, mod, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 1 - remote_policies: 3 - remote_policies: 4 - l7_proto: "test.headerparser" - l7_rules: < - l7_allow_rules: < - rule: < - key: "prefix" - value: "Beginning" - > - > - l7_allow_rules: < - rule: < - key: "suffix" - value: "End" - > - > - > - > - > - `}) - - // Using headertester parser - buf := CheckOnNewConnection(t, mod, "test.headerparser", 1, true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1", - 80, proxylib.OK, 1) - - // Original direction data, drops with remaining data - line1, line2, line3, line4 := "Beginning----\n", "foo\n", "----End\n", "\n" - data := line1 + line2 + line3 + line4 - CheckOnData(t, 1, false, false, &[][]byte{[]byte(data)}, []ExpFilterOp{ - {proxylib.PASS, len(line1)}, - {proxylib.DROP, len(line2)}, - {proxylib.PASS, len(line3)}, - {proxylib.DROP, len(line4)}, - }, proxylib.OK, "Line dropped: "+line2+"Line dropped: "+line4) - - expPasses, expDrops := 2, 2 - checkAccessLogs(t, logServer, expPasses, expDrops) - - CheckClose(t, 1, buf, 1) -} - -func TestAllowAllPolicy(t *testing.T) { - logServer := test.StartAccessLogServer("access_log.sock", 10) - defer logServer.Close() - - mod := OpenModule([][2]string{{"access-log-path", logServer.Path}}, debug) - if mod == 0 { - t.Errorf("OpenModule() with access log path %s failed", logServer.Path) - } else { - defer CloseModule(mod) - } - - insertPolicyText(t, mod, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2 - ingress_per_port_policies: < - port: 80 - rules: < - l7_proto: "test.headerparser" - l7_rules: < - l7_allow_rules: <> - > - > - > - `}) - - // Using headertester parser - buf := CheckOnNewConnection(t, mod, "test.headerparser", 1, true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1", - 80, proxylib.OK, 1) - - // Original direction data, drops with remaining data - line1, line2, line3, line4 := "Beginning----\n", "foo\n", "----End\n", "\n" - data := line1 + line2 + line3 + line4 - CheckOnData(t, 1, false, false, &[][]byte{[]byte(data)}, []ExpFilterOp{ - {proxylib.PASS, len(line1)}, - {proxylib.PASS, len(line2)}, - {proxylib.PASS, len(line3)}, - {proxylib.PASS, len(line4)}, - }, proxylib.OK, "") - - expPasses, expDrops := 4, 0 - checkAccessLogs(t, logServer, expPasses, expDrops) - - CheckClose(t, 1, buf, 1) -} - -// L3 drops happen before calling into proxylib, but here we test that the policy update does not -// accidentally treat deny rules as allow rules. -func TestDenyAllPolicy(t *testing.T) { - logServer := test.StartAccessLogServer("access_log.sock", 10) - defer logServer.Close() - - mod := OpenModule([][2]string{{"access-log-path", logServer.Path}}, debug) - if mod == 0 { - t.Errorf("OpenModule() with access log path %s failed", logServer.Path) - } else { - defer CloseModule(mod) - } - - insertPolicyText(t, mod, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 1 - l7_proto: "test.headerparser" - l7_rules: < - l7_allow_rules: <> - > - > - rules: < - deny: true - > - > - `}) - - // Using headertester parser - buf := CheckOnNewConnection(t, mod, "test.headerparser", 1, true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1", - 80, proxylib.OK, 1) - - // Original direction data, drops with remaining data - line1, line2, line3, line4 := "Beginning----\n", "foo\n", "----End\n", "\n" - data := line1 + line2 + line3 + line4 - CheckOnData(t, 1, false, false, &[][]byte{[]byte(data)}, []ExpFilterOp{ - {proxylib.DROP, len(line1)}, - {proxylib.DROP, len(line2)}, - {proxylib.DROP, len(line3)}, - {proxylib.DROP, len(line4)}, - }, proxylib.OK, "Line dropped: "+line1+"Line dropped: "+line2+"Line dropped: "+line3+"Line dropped: "+line4) - - expPasses, expDrops := 0, 4 - checkAccessLogs(t, logServer, expPasses, expDrops) - - CheckClose(t, 1, buf, 1) -} - -func TestDenyPolicy(t *testing.T) { - logServer := test.StartAccessLogServer("access_log.sock", 10) - defer logServer.Close() - - mod := OpenModule([][2]string{{"access-log-path", logServer.Path}}, debug) - if mod == 0 { - t.Errorf("OpenModule() with access log path %s failed", logServer.Path) - } else { - defer CloseModule(mod) - } - - insertPolicyText(t, mod, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2 - ingress_per_port_policies: < - port: 80 - rules: < - remote_policies: 1 - l7_proto: "test.headerparser" - l7_rules: < - l7_allow_rules: <> - > - > - rules: < - remote_policies: 42 - deny: true - > - > - `}) - - // deny on ID 42 has no effect on traffic from 1 - // Using headertester parser - buf := CheckOnNewConnection(t, mod, "test.headerparser", 1, true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1", - 80, proxylib.OK, 1) - - // Original direction data, drops with remaining data - line1, line2, line3, line4 := "Beginning----\n", "foo\n", "----End\n", "\n" - data := line1 + line2 + line3 + line4 - CheckOnData(t, 1, false, false, &[][]byte{[]byte(data)}, []ExpFilterOp{ - {proxylib.PASS, len(line1)}, - {proxylib.PASS, len(line2)}, - {proxylib.PASS, len(line3)}, - {proxylib.PASS, len(line4)}, - }, proxylib.OK, "") - - expPasses, expDrops := 4, 0 - checkAccessLogs(t, logServer, expPasses, expDrops) - - CheckClose(t, 1, buf, 1) -} - -func TestAllowEmptyPolicy(t *testing.T) { - logServer := test.StartAccessLogServer("access_log.sock", 10) - defer logServer.Close() - - mod := OpenModule([][2]string{{"access-log-path", logServer.Path}}, debug) - if mod == 0 { - t.Errorf("OpenModule() with access log path %s failed", logServer.Path) - } else { - defer CloseModule(mod) - } - - insertPolicyText(t, mod, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2 - ingress_per_port_policies: < - port: 80 - rules: < - l7_proto: "test.headerparser" - > - > - `}) - - // Using headertester parser, policy name matches the policy - buf := CheckOnNewConnection(t, mod, "test.headerparser", 1, true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1", - 80, proxylib.OK, 1) - - // Original direction data, drops with remaining data - line1, line2, line3, line4 := "Beginning----\n", "foo\n", "----End\n", "\n" - CheckOnData(t, 1, false, false, &[][]byte{[]byte(line1), []byte(line2), []byte(line3), []byte(line4)}, []ExpFilterOp{ - {proxylib.PASS, len(line1)}, - {proxylib.PASS, len(line2)}, - {proxylib.PASS, len(line3)}, - {proxylib.PASS, len(line4)}, - }, proxylib.OK, "") - - // Connection using a different policy name still drops - CheckOnNewConnection(t, mod, "test.headerparser", 2, true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "2.2.2.2", - 80, proxylib.OK, 2) - CheckOnData(t, 2, false, false, &[][]byte{[]byte(line1)}, []ExpFilterOp{ - {proxylib.DROP, len(line1)}, - }, proxylib.OK, "Line dropped: "+line1) - - expPasses, expDrops := 4, 1 - checkAccessLogs(t, logServer, expPasses, expDrops) - - CheckClose(t, 2, buf, 2) - CheckClose(t, 1, buf, 1) -} - -func TestAllowAllPolicyL3Egress(t *testing.T) { - logServer := test.StartAccessLogServer("access_log.sock", 10) - defer logServer.Close() - - mod := OpenModule([][2]string{{"access-log-path", logServer.Path}}, debug) - if mod == 0 { - t.Errorf("OpenModule() with access log path %s failed", logServer.Path) - } else { - defer CloseModule(mod) - } - - // logging.ToggleDebugLogs(true) - // logrus.SetLevel(logrus.DebugLevel) - - insertPolicyText(t, mod, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 42 - egress_per_port_policies: < - port: 80 - rules: < - remote_policies: 2 - l7_proto: "test.headerparser" - l7_rules: < - l7_allow_rules: <> - > - > - > - `}) - - // Using headertester parser - buf := CheckOnNewConnection(t, mod, "test.headerparser", 1, false, 42, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1", - 80, proxylib.OK, 1) - - // Original direction data, drops with remaining data - line1, line2, line3, line4 := "Beginning----\n", "foo\n", "----End\n", "\n" - data := line1 + line2 + line3 + line4 - CheckOnData(t, 1, false, false, &[][]byte{[]byte(data)}, []ExpFilterOp{ - {proxylib.PASS, len(line1)}, - {proxylib.PASS, len(line2)}, - {proxylib.PASS, len(line3)}, - {proxylib.PASS, len(line4)}, - }, proxylib.OK, "") - - expPasses, expDrops := 4, 0 - checkAccessLogs(t, logServer, expPasses, expDrops) - - CheckClose(t, 1, buf, 1) -} diff --git a/proxylib/memcached/binary/parser.go b/proxylib/memcached/binary/parser.go deleted file mode 100644 index 0cb8ce936..000000000 --- a/proxylib/memcached/binary/parser.go +++ /dev/null @@ -1,194 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package binary - -import ( - "bytes" - "encoding/binary" - "strconv" - - "github.com/sirupsen/logrus" - - cilium "github.com/cilium/proxy/go/cilium/api" - "github.com/cilium/proxy/proxylib/memcached/meta" - "github.com/cilium/proxy/proxylib/proxylib" -) - -// ParserFactory implements proxylib.ParserFactory -type ParserFactory struct{} - -// Create creates binary memcached parser -func (p *ParserFactory) Create(connection *proxylib.Connection) interface{} { - logrus.Debugf("ParserFactory: Create: %v", connection) - return &Parser{connection: connection, injectQueue: make([]queuedInject, 0)} -} - -// compile time check for interface implementation -var _ proxylib.ParserFactory = &ParserFactory{} - -// ParserFactoryInstance creates binary parser for unified parser -var ParserFactoryInstance *ParserFactory - -// Parser implements proxylib.Parser -type Parser struct { - connection *proxylib.Connection - - requestCount uint32 - replyCount uint32 - injectQueue []queuedInject -} - -var _ proxylib.Parser = &Parser{} - -const headerSize = 24 - -// OnData parses binary memcached data -func (p *Parser) OnData(reply, endStream bool, dataBuffers [][]byte) (proxylib.OpType, int) { - if reply { - if p.injectFromQueue() { - return proxylib.INJECT, len(DeniedMsgBase) - } - if len(dataBuffers) == 0 { - return proxylib.NOP, 0 - } - } - - //TODO don't copy data from buffers - data := bytes.Join(dataBuffers, []byte{}) - logrus.Debugf("Data length: %d", len(data)) - - if headerSize > len(data) { - headerMissing := headerSize - len(data) - logrus.Debugf("Did not receive needed header data, need %d more bytes", headerMissing) - return proxylib.MORE, headerMissing - } - - bodyLength := binary.BigEndian.Uint32(data[8:12]) - - keyLength := binary.BigEndian.Uint16(data[2:4]) - extrasLength := data[4] - - if keyLength > 0 { - neededData := headerSize + int(keyLength) + int(extrasLength) - if neededData > len(data) { - keyMissing := neededData - len(data) - logrus.Debugf("Did not receive enough bytes for key, need %d more bytes", keyMissing) - return proxylib.MORE, keyMissing - } - } - - opcode, key, err := p.getOpcodeAndKey(data, extrasLength, keyLength) - if err != 0 { - return proxylib.ERROR, int(err) - } - - logEntry := &cilium.LogEntry_GenericL7{ - GenericL7: &cilium.L7LogEntry{ - Proto: "binarymemcached", - Fields: map[string]string{ - "opcode": strconv.Itoa(int(opcode)), - "key": string(key), - }, - }, - } - - // we don't filter reply traffic - if reply { - logrus.Debugf("reply, passing %d bytes", len(data)) - p.connection.Log(cilium.EntryType_Response, logEntry) - p.replyCount++ - return proxylib.PASS, int(bodyLength + headerSize) - } - - p.requestCount++ - - matches := p.connection.Matches(meta.MemcacheMeta{ - Opcode: opcode, - Keys: [][]byte{key}, - }) - if matches { - p.connection.Log(cilium.EntryType_Request, logEntry) - return proxylib.PASS, int(bodyLength + headerSize) - } - - magic := ResponseMagic | data[0] - - // This is done to ensure in-order replies - if p.requestCount == p.replyCount+1 { - p.injectDeniedMessage(magic) - } else { - p.injectQueue = append(p.injectQueue, queuedInject{magic, p.requestCount}) - } - - p.injectQueue = append(p.injectQueue, queuedInject{magic, p.requestCount}) - - p.connection.Log(cilium.EntryType_Denied, logEntry) - return proxylib.DROP, int(bodyLength + headerSize) -} - -type queuedInject struct { - magic byte - requestID uint32 -} - -func (p *Parser) injectDeniedMessage(magic byte) { - deniedMsg := make([]byte, len(DeniedMsgBase)) - copy(deniedMsg, DeniedMsgBase) - - deniedMsg[0] = magic - - p.connection.Inject(true, deniedMsg) - p.replyCount++ -} - -func (p *Parser) injectFromQueue() bool { - if len(p.injectQueue) > 0 { - if p.injectQueue[0].requestID == p.replyCount+1 { - p.injectDeniedMessage(p.injectQueue[0].magic) - p.injectQueue = p.injectQueue[1:] - return true - } - } - return false -} - -const ( - // RequestMagic says that memcache frame is a request - RequestMagic = 0x80 - // ResponseMagic says that memcache frame is a response - ResponseMagic = 0x81 -) - -func (p *Parser) getOpcodeAndKey(data []byte, extrasLength byte, keyLength uint16) (byte, []byte, proxylib.OpError) { - if data[0]&RequestMagic != RequestMagic { - logrus.Warnf("Direction bit is 'response', but memcached parser only parses requests") - return 0, []byte{}, proxylib.ERROR_INVALID_FRAME_TYPE - } - - opcode := data[1] - key := getMemcacheKey(data, extrasLength, keyLength) - - return opcode, key, 0 -} - -func getMemcacheKey(packet []byte, extrasLength byte, keyLength uint16) []byte { - if keyLength == 0 { - return []byte{} - } - return packet[headerSize+int(extrasLength) : headerSize+int(extrasLength)+int(keyLength)] -} - -// DeniedMsgBase is sent if policy denies the request. Exported for tests -var DeniedMsgBase = []byte{ - 0x81, 0, 0, 0, - 0, 0, 0, 8, - 0, 0, 0, 0x0d, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 'a', 'c', 'c', - 'e', 's', 's', - ' ', 'd', 'e', - 'n', 'i', 'e', - 'd'} diff --git a/proxylib/memcached/binary/parser_test.go b/proxylib/memcached/binary/parser_test.go deleted file mode 100644 index c81a1dadb..000000000 --- a/proxylib/memcached/binary/parser_test.go +++ /dev/null @@ -1,57 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package binary - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestMemcacheGetKey(c *testing.T) { - packet := []byte{ - 0x80, 0, 0, 0x5, - 0, 0, 0, 0, - 0, 0, 0, 0x5, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 'T', 'e', 's', 't', - '1', - } - - key := getMemcacheKey(packet, 0, 5) - - require.Equal(c, "Test1", string(key)) - - packet = []byte{ - 0x80, 0, 0, 0x5, - 0x4, 0, 0, 0, - 0, 0, 0, 0x5, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 'e', 'x', 't', 'r', - 'T', 'e', 's', 't', - '1', - } - - key = getMemcacheKey(packet, 4, 5) - - require.Equal(c, "Test1", string(key)) - - packet = []byte{ - 0x80, 0x8, 0, 0x0, - 0x4, 0, 0, 0, - 0, 0, 0, 0x4, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0x1c, 0x20, - } - - key = getMemcacheKey(packet, 4, 0) - - require.Equal(c, "", string(key)) -} diff --git a/proxylib/memcached/meta/meta.go b/proxylib/memcached/meta/meta.go deleted file mode 100644 index 222709148..000000000 --- a/proxylib/memcached/meta/meta.go +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -// text memcache protocol parser based on https://github.com/memcached/memcached/blob/master/doc/protocol.txt - -package meta - -// MemcacheMeta gathers information about memcache frame for L7 rules matching -type MemcacheMeta struct { - // for text protocol - Command string - // for binary protocol - Opcode byte - Keys [][]byte -} - -// IsBinary tells whether meta instance is for text or binary protocol -func (m *MemcacheMeta) IsBinary() bool { - return len(m.Command) == 0 -} diff --git a/proxylib/memcached/parser.go b/proxylib/memcached/parser.go deleted file mode 100644 index 114dec9e5..000000000 --- a/proxylib/memcached/parser.go +++ /dev/null @@ -1,464 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -// text memcache protocol parser based on https://github.com/memcached/memcached/blob/master/doc/protocol.txt - -package memcache - -import ( - "bytes" - "fmt" - "regexp" - - "github.com/sirupsen/logrus" - - cilium "github.com/cilium/proxy/go/cilium/api" - - "github.com/cilium/proxy/proxylib/memcached/binary" - "github.com/cilium/proxy/proxylib/memcached/meta" - "github.com/cilium/proxy/proxylib/memcached/text" - "github.com/cilium/proxy/proxylib/proxylib" -) - -// Rule matches against memcached requests -type Rule struct { - // allowed commands - commands memcacheCommandSet - // compiled regex - keyExact []byte - keyPrefix []byte - regex *regexp.Regexp - - empty bool -} - -// Matches returns true if the Rule matches -func (rule *Rule) Matches(data interface{}) bool { - logrus.Debugf("memcache checking rule %v", *rule) - - packetMeta, ok := data.(meta.MemcacheMeta) - - if !ok { - logrus.Debugf("Wrong type supplied to Rule.Matches") - return false - } - - if rule.empty { - return true - } - - if packetMeta.IsBinary() { - if !rule.matchOpcode(packetMeta.Opcode) { - return false - } - } else { - if !rule.matchCommand(packetMeta.Command) { - return false - } - } - - if len(rule.keyExact) > 0 { - for _, key := range packetMeta.Keys { - if !bytes.Equal(rule.keyExact, key) { - return false - } - } - return true - } - - if len(rule.keyPrefix) > 0 { - for _, key := range packetMeta.Keys { - if !bytes.HasPrefix(key, rule.keyPrefix) { - return false - } - } - return true - } - - if rule.regex != nil { - for _, key := range packetMeta.Keys { - if !rule.regex.Match(key) { - return false - } - } - return true - } - - logrus.Debugf("No key rule specified, accepted by command match") - return true -} - -func (rule *Rule) matchCommand(cmd string) bool { - _, ok := rule.commands.text[cmd] - return ok -} - -func (rule *Rule) matchOpcode(code byte) bool { - _, ok := rule.commands.binary[code] - return ok -} - -// L7RuleParser parses protobuf L7 rules to and array of Rule -// May panic -func L7RuleParser(rule *cilium.PortNetworkPolicyRule) []proxylib.L7NetworkPolicyRule { - l7Rules := rule.GetL7Rules() - if l7Rules == nil { - return nil - } - - allowRules := l7Rules.GetL7AllowRules() - rules := make([]proxylib.L7NetworkPolicyRule, 0, len(allowRules)) - for _, l7Rule := range allowRules { - var br Rule - var commandFound = false - for k, v := range l7Rule.Rule { - switch k { - case "command": - br.commands, commandFound = MemcacheOpCodeMap[v] - case "keyExact": - br.keyExact = []byte(v) - case "keyPrefix": - br.keyPrefix = []byte(v) - case "keyRegex": - br.regex = regexp.MustCompile(v) - default: - proxylib.ParseError(fmt.Sprintf("Unsupported key: %s", k), rule) - } - } - if !commandFound { - if len(br.keyExact) > 0 || len(br.keyPrefix) > 0 || br.regex != nil { - proxylib.ParseError("command not specified but key was provided", rule) - } else { - br.empty = true - } - } - logrus.Debugf("Parsed Rule pair: %v", br) - rules = append(rules, &br) - } - return rules -} - -// ParserFactory implements proxylib.ParserFactory -type ParserFactory struct{} - -// Create creates memcached parser -func (p *ParserFactory) Create(connection *proxylib.Connection) interface{} { - logrus.Debugf("ParserFactory: Create: %v", connection) - return &Parser{ - connection: connection, - } -} - -// compile time check for interface implementation -var _ proxylib.ParserFactory = &ParserFactory{} - -var memcacheParserFactory *ParserFactory - -const ( - parserName = "memcache" -) - -func init() { - logrus.Debug("init(): Registering memcacheParserFactory") - proxylib.RegisterParserFactory(parserName, memcacheParserFactory) - proxylib.RegisterL7RuleParser(parserName, L7RuleParser) -} - -// Parser implements proxylib.Parser -type Parser struct { - connection *proxylib.Connection - parser proxylib.Parser -} - -var _ proxylib.Parser = &Parser{} - -// OnData parses memcached data -func (p *Parser) OnData(reply, endStream bool, dataBuffers [][]byte) (proxylib.OpType, int) { - if p.parser == nil { - var magicByte byte - if len(dataBuffers) > 0 && len(dataBuffers[0]) > 0 { - magicByte = dataBuffers[0][0] - } else { - return proxylib.NOP, 0 - } - - if magicByte >= 128 { - p.parser = binary.ParserFactoryInstance.Create(p.connection).(proxylib.Parser) - } else { - p.parser = text.ParserFactoryInstance.Create(p.connection).(proxylib.Parser) - } - } - return p.parser.OnData(reply, endStream, dataBuffers) -} - -type memcacheCommandSet struct { - text map[string]struct{} - binary map[byte]struct{} -} - -// empty var for filling map below which will be used as set -var e = struct{}{} - -// MemcacheOpCodeMap maps operation names and groups used in policy rules to sets of operation names and opcodes that are allowed in such a policy rule. -// for more information on protocol check https://github.com/memcached/memcached/wiki/Protocols -var MemcacheOpCodeMap = map[string]memcacheCommandSet{ - "add": { - text: map[string]struct{}{"add": e}, - binary: map[byte]struct{}{2: e, 18: e}, - }, - "set": { - text: map[string]struct{}{"set": e}, - binary: map[byte]struct{}{1: e, 17: e}, - }, - "replace": { - text: map[string]struct{}{"replace": e}, - binary: map[byte]struct{}{3: e, 19: e}, - }, - "append": { - text: map[string]struct{}{"append": e}, - binary: map[byte]struct{}{14: e, 25: e}, - }, - "prepend": { - text: map[string]struct{}{"prepend": e}, - binary: map[byte]struct{}{15: e, 26: e}, - }, - "cas": { - text: map[string]struct{}{"cas": e}, - binary: map[byte]struct{}{}, - }, - "incr": { - text: map[string]struct{}{"incr": e}, - binary: map[byte]struct{}{5: e, 21: e}, - }, - "decr": { - text: map[string]struct{}{"decr": e}, - binary: map[byte]struct{}{6: e, 22: e}, - }, - "storage": { - text: map[string]struct{}{ - "add": e, - "set": e, - "replace": e, - "append": e, - "prepend": e, - "cas": e, - "incr": e, - "decr": e, - }, - binary: map[byte]struct{}{ - 1: e, - 2: e, - 3: e, - 5: e, - 6: e, - 17: e, - 18: e, - 19: e, - 21: e, - 22: e, - 25: e, - 26: e, - }, - }, - - "get": { - text: map[string]struct{}{"get": e, "gets": e}, - binary: map[byte]struct{}{ - 0: e, - 9: e, - 12: e, - 13: e, - }, - }, - - "delete": { - text: map[string]struct{}{"delete": e}, - binary: map[byte]struct{}{ - 4: e, - 20: e, - }, - }, - - "touch": { - text: map[string]struct{}{"touch": e}, - binary: map[byte]struct{}{28: e}, - }, - - "gat": { - text: map[string]struct{}{"gat": e, "gats": e}, - binary: map[byte]struct{}{29: e, 30: e}, - }, - - "writeGroup": { - text: map[string]struct{}{ - "add": e, - "set": e, - "replace": e, - "append": e, - "prepend": e, - "cas": e, - "incr": e, - "decr": e, - "delete": e, - "touch": e, - }, - binary: map[byte]struct{}{ - 1: e, - 2: e, - 3: e, - 4: e, - 5: e, - 6: e, - 17: e, - 18: e, - 19: e, - 20: e, - 21: e, - 22: e, - 25: e, - 26: e, - 28: e, - }, - }, - - "slabs": { - text: map[string]struct{}{"slabs": e}, - binary: map[byte]struct{}{}, - }, - - "lru": { - text: map[string]struct{}{"lru": e}, - binary: map[byte]struct{}{}, - }, - - "lru_crawler": { - text: map[string]struct{}{"lru_crawler": e}, - binary: map[byte]struct{}{}, - }, - - "watch": { - text: map[string]struct{}{"watch": e}, - binary: map[byte]struct{}{}, - }, - - "stats": { - text: map[string]struct{}{"stats": e}, - binary: map[byte]struct{}{16: e}, - }, - - "flush_all": { - text: map[string]struct{}{"flush_all": e}, - binary: map[byte]struct{}{8: e, 24: e}, - }, - - "cache_memlimit": { - text: map[string]struct{}{"cache_memlimit": e}, - binary: map[byte]struct{}{}, - }, - - "version": { - text: map[string]struct{}{"version": e}, - binary: map[byte]struct{}{11: e}, - }, - - "misbehave": { - text: map[string]struct{}{"misbehave": e}, - binary: map[byte]struct{}{}, - }, - - "quit": { - text: map[string]struct{}{"quit": e}, - binary: map[byte]struct{}{7: e, 23: e}, - }, - - "noop": { - text: map[string]struct{}{}, - binary: map[byte]struct{}{10: e}, - }, - "verbosity": { - text: map[string]struct{}{}, - binary: map[byte]struct{}{27: e}, - }, - "sasl-list-mechs": { - text: map[string]struct{}{}, - binary: map[byte]struct{}{32: e}, - }, - "sasl-auth": { - text: map[string]struct{}{}, - binary: map[byte]struct{}{33: e}}, - "sasl-step": { - text: map[string]struct{}{}, - binary: map[byte]struct{}{34: e}}, - "rget": { - text: map[string]struct{}{}, - binary: map[byte]struct{}{48: e}}, - "rset": { - text: map[string]struct{}{}, - binary: map[byte]struct{}{49: e}}, - "rsetq": { - text: map[string]struct{}{}, - binary: map[byte]struct{}{50: e}}, - "rappend": { - text: map[string]struct{}{}, - binary: map[byte]struct{}{51: e}}, - "rappendq": { - text: map[string]struct{}{}, - binary: map[byte]struct{}{52: e}}, - "rprepend": { - text: map[string]struct{}{}, - binary: map[byte]struct{}{53: e}}, - "rprependq": { - text: map[string]struct{}{}, - binary: map[byte]struct{}{54: e}}, - "rdelete": { - text: map[string]struct{}{}, - binary: map[byte]struct{}{55: e}}, - "rdeleteq": { - text: map[string]struct{}{}, - binary: map[byte]struct{}{56: e}}, - "rincr": { - text: map[string]struct{}{}, - binary: map[byte]struct{}{57: e}}, - "rincrq": { - text: map[string]struct{}{}, - binary: map[byte]struct{}{58: e}}, - "rdecr": { - text: map[string]struct{}{}, - binary: map[byte]struct{}{59: e}}, - "rdecrq": { - text: map[string]struct{}{}, - binary: map[byte]struct{}{60: e}}, - "set-vbucket": { - text: map[string]struct{}{}, - binary: map[byte]struct{}{61: e}}, - "get-vbucket": { - text: map[string]struct{}{}, - binary: map[byte]struct{}{62: e}}, - "del-vbucket": { - text: map[string]struct{}{}, - binary: map[byte]struct{}{63: e}}, - "tap-connect": { - text: map[string]struct{}{}, - binary: map[byte]struct{}{64: e}}, - "tap-mutation": { - text: map[string]struct{}{}, - binary: map[byte]struct{}{65: e}}, - "tap-delete": { - text: map[string]struct{}{}, - binary: map[byte]struct{}{66: e}}, - "tap-flush": { - text: map[string]struct{}{}, - binary: map[byte]struct{}{67: e}}, - "tap-opaque": { - text: map[string]struct{}{}, - binary: map[byte]struct{}{68: e}}, - "tap-vbucket-set": { - text: map[string]struct{}{}, - binary: map[byte]struct{}{69: e}}, - "tap-checkpoint-start": { - text: map[string]struct{}{}, - binary: map[byte]struct{}{70: e}}, - "tap-checkpoint-end": { - text: map[string]struct{}{}, - binary: map[byte]struct{}{71: e}}, -} diff --git a/proxylib/memcached/text/parser.go b/proxylib/memcached/text/parser.go deleted file mode 100644 index c7ab986c4..000000000 --- a/proxylib/memcached/text/parser.go +++ /dev/null @@ -1,320 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -// text memcache protocol parser based on https://github.com/memcached/memcached/blob/master/doc/protocol.txt - -package text - -import ( - "bytes" - "strconv" - - "github.com/sirupsen/logrus" - - cilium "github.com/cilium/proxy/go/cilium/api" - - "github.com/cilium/proxy/proxylib/memcached/meta" - "github.com/cilium/proxy/proxylib/proxylib" -) - -// ParserFactory implements proxylib.ParserFactory -type ParserFactory struct{} - -// Create creates memcached parser -func (p *ParserFactory) Create(connection *proxylib.Connection) interface{} { - logrus.Debugf("ParserFactory: Create: %v", connection) - return &Parser{connection: connection, replyQueue: make([]*replyIntent, 0)} -} - -// compile time check for interface implementation -var _ proxylib.ParserFactory = &ParserFactory{} - -// ParserFactoryInstance creates text parser for unified memcached parser -var ParserFactoryInstance *ParserFactory - -// Parser implements proxylib.Parser -type Parser struct { - connection *proxylib.Connection - - replyQueue []*replyIntent - - //set to true when watch command is observed - watching bool -} - -type replyIntent struct { - command []byte - denied bool -} - -var _ proxylib.Parser = &Parser{} - -// consts indicating number of tokens in memcache command that indicates noreply command -const ( - casWithNoreplyFields = 7 - storageWithNoreplyFields = 6 - deleteWithNoreplyFields = 3 - incrWithNoreplyFields = 4 - touchWithNoreplyFields = 4 -) - -// OnData parses text memcached data -func (p *Parser) OnData(reply, endStream bool, dataBuffers [][]byte) (proxylib.OpType, int) { - if reply { - injected := p.injectFromQueue() - if injected > 0 { - return proxylib.INJECT, injected - } - if len(dataBuffers) == 0 { - return proxylib.NOP, 0 - } - } - - // TODO: don't copy data to new slices - data := (bytes.Join(dataBuffers, []byte{})) - logrus.Debugf("Data length: %d", len(data)) - - linefeed := bytes.Index(data, []byte("\r\n")) - if linefeed < 0 { - logrus.Debugf("Did not receive full first line") - if len(data) > 0 && data[len(data)-1] == '\r' { - return proxylib.MORE, 1 - } - return proxylib.MORE, 2 - } - - // TODO: iterate over data without copying it to new slices - // Tokenizing in memcached is done by spaces: https://github.com/memcached/memcached/blob/master/memcached.c#L2978 - tokens := bytes.Fields(data[:linefeed]) - - if !reply { - meta := meta.MemcacheMeta{ - Command: string(tokens[0]), - } - command := tokens[0] - - frameLength := linefeed + 2 - hasNoreply := false - switch { - case p.isCommandRetrieval(command): - // get, gets, gat, gats - if bytes.HasPrefix(command, []byte("get")) { - meta.Keys = tokens[1:] - } else if bytes.HasPrefix(command, []byte("gat")) { - meta.Keys = tokens[2:] - } - case p.isCommandStorage(command): - // storage commands - meta.Keys = tokens[1:2] - nBytes, err := strconv.Atoi(string(tokens[4])) - if err != nil { - logrus.Error("Failed to parse storage payload length") - return proxylib.ERROR, 0 - } - // 2 additional bytes for terminating linefeed - frameLength += nBytes + 2 - - if command[0] == 'c' { //storage command is "cas" - hasNoreply = len(tokens) == casWithNoreplyFields - } else { - hasNoreply = len(tokens) == storageWithNoreplyFields - } - case p.isCommandDelete(command): - meta.Keys = tokens[1:2] - hasNoreply = len(tokens) == deleteWithNoreplyFields - case p.isCommandIncrDecr(command): - meta.Keys = tokens[1:2] - hasNoreply = len(tokens) == incrWithNoreplyFields - case bytes.Equal(command, []byte("touch")): - meta.Keys = tokens[1:2] - hasNoreply = len(tokens) == touchWithNoreplyFields - case bytes.Equal(command, []byte("slabs")), - bytes.Equal(command, []byte("lru")), - bytes.Equal(command, []byte("lru_crawler")), - bytes.Equal(command, []byte("stats")), - bytes.Equal(command, []byte("version")), - bytes.Equal(command, []byte("misbehave")): - - meta.Keys = [][]byte{} - case bytes.Equal(command, []byte("flush_all")), - bytes.Equal(command, []byte("cache_memlimit")): - meta.Keys = [][]byte{} - hasNoreply = bytes.Equal(tokens[len(tokens)-1], []byte("noreply")) - case bytes.Equal(command, []byte("quit")): - meta.Keys = [][]byte{} - hasNoreply = true - case bytes.Equal(command, []byte("watch")): - meta.Keys = [][]byte{} - p.watching = true - default: - logrus.Error("Could not parse text memcache frame") - return proxylib.ERROR, 0 - } - logEntry := &cilium.LogEntry_GenericL7{ - GenericL7: &cilium.L7LogEntry{ - Proto: "textmemcached", - Fields: map[string]string{ - "command": meta.Command, - "keys": string(bytes.Join(meta.Keys, []byte(", "))), - }, - }, - } - - r := &replyIntent{ - command: command, - } - - matches := p.connection.Matches(meta) - - if matches { - r.denied = false - if !hasNoreply { - p.replyQueue = append(p.replyQueue, r) - } - p.connection.Log(cilium.EntryType_Request, logEntry) - return proxylib.PASS, frameLength - } - - r.denied = true - if !hasNoreply { - if len(p.replyQueue) == 0 { - p.injectDeniedMessage() - } else { - p.replyQueue = append(p.replyQueue, r) - } - } - p.connection.Log(cilium.EntryType_Denied, logEntry) - return proxylib.DROP, frameLength - } - - //reply - logrus.Debugf("reply, parsing to figure out if we have it all") - - intent := p.replyQueue[0] - - logEntry := &cilium.LogEntry_GenericL7{ - GenericL7: &cilium.L7LogEntry{ - Proto: "textmemcached", - Fields: map[string]string{ - "command": string(intent.command), - }, - }, - } - if p.watching { - // in watch mode we pass all replied lines - return proxylib.PASS, linefeed + 2 - } - - switch { - case p.isErrorReply(tokens[0]), - p.isCommandStorage(intent.command), - p.isCommandDelete(intent.command), - p.isCommandIncrDecr(intent.command), - bytes.Equal(intent.command, []byte("touch")), - bytes.Equal(intent.command, []byte("slabs")), - bytes.Equal(intent.command, []byte("lru")), - bytes.Equal(intent.command, []byte("flush_all")), - bytes.Equal(intent.command, []byte("cache_memlimit")), - bytes.Equal(intent.command, []byte("version")), - bytes.Equal(intent.command, []byte("misbehave")): - - // passing one line of reply - p.connection.Log(cilium.EntryType_Response, logEntry) - p.replyQueue = p.replyQueue[1:] - return proxylib.PASS, linefeed + 2 - case p.isCommandRetrieval(intent.command), - bytes.Equal(intent.command, []byte("stats")): - t, nBytes := p.untilEnd(data) - if t == proxylib.PASS { - p.connection.Log(cilium.EntryType_Response, logEntry) - p.replyQueue = p.replyQueue[1:] - } - return t, nBytes - case bytes.Equal(intent.command, []byte("lru_crawler")): - // check if it's response line - if bytes.Equal(tokens[0], []byte("OK")) || - bytes.Equal(tokens[0], []byte("BUSY")) || - bytes.Equal(tokens[0], []byte("BADCLASS")) { - p.connection.Log(cilium.EntryType_Response, logEntry) - p.replyQueue = p.replyQueue[1:] - return proxylib.PASS, linefeed + 2 - } - - t, nBytes := p.untilEnd(data) - if t == proxylib.PASS { - p.connection.Log(cilium.EntryType_Response, logEntry) - p.replyQueue = p.replyQueue[1:] - } - return t, nBytes - } - logrus.Error("Could not parse text memcache frame") - return proxylib.ERROR, 0 -} - -const payloadEnd = "\r\nEND\r\n" - -func (p *Parser) untilEnd(data []byte) (proxylib.OpType, int) { - // TODO: optimise this to not ask per byte, but take VALUES lines into account - endIndex := bytes.Index(data, []byte(payloadEnd)) - if endIndex > 0 { - return proxylib.PASS, endIndex + len(payloadEnd) - } - return proxylib.MORE, 1 -} - -func (p *Parser) isCommandRetrieval(cmd []byte) bool { - return bytes.HasPrefix(cmd, []byte("get")) || - bytes.HasPrefix(cmd, []byte("gat")) -} - -func (p *Parser) isCommandStorage(cmd []byte) bool { - return bytes.Equal(cmd, []byte("set")) || - bytes.Equal(cmd, []byte("add")) || - bytes.Equal(cmd, []byte("replace")) || - bytes.Equal(cmd, []byte("append")) || - bytes.Equal(cmd, []byte("prepend")) || - bytes.Equal(cmd, []byte("cas")) -} - -func (p *Parser) isCommandDelete(cmd []byte) bool { - return bytes.Equal(cmd, []byte("delete")) -} - -func (p *Parser) isCommandIncrDecr(cmd []byte) bool { - return bytes.Equal(cmd, []byte("incr")) || - bytes.Equal(cmd, []byte("decr")) -} - -func (p *Parser) isErrorReply(firstToken []byte) bool { - return bytes.Equal(firstToken, []byte("ERROR")) || - bytes.Equal(firstToken, []byte("CLIENT_ERROR")) || - bytes.Equal(firstToken, []byte("SERVER_ERROR")) -} - -// returns injected bytes -func (p *Parser) injectFromQueue() int { - injected := 0 - for _, rep := range p.replyQueue { - if rep.denied { - injected++ - p.injectDeniedMessage() - } else { - break - } - - } - if injected > 0 { - p.replyQueue = p.replyQueue[injected:] - } - return injected * len(DeniedMsg) -} - -func (p *Parser) injectDeniedMessage() { - p.connection.Inject(true, DeniedMsg) -} - -// DeniedMsg is sent if policy denies the request. Exported for tests -var DeniedMsg = []byte("CLIENT_ERROR access denied\r\n") - -// ErrorMsg is standard memcached error line -var ErrorMsg = []byte("ERROR\r\n") diff --git a/proxylib/npds/backoff.go b/proxylib/npds/backoff.go deleted file mode 100644 index 26b2cbe01..000000000 --- a/proxylib/npds/backoff.go +++ /dev/null @@ -1,94 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package npds - -import ( - "context" - "fmt" - "math" - "time" - - "github.com/sirupsen/logrus" -) - -// exponentialBackoff implements an exponential backoff -type exponentialBackoff struct { - // Min is the minimal backoff time, if unspecified, 1 second will be - // used - Min time.Duration - - // Max is the maximum backoff time, if unspecified, no maximum time is - // applied - Max time.Duration - - // Name is a free form string describing the operation subject to the - // backoff, if unspecified, a UUID is generated. This string is used - // for logging purposes. - Name string - - lastBackoffStart time.Time - - attempt int -} - -// calculateDuration calculates the backoff duration based on minimum base -// interval, exponential factor and number of failures. -func calculateDuration(min, max time.Duration, factor float64, failures int) time.Duration { - minFloat := float64(min) - maxFloat := float64(max) - - t := minFloat * math.Pow(factor, float64(failures)) - if max != time.Duration(0) && t > maxFloat { - t = maxFloat - } - - return time.Duration(t) -} - -// Reset backoff attempt counter -func (b *exponentialBackoff) Reset() { - b.attempt = 0 -} - -// Wait waits for the required time using an exponential backoff -func (b *exponentialBackoff) Wait(ctx context.Context) error { - if b.Name == "" { - panic("no name provided") - } - - b.lastBackoffStart = time.Now() - b.attempt++ - t := b.duration(b.attempt) - - logrus.WithFields(logrus.Fields{ - "subsys": "backoff", - "time": t, - "attempt": b.attempt, - "name": b.Name, - }).Debug("Sleeping with exponential backoff") - - select { - case <-ctx.Done(): - return fmt.Errorf("exponential backoff cancelled via context: %s", ctx.Err()) - case <-time.After(t): - } - - return nil -} - -// duration returns the wait duration for the nth attempt -func (b *exponentialBackoff) duration(attempt int) time.Duration { - min := time.Duration(1) * time.Second - if b.Min != time.Duration(0) { - min = b.Min - } - - t := calculateDuration(min, b.Max, 2, attempt) - - if b.Max != time.Duration(0) && t > b.Max { - t = b.Max - } - - return t -} diff --git a/proxylib/npds/client.go b/proxylib/npds/client.go deleted file mode 100644 index 67236b4b8..000000000 --- a/proxylib/npds/client.go +++ /dev/null @@ -1,209 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package npds - -import ( - "context" - "errors" - "fmt" - "io" - "sync" - "time" - - "github.com/sirupsen/logrus" - "google.golang.org/genproto/googleapis/rpc/status" - "google.golang.org/grpc" - - cilium "github.com/cilium/proxy/go/cilium/api" - "github.com/cilium/proxy/proxylib/proxylib" - envoy_config_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" - envoy_service_discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" -) - -const ( - DialDelay = 100 * time.Millisecond - BackOffLimit = 100 // Max 100 times DialDelay - NPDSTypeURL = "type.googleapis.com/cilium.NetworkPolicy" -) - -type Client struct { - updater proxylib.PolicyUpdater - mutex sync.Mutex - nodeId string - path string - conn *grpc.ClientConn - stream grpc.ClientStream - closing bool -} - -func (c *Client) Close() { - c.mutex.Lock() - defer c.mutex.Unlock() - if !c.closing { - logrus.Debugf("NPDS: Client %s closing on %s", c.nodeId, c.path) - c.closing = true - if c.stream != nil { - c.stream.CloseSend() - } - if c.conn != nil { - c.conn.Close() - } - } -} - -func (c *Client) Path() string { - return c.path -} - -func NewClient(path, nodeId string, updater proxylib.PolicyUpdater) proxylib.PolicyClient { - if path == "" { - return nil - } - c := &Client{ - updater: updater, - path: path, - nodeId: nodeId, - } - logrus.Debugf("NPDS: Client %s starting on %s", c.nodeId, c.path) - - // These are used to return error if the 1st try fails - // Only used for testing and logging, as we keep on trying anyway. - startErr := make(chan error) // Channel open as long as 'starting == true' - - go func() { - starting := true - backOff := exponentialBackoff{ - Min: DialDelay, - Max: BackOffLimit * DialDelay, - Name: "proxylib NPDS client", - } - for { - err := c.Run(func() { - // Report successful start on the first try by closing the channel - if starting { - close(startErr) - starting = false - } - logrus.Debugf("NPDS: Client %s connected on %s", c.nodeId, c.path) - }) - c.mutex.Lock() - closing := c.closing - c.mutex.Unlock() - - if err != nil { - logrus.Debug(err) - if starting { - startErr <- err - close(startErr) - starting = false - } - } else { - // Reset backoff after successful start - backOff.Reset() - } - - if closing { - break - } - - // Back off before retrying - backOff.Wait(context.TODO()) - } - }() - - // Block until we know if the first connection try succeeded or failed - _ = <-startErr - return c -} - -func (c *Client) Run(connected func()) (err error) { - unixPath := "unix://" + c.path - - defer func() { - // Recover from any possible panics - if r := recover(); r != nil { - err = fmt.Errorf("NPDS Client %s: Panic: %v", c.nodeId, r) - } - }() - - // - // WithInsecure() is safe here because we are connecting to a Unix-domain socket, - // data of whch is never on the wire and security for which can be managed with file permissions. - // - conn, err := grpc.Dial(unixPath, grpc.WithInsecure()) - if err != nil { - return fmt.Errorf("NPDS: Client %s grpc.Dial() on %s failed: %s", c.nodeId, c.path, err) - } - client := cilium.NewNetworkPolicyDiscoveryServiceClient(conn) - stream, err := client.StreamNetworkPolicies(context.Background()) - if err != nil { - conn.Close() - return fmt.Errorf("NPDS: Client %s stream failed on %s: %s", c.nodeId, c.path, err) - } - c.mutex.Lock() - c.conn = conn - c.stream = stream - c.mutex.Unlock() - defer func() { - c.mutex.Lock() - c.stream.CloseSend() - c.conn.Close() - c.mutex.Unlock() - }() - - // VersionInfo must be empty as we have not received anything yet. - // ResourceNames is empty to request for all policies. - // ResponseNonce is copied from the response, initially empty. - req := envoy_service_discovery.DiscoveryRequest{ - TypeUrl: NPDSTypeURL, - VersionInfo: "", - Node: &envoy_config_core.Node{Id: c.nodeId}, - ResourceNames: nil, - ResponseNonce: "", - } - err = stream.Send(&req) - if err != nil { - return fmt.Errorf("NPDS: Client %s stream.Send() failed on %s: %s", c.nodeId, c.path, err) - } - - connected() - - for { - // Receive next policy configuration. This will block until the - // server has a new version to send, which may take a long time. - resp, err := stream.Recv() - if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) { - logrus.Debugf("NPDS: Client %s stream on %s closed.", c.nodeId, c.path) - break - } - if err != nil { - return fmt.Errorf("NPDS: Client %s stream.Recv() on %s failed: %s", c.nodeId, c.path, err) - } - - // Validate the response - if resp.TypeUrl != req.TypeUrl { - msg := fmt.Sprintf("NPDS: Client %s rejecting mismatching resource type on %s: %s", c.nodeId, c.path, resp.TypeUrl) - req.ErrorDetail = &status.Status{Message: msg} - logrus.Warning(msg) - } else { - err = c.updater.PolicyUpdate(resp) - if err != nil { - msg := fmt.Sprintf("NPDS: Client %s rejecting invalid policy on %s: %s", c.nodeId, c.path, err) - req.ErrorDetail = &status.Status{Message: msg} - logrus.Warning(msg) - } else { - // Success, update the last applied version - logrus.Debugf("NPDS: Client %s acking new policy version on %s: %s", c.nodeId, c.path, resp.VersionInfo) - req.ErrorDetail = nil - req.VersionInfo = resp.VersionInfo - } - } - req.ResponseNonce = resp.Nonce - err = stream.Send(&req) - if err != nil { - return fmt.Errorf("NPDS: Client %s stream.Send() failed on %s: %s", c.nodeId, c.path, err) - } - } - return nil -} diff --git a/proxylib/proxylib.go b/proxylib/proxylib.go deleted file mode 100644 index 21779d16e..000000000 --- a/proxylib/proxylib.go +++ /dev/null @@ -1,83 +0,0 @@ -// nolint:goheader -// CGo injects a 'Code generated by ...' header here before AST parsing, ignore it. - -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package main - -/* -#include "types.h" -*/ -import "C" - -import ( - "github.com/sirupsen/logrus" - - "github.com/cilium/proxy/proxylib/libcilium" -) - -func init() { - logrus.Info("proxylib: Initializing library") -} - -// OnNewConnection is used to register a new connection of protocol 'proto'. -// Note that the 'origBuf' and replyBuf' type '*[]byte' corresponds to 'InjectBuf' type, but due to -// cgo export restrictions we can't use the go type in the prototype. -// -//export OnNewConnection -func OnNewConnection(instanceId uint64, proto string, connectionId uint64, ingress bool, srcId, dstId uint32, srcAddr, dstAddr, policyName string, origBuf, replyBuf *[]byte) C.FilterResult { - return C.FilterResult(libcilium.OnNewConnection(instanceId, proto, connectionId, ingress, srcId, dstId, srcAddr, dstAddr, policyName, origBuf, replyBuf)) -} - -// Each connection is assumed to be called from a single thread, so accessing connection metadata -// does not need protection. -// -// OnData gets all the unparsed data the datapath has received so far. The data is provided to the parser -// associated with the connection, and the parser is expected to find if the data frame contains enough data -// to make a PASS/DROP decision for the whole data frame. Note that the whole data frame need not be received, -// if the decision including the length of the data frame in bytes can be determined based on the beginning of -// the data frame only (e.g., headers including the length of the data frame). The parser returns a decision -// with the number of bytes on which the decision applies. If more data is available, then the parser will be -// called again with the remaining data. Parser needs to return MORE if a decision can't be made with -// the available data, including the minimum number of additional bytes that is needed before the parser is -// called again. -// -// The parser can also inject at arbitrary points in the data stream. This is indecated by an INJECT operation -// with the number of bytes to be injected. The actual bytes to be injected are provided via an Inject() -// callback prior to returning the INJECT operation. The Inject() callback operates on a limited size buffer -// provided by the datapath, and multiple INJECT operations may be needed to inject large amounts of data. -// Since we get the data on one direction at a time, any frames to be injected in the reverse direction -// are placed in the reverse direction buffer, from where the datapath injects the data before calling -// us again for the reverse direction input. -// -//export OnData -func OnData(connectionId uint64, reply, endStream bool, data *[][]byte, filterOps *[][2]int64) C.FilterResult { - return C.FilterResult(libcilium.OnData(connectionId, reply, endStream, data, filterOps)) -} - -// Make this more general connection event callback -// -//export Close -func Close(connectionId uint64) { - libcilium.Close(connectionId) -} - -// OpenModule is called before any other APIs. -// Called concurrently by different filter instances. -// Returns a library instance ID that must be passed to all other API calls. -// Calls with the same parameters will return the same instance. -// Zero return value indicates an error. -// -//export OpenModule -func OpenModule(params [][2]string, debug bool) uint64 { - return libcilium.OpenModule(params, debug) -} - -//export CloseModule -func CloseModule(id uint64) { - libcilium.CloseModule(id) -} - -// Must have empty main -func main() {} diff --git a/proxylib/proxylib/connection.go b/proxylib/proxylib/connection.go deleted file mode 100644 index 810e18964..000000000 --- a/proxylib/proxylib/connection.go +++ /dev/null @@ -1,259 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package proxylib - -import ( - "fmt" - "net" - "strconv" - "time" - - "github.com/sirupsen/logrus" - - cilium "github.com/cilium/proxy/go/cilium/api" -) - -// A parser sees data from the underlying stream in both directions -// (original, connection open direction and the opposite, the reply -// direction). Each call to the filter returns an ordered set of -// operations to be performed on the data in that direction. Any data -// left over after the returned operations must be buffered by the -// caller and passed in again when more data has been received on the -// connection. - -// InjectBuf is a pointer to a slice header for an inject buffer allocated by -// the proxylib caller. As data is placed into the buffer, the length -// of the buffer in the slice header is increased correspondingly. To make -// the the injected data visible to the caller we need to pass the slice header -// by reference rather than by value, hence the pointer in the type. -// As the caller is typically in a differnent memory management domain (not -// subject to Go runtime garbage collection), the underlying buffer may never -// be expanded or otherwise reallocated. -type InjectBuf *[]byte - -// Connection holds the connection metadata that is used both for -// policy enforcement and access logging. -type Connection struct { - Instance *Instance // Holder of Policy protocol and access logging clients - Id uint64 // Unique connection ID allocated by the caller - Ingress bool // 'true' for ingress, 'false' for egress - SrcId uint32 // Source security ID, may be mapped from the source IP address - DstId uint32 // Destination security ID, may be mapped from the destination IP address - SrcAddr string // Source IP address in "a.b.c.d:port" or "[A:...:C]:port" format - DstAddr string // Original destination IP address - PolicyName string // Identifies which policy instance applies to this connection - Port uint32 // (original) destination port number in numeric format - - ParserName string // Name of the parser - Parser interface{} // Parser instance used on this connection - Reader Reader - OrigBuf InjectBuf // Buffer for injected frames in original direction - ReplyBuf InjectBuf // Buffer for injected frames in reply direction -} - -func NewConnection(instance *Instance, proto string, connectionId uint64, ingress bool, srcId, dstId uint32, srcAddr, dstAddr, policyName string, origBuf, replyBuf *[]byte) (error, *Connection) { - // Find the parser for the proto - parserFactory := GetParserFactory(proto) - if parserFactory == nil { - return UNKNOWN_PARSER, nil - } - _, port, err := net.SplitHostPort(dstAddr) - if err != nil { - return INVALID_ADDRESS, nil - } - dstPort, err := strconv.ParseUint(port, 10, 32) - if err != nil || dstPort == 0 { - return INVALID_ADDRESS, nil - } - - connection := &Connection{ - Instance: instance, - Id: connectionId, - Ingress: ingress, - SrcId: srcId, - DstId: dstId, - SrcAddr: srcAddr, - DstAddr: dstAddr, - Port: uint32(dstPort), - PolicyName: policyName, - ParserName: proto, - OrigBuf: origBuf, - ReplyBuf: replyBuf, - } - connection.Parser = parserFactory.Create(connection) - if connection.Parser == nil { - // Parser rejected the new connection based on the connection metadata - return POLICY_DROP, nil - } - - return nil, connection -} - -// Skip bytes in input, or exhaust the input. -func advanceInput(input [][]byte, bytes int) [][]byte { - for bytes > 0 && len(input) > 0 { - rem := len(input[0]) // this much data left in the first slice - if bytes < rem { - input[0] = input[0][bytes:] // skip 'bytes' bytes - bytes = 0 - } else { // go to the beginning of the next unit - bytes -= rem - input = input[1:] // may result in an empty slice - } - } - return input -} - -func (connection *Connection) OnData(reply, endStream bool, data *[][]byte, filterOps *[][2]int64) (res FilterResult) { - defer func() { - // Recover from any possible parser datapath panics - if r := recover(); r != nil { - // Log the Panic into accesslog - connection.Log(cilium.EntryType_Denied, - &cilium.LogEntry_GenericL7{ - GenericL7: &cilium.L7LogEntry{ - Proto: connection.ParserName, - Fields: map[string]string{ - // "status" is shown in Cilium monitor - "status": fmt.Sprintf("Panic: %s", r), - }, - }, - }) - res = PARSER_ERROR // Causes the connection to be dropped - } - }() - - if parser, ok := connection.Parser.(Parser); ok { - input := *data - // Loop until `filterOps` becomes full, or parser is done with the data. - for len(*filterOps) < cap(*filterOps) { - op, bytes := parser.OnData(reply, endStream, input) - if op == NOP { - break // No operations after NOP - } - if bytes == 0 { - return PARSER_ERROR - } - *filterOps = append(*filterOps, [2]int64{int64(op), int64(bytes)}) - - if op == MORE { - // Need more data before can parse ahead. - // Parser will see the unused data again in the next call, which will take place - // after there are at least 'bytes' of additional data to parse. - break - } - - if op == PASS || op == DROP { - input = advanceInput(input, bytes) - // Loop back to parser even if have no more data to allow the parser to - // inject frames at the end of the input. - } - - // Injection does not advance input data, but instructs the datapath to - // send data the parser has placed in the inject buffer. We need to stop processing - // if inject buffer becomes full as the parser in this case can't inject any more - // data. - if op == INJECT && connection.IsInjectBufFull(reply) { - // return if inject buffer becomes full - break - } - } - } else if parser, ok := connection.Parser.(ReaderParser); ok { - connection.Reader = NewReader(*data, endStream) - // Loop until `filterOps` becomes full, or parser is done with the data. - for len(*filterOps) < cap(*filterOps) { - op, bytes := parser.OnData(reply, &connection.Reader) - if op == NOP { - break // No operations after NOP - } - if bytes == 0 { - return PARSER_ERROR - } - *filterOps = append(*filterOps, [2]int64{int64(op), int64(bytes)}) - - if op == MORE { - // Need more data before can parse ahead. - // Parser will see the unused data again in the next call, which will take place - // after there are at least 'bytes' of additional data to parse. - break - } - - // Get the current read count && reset for the next round - read := connection.Reader.Reset() - - if op == PASS || op == DROP { - // Andvance input if needed - if bytes > read { - connection.Reader.AdvanceInput(bytes - read) - } - // Loop back to parser even if have no more data to allow the parser to - // inject frames at the end of the input. - } - - // Injection does not advance input data, but instructs the datapath to - // send data the parser has placed in the inject buffer. We need to stop processing - // if inject buffer becomes full as the parser in this case can't inject any more - // data. - if op == INJECT && connection.IsInjectBufFull(reply) { - // return if inject buffer becomes full - break - } - } - } - return OK -} - -func (connection *Connection) Matches(l7 interface{}) bool { - logrus.Debugf("proxylib: Matching policy on connection %v", connection) - remoteID := connection.DstId - if connection.Ingress { - remoteID = connection.SrcId - } - return connection.Instance.PolicyMatches(connection.PolicyName, connection.Ingress, connection.Port, remoteID, l7) -} - -// getInjectBuf return the pointer to the inject buffer slice header for the indicated direction -func (connection *Connection) getInjectBuf(reply bool) InjectBuf { - if reply { - return connection.ReplyBuf - } - return connection.OrigBuf -} - -// inject buffers data to be injected into the connection at the point of INJECT -func (connection *Connection) Inject(reply bool, data []byte) int { - buf := connection.getInjectBuf(reply) - // append data to C-provided buffer - offset := len(*buf) - n := copy((*buf)[offset:cap(*buf)], data) - *buf = (*buf)[:offset+n] // update the buffer length - - logrus.Debugf("proxylib: Injected %d bytes: %s (given: %s)", n, string((*buf)[offset:offset+n]), string(data)) - - // return the number of bytes injected. This may be less than the length of `data` is - // the buffer becomes full. - // Parser may opt dropping the connection via parser error in this case! - return n -} - -// isInjectBufFull return true if the inject buffer for the indicated direction is full -func (connection *Connection) IsInjectBufFull(reply bool) bool { - buf := connection.getInjectBuf(reply) - return len(*buf) == cap(*buf) -} - -func (conn *Connection) Log(entryType cilium.EntryType, l7 cilium.IsLogEntry_L7) { - pblog := &cilium.LogEntry{ - Timestamp: uint64(time.Now().UnixNano()), - IsIngress: conn.Ingress, - EntryType: entryType, - PolicyName: conn.PolicyName, - SourceSecurityId: conn.SrcId, - DestinationSecurityId: conn.DstId, - SourceAddress: conn.SrcAddr, - DestinationAddress: conn.DstAddr, - L7: l7, - } - conn.Instance.Log(pblog) -} diff --git a/proxylib/proxylib/input_test.go b/proxylib/proxylib/input_test.go deleted file mode 100644 index f1e477da7..000000000 --- a/proxylib/proxylib/input_test.go +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package proxylib - -import ( - "testing" - - "github.com/stretchr/testify/require" -) - -func TestAdvanceInput(t *testing.T) { - input := [][]byte{[]byte("ABCD"), []byte("1234567890"), []byte("abcdefghij")} - - require.Equal(t, byte('A'), input[0][0]) - require.Len(t, input, 3) // Three slices in input - - // Advance to one byte before the end of the first slice - input = advanceInput(input, 3) - require.Len(t, input, 3) // Still in the first slice - require.Len(t, input[0], 1) - require.Equal(t, byte('D'), input[0][0]) - - // Advance to the beginning of the next slice - input = advanceInput(input, 1) - require.Len(t, input, 2) // Moved to the next slice - require.Equal(t, byte('1'), input[0][0]) - - // Advance 11 bytes, crossing to the next slice - input = advanceInput(input, 11) - require.Len(t, input, 1) // Moved to the 3rd slice - require.Equal(t, byte('b'), input[0][0]) - - // Try to advance 11 bytes when only 9 remmain - input = advanceInput(input, 11) - require.Len(t, input, 0) // All data exhausted - - // Try advance on an empty slice - input = advanceInput(input, 1) - require.Len(t, input, 0) -} diff --git a/proxylib/proxylib/instance.go b/proxylib/proxylib/instance.go deleted file mode 100644 index 4370fdacb..000000000 --- a/proxylib/proxylib/instance.go +++ /dev/null @@ -1,221 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package proxylib - -import ( - "fmt" - "sync" - "sync/atomic" - - "google.golang.org/protobuf/proto" - "github.com/sirupsen/logrus" - - cilium "github.com/cilium/proxy/go/cilium/api" - envoy_service_discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" -) - -type PolicyClient interface { - Close() - Path() string -} - -type AccessLogger interface { - Log(pblog *cilium.LogEntry) - Close() - Path() string -} - -type PolicyUpdater interface { - PolicyUpdate(resp *envoy_service_discovery.DiscoveryResponse) error -} - -type Instance struct { - id uint64 - openCount uint64 - nodeID string - accessLogger AccessLogger - policyClient PolicyClient - - policyMap atomic.Value // holds PolicyMap -} - -var ( - // mutex protects instances - mutex sync.RWMutex - // Key uint64 is a monotonically increasing instance ID - instances map[uint64]*Instance = make(map[uint64]*Instance) - // Last instance ID used - instanceId uint64 = 0 -) - -func NewInstance(nodeID string, accessLogger AccessLogger) *Instance { - instanceId++ - - if nodeID == "" { - nodeID = fmt.Sprintf("host~127.0.0.2~libcilium-%d~localdomain", instanceId) - } - - ins := &Instance{ - id: instanceId, - openCount: 1, - nodeID: nodeID, - accessLogger: accessLogger, - } - ins.setPolicyMap(newPolicyMap()) - - return ins -} - -// OpenInstance creates a new instance or finds an existing one with equivalent parameters. -// returns the instance id. -func OpenInstance(nodeID string, xdsPath string, newPolicyClient func(path, nodeID string, updater PolicyUpdater) PolicyClient, - accessLogPath string, newAccessLogger func(accessLogPath string) AccessLogger, -) uint64 { - mutex.Lock() - defer mutex.Unlock() - - // Check if have an instance with these params already - for id, old := range instances { - oldXdsPath := "" - if old.policyClient != nil { - oldXdsPath = old.policyClient.Path() - } - oldAccessLogPath := "" - if old.accessLogger != nil { - oldAccessLogPath = old.accessLogger.Path() - } - if (nodeID == "" || old.nodeID == nodeID) && xdsPath == oldXdsPath && accessLogPath == oldAccessLogPath { - old.openCount++ - logrus.Debugf("Opened existing library instance %d, open count: %d", id, old.openCount) - return id - } - } - - ins := NewInstance(nodeID, newAccessLogger(accessLogPath)) - // policy client needs the instance so we set it after instance has been created - ins.policyClient = newPolicyClient(xdsPath, ins.nodeID, ins) - - instances[instanceId] = ins - - logrus.Debugf("Opened new library instance %d", instanceId) - - return instanceId -} - -func FindInstance(id uint64) *Instance { - mutex.RLock() - defer mutex.RUnlock() - return instances[id] -} - -// Close returns the new open count -func CloseInstance(id uint64) uint64 { - mutex.Lock() - defer mutex.Unlock() - - count := uint64(0) - if ins, ok := instances[id]; ok { - ins.openCount-- - count = ins.openCount - if count == 0 { - if ins.policyClient != nil { - ins.policyClient.Close() - } - if ins.accessLogger != nil { - ins.accessLogger.Close() - } - delete(instances, id) - } - logrus.Debugf("CloseInstance(%d): Remaining open count: %d", id, count) - } else { - logrus.Debugf("CloseInstance(%d): Not found (closed already?)", id) - } - return count -} - -func (ins *Instance) getPolicyMap() PolicyMap { - return ins.policyMap.Load().(PolicyMap) -} - -func (ins *Instance) setPolicyMap(newMap PolicyMap) { - ins.policyMap.Store(newMap) -} - -func (ins *Instance) PolicyMatches(endpointPolicyName string, ingress bool, port, remoteId uint32, l7 interface{}) bool { - // Policy maps are never modified once published - policy, found := ins.getPolicyMap()[endpointPolicyName] - if !found { - logrus.Debugf("NPDS: Policy for %s not found", endpointPolicyName) - } - - return found && policy.Matches(ingress, port, remoteId, l7) -} - -// Update the PolicyMap from a protobuf. PolicyMap is only ever changed if the whole update is successful. -func (ins *Instance) PolicyUpdate(resp *envoy_service_discovery.DiscoveryResponse) (err error) { - defer func() { - if r := recover(); r != nil { - var ok bool - if err, ok = r.(error); !ok { - err = fmt.Errorf("NPDS: Panic: %v", r) - } - } - }() - - logrus.Debugf("NPDS: Updating policy for version %s", resp.VersionInfo) - - oldMap := ins.getPolicyMap() - newMap := newPolicyMap() - - for _, any := range resp.Resources { - if any.TypeUrl != resp.TypeUrl { - return fmt.Errorf("NPDS: Mismatching TypeUrls: %s != %s", any.TypeUrl, resp.TypeUrl) - } - var config cilium.NetworkPolicy - if err = proto.Unmarshal(any.Value, &config); err != nil { - return fmt.Errorf("NPDS: Policy unmarshal error: %v", err) - } - - ips := config.GetEndpointIps() - if len(ips) == 0 { - return fmt.Errorf("NPDS: Policy has no endpoint_ips") - } - for _, ip := range ips { - logrus.Debugf("NPDS: Endpoint IP: %s", ip) - } - // Locate the old version, if any - oldPolicy, found := oldMap[ips[0]] - if found { - // Check if the new policy is the same as the old one - if proto.Equal(&config, oldPolicy.protobuf) { - logrus.Debugf("NPDS: New policy for Endpoint %d is equal to the old one, no need to change", config.GetEndpointId()) - for _, ip := range ips { - newMap[ip] = oldPolicy - } - continue - } - } - - // Validate new config - if err = config.Validate(); err != nil { - return fmt.Errorf("NPDS: Policy validation error for Endpoint %d: %v", config.GetEndpointId(), err) - } - - // Create new PolicyInstance, may panic. Takes ownership of 'config'. - newPolicy := newPolicyInstance(&config) - for _, ip := range ips { - newMap[ip] = newPolicy - } - } - - // Store the new policy map - ins.setPolicyMap(newMap) - - logrus.Debugf("NPDS: Policy Update completed for instance %d: %v", ins.id, newMap) - return -} - -func (ins *Instance) Log(pblog *cilium.LogEntry) { - ins.accessLogger.Log(pblog) -} diff --git a/proxylib/proxylib/parserfactory.go b/proxylib/proxylib/parserfactory.go deleted file mode 100644 index 7b0779f14..000000000 --- a/proxylib/proxylib/parserfactory.go +++ /dev/null @@ -1,102 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package proxylib - -import ( - "github.com/sirupsen/logrus" -) - -// Parser is a paser instance used for each connection. OnData will be called from a single thread only. -type Parser interface { - // OnData is called when input is available on the underlying connection. The Parser - // instance is only ever used for processing data of a single connection, which allows - // the parser instance to keep connection specific state. All OnData() calls for a - // single connection (both directions) are made from a single thread, so that - // no locking is needed for the parser instance if no other goroutines need to access - // the parser instance. (Note that any L7 policy protocol rule parsing happens in - // other goroutine so any such parsing should not access parser instances directly.) - // - // OnData() parameters are as follows: - // 'reply' is 'false' for original direction of the connection, 'true' otherwise. - // 'endStream' is true if there is no more data after 'data' in this direction. - // 'data' is the available data in the current direction. The datapath buffers - // partial frames as instructed by the operations returned by the parser - // so that the 'data' always starts on a frame boundary. That is, whenever - // the parser returns `MORE` indicating it needs more input, the bytes - // not 'PASS'ed or 'DROP'ped are retained in a datapath buffer and those - // same bytes are passed to the parser again when more input is available. - // 'data' may be an empty slice, but the slices contained are never empty. - // - // OnData() returns an operation and the number of bytes ('N') the operation applies. - // The possible values for 'op' are: - // 'MORE' - Data currently in 'data' is to be retained by the datapath and passed - // again to OnData() after 'N' bytes more data is available. - // 'PASS' - Allow 'N' bytes. - // 'DROP' - Drop 'N' bytes and call OnData() again for the remaining data. - // 'INJECT' - Insert 'N' bytes of data placed into the inject buffer in to the - // data stream in this direction. - // 'NOP' - Do nothing, to be used when it is known if no more input - // is to be expected. - // 'ERROR' - Protocol parsing failed and the connection should be closed. - // - // OnData() is called again after 'PASS', 'DROP', and 'INJECT' with the remaining - // data even if none remains. - OnData(reply, endStream bool, data [][]byte) (op OpType, N int) -} - -// ReaderParser is an alternate parser instance is used for each connection. OnData will be called from a single thread only. -type ReaderParser interface { - // OnData is called when input is available on the underlying connection. The Parser - // instance is only ever used for processing data of a single connection, which allows - // the parser instance to keep connection specific state. All OnData() calls for a - // single connection (both directions) are made from a single thread, so that - // no locking is needed for the parser instance if no other goroutines need to access - // the parser instance. (Note that any L7 policy protocol rule parsing happens in - // other goroutine so any such parsing should not access parser instances directly.) - // - // OnData() parameters are as follows: - // 'reply' is 'false' for original direction of the connection, 'true' otherwise. - // 'endStream' is true if there is no more data after 'data' in this direction. - // 'data' is the available data in the current direction. The datapath buffers - // partial frames as instructed by the operations returned by the parser - // so that the 'data' always starts on a frame boundary. That is, whenever - // the parser returns `MORE` indicating it needs more input, the bytes - // not 'PASS'ed or 'DROP'ped are retained in a datapath buffer and those - // same bytes are passed to the parser again when more input is available. - // 'data' may be an empty slice, but the slices contained are never empty. - // - // OnData() returns an operation and the number of bytes ('N') the operation applies. - // The possible values for 'op' are: - // 'MORE' - Data currently in 'data' is to be retained by the datapath and passed - // again to OnData() after 'N' bytes more data is available. - // 'PASS' - Allow 'N' bytes. - // 'DROP' - Drop 'N' bytes and call OnData() again for the remaining data. - // 'INJECT' - Insert 'N' bytes of data placed into the inject buffer in to the - // data stream in this direction. - // 'NOP' - Do nothing, to be used when it is known if no more input - // is to be expected. - // 'ERROR' - Protocol parsing failed and the connection should be closed. - // - // OnData() is called again after 'PASS', 'DROP', and 'INJECT' with the remaining - // data even if none remains. - OnData(reply bool, reader *Reader) (op OpType, N int) -} - -type ParserFactory interface { - Create(connection *Connection) interface{} // must be thread safe! -} - -// const after initialization -var parserFactories map[string]ParserFactory = make(map[string]ParserFactory) - -// RegisterParserFactory adds a protocol parser factory to the map of known parsers. -// This is called from parser init() functions while we are still single-threaded -func RegisterParserFactory(name string, parserFactory ParserFactory) { - logrus.Debugf("proxylib: Registering L7 parser: %v", name) - parserFactories[name] = parserFactory -} - -func GetParserFactory(name string) ParserFactory { - return parserFactories[name] -} diff --git a/proxylib/proxylib/policymap.go b/proxylib/proxylib/policymap.go deleted file mode 100644 index e3cc702eb..000000000 --- a/proxylib/proxylib/policymap.go +++ /dev/null @@ -1,286 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package proxylib - -import ( - "fmt" - "reflect" - "strings" - - "github.com/sirupsen/logrus" - - cilium "github.com/cilium/proxy/go/cilium/api" - core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3" -) - -// L7NetworkPolicyRule is the interface, which each L7 rule implements this interface -type L7NetworkPolicyRule interface { - Matches(interface{}) bool -} - -// L7RuleParser takes the protobuf and converts the one of relevant for the given L7 to an array -// of L7 rules. A packet matches if the 'Matches' method of any of these rules matches the -// 'l7' interface passed by the L7 implementation to PolicyMap.Matches() as the last parameter. -type L7RuleParser func(rule *cilium.PortNetworkPolicyRule) []L7NetworkPolicyRule - -// const after initialization -var l7RuleParsers map[string]L7RuleParser = make(map[string]L7RuleParser) - -// RegisterL7RuleParser adds a l7 policy protocol parser to the map of known l7 policy parsers. -// This is called from parser init() functions while we are still single-threaded -func RegisterL7RuleParser(l7PolicyTypeName string, parserFunc L7RuleParser) { - logrus.Debugf("NPDS: Registering L7 rule parser: %s", l7PolicyTypeName) - l7RuleParsers[l7PolicyTypeName] = parserFunc -} - -// ParseError may be issued by Policy parsing code. The policy configuration change will -// be graciously rejected by recovering from the panic. -func ParseError(reason string, config interface{}) { - panic(fmt.Errorf("NPDS: %s (config: %v)", reason, config)) -} - -type PortNetworkPolicyRule struct { - Deny bool - Remotes map[uint32]struct{} - L7Rules []L7NetworkPolicyRule // only used when not denied -} - -func newPortNetworkPolicyRule(config *cilium.PortNetworkPolicyRule) (PortNetworkPolicyRule, string, bool) { - rule := PortNetworkPolicyRule{ - Deny: config.GetDeny(), - Remotes: make(map[uint32]struct{}, len(config.RemotePolicies)), - } - action := "Allowing" - if rule.Deny { - action = "Denying" - } - for _, remote := range config.GetRemotePolicies() { - logrus.Debugf("NPDS::PortNetworkPolicyRule: %s remote %d", action, remote) - rule.Remotes[remote] = struct{}{} - } - - // Each parser registers a parsing function to parse it's L7 rules - // The registered name must match 'l7_proto', if included in the message, - // or one of the oneof type names - l7Name := config.L7Proto - if l7Name == "" { - typeOf := reflect.TypeOf(config.L7) - if typeOf != nil { - l7Name = typeOf.Elem().Name() - } - } - if strings.HasPrefix(l7Name, "envoy.") { - return rule, "", false // Silently drop Envoy filter traffic to this port if forwarded to proxylib - } - if l7Name != "" { - l7Parser, ok := l7RuleParsers[l7Name] - if ok { - if logrus.IsLevelEnabled(logrus.DebugLevel) { - logrus.Debugf("NPDS::PortNetworkPolicyRule: Calling L7Parser %s on %v", l7Name, config.String()) - } - rule.L7Rules = l7Parser(config) - } else { - logrus.Debugf("NPDS::PortNetworkPolicyRule: Unknown L7 (%s), should drop everything.", l7Name) - } - // Unknown parsers are expected, but will result in drop-all policy - return rule, l7Name, ok - } - return rule, "", true // No L7 is ok -} - -func (p *PortNetworkPolicyRule) Matches(remoteId uint32, l7 interface{}) (allowed, denied bool) { - // Remote ID must match if we have any. - if len(p.Remotes) > 0 { - _, found := p.Remotes[remoteId] - if !found { - if logrus.IsLevelEnabled(logrus.DebugLevel) { - logrus.Debugf("NPDS::PortNetworkPolicyRule: No L3 match on (%v)", *p) - } - // no remote ID match, does not allow or deny explicitly - return false, false - } - if p.Deny { - // Explicit deny, not allowed even if another rule would allow. - return false, true - } - } else if p.Deny { - // Deny with empty remotes denies all remotes explicitly - return false, true - } - if len(p.L7Rules) > 0 { - for _, rule := range p.L7Rules { - if rule.Matches(l7) { - if logrus.IsLevelEnabled(logrus.DebugLevel) { - logrus.Debugf("NPDS::PortNetworkPolicyRule: L7 rule matches (%v)", *p) - } - return true, false - } - } - return false, false - } - // Empty set matches any payload - if logrus.IsLevelEnabled(logrus.DebugLevel) { - logrus.Debugf("NPDS::PortNetworkPolicyRule: Empty L7Rules matches (%v)", *p) - } - return true, false -} - -type PortNetworkPolicyRules struct { - Rules []PortNetworkPolicyRule -} - -func newPortNetworkPolicyRules(config []*cilium.PortNetworkPolicyRule, port uint32) (PortNetworkPolicyRules, bool) { - rules := PortNetworkPolicyRules{ - Rules: make([]PortNetworkPolicyRule, 0, len(config)), - } - if len(config) == 0 { - logrus.Debugf("NPDS::PortNetworkPolicyRules: No rules, will allow everything.") - } - var firstTypeName string - for _, rule := range config { - newRule, typeName, ok := newPortNetworkPolicyRule(rule) - if !ok { - // Unknown L7 parser, must drop all traffic - return PortNetworkPolicyRules{}, false - } - if typeName != "" { - if firstTypeName == "" { - firstTypeName = typeName - } else if typeName != firstTypeName { - ParseError("Mismatching L7 types on the same port", config) - } - } - rules.Rules = append(rules.Rules, newRule) - } - return rules, true -} - -func (p *PortNetworkPolicyRules) Matches(remoteId uint32, l7 interface{}) bool { - // Empty set matches any payload from anyone - if len(p.Rules) == 0 { - if logrus.IsLevelEnabled(logrus.DebugLevel) { - logrus.Debugf("NPDS::PortNetworkPolicyRules: No Rules; matches (%v)", p) - } - return true - } - var allowed bool - for _, rule := range p.Rules { - allow, deny := rule.Matches(remoteId, l7) - if deny { - // explicit deny - return false - } - if allow { - // allowed if no other rule denies - allowed = true - } - } - if allowed { - if logrus.IsLevelEnabled(logrus.DebugLevel) { - logrus.Debugf("NPDS::PortNetworkPolicyRules(remoteId=%d): rule matches (%v)", remoteId, p) - } - return true - } - return false -} - -type PortNetworkPolicies struct { - Rules map[uint32]PortNetworkPolicyRules -} - -func newPortNetworkPolicies(config []*cilium.PortNetworkPolicy, dir string) PortNetworkPolicies { - policy := PortNetworkPolicies{ - Rules: make(map[uint32]PortNetworkPolicyRules, len(config)), - } - for _, rule := range config { - // Ignore UDP policies - if rule.GetProtocol() == core.SocketAddress_UDP { - continue - } - - port := rule.GetPort() - if _, found := policy.Rules[port]; found { - ParseError(fmt.Sprintf("Duplicate port number %d in (rule: %v)", port, rule), config) - } - - if rule.GetProtocol() != core.SocketAddress_TCP { - ParseError(fmt.Sprintf("Invalid transport protocol %v", rule.GetProtocol()), config) - } - - // Skip the port if not 'ok' - rules, ok := newPortNetworkPolicyRules(rule.GetRules(), port) - if ok { - logrus.Debugf("NPDS::PortNetworkPolicies(): installed %s TCP policy for port %d", dir, port) - policy.Rules[port] = rules - } else { - logrus.Debugf("NPDS::PortNetworkPolicies(): Skipped %s port due to unsupported L7: %d", dir, port) - } - } - return policy -} - -func (p *PortNetworkPolicies) Matches(port, remoteId uint32, l7 interface{}) bool { - rules, found := p.Rules[port] - if found { - if rules.Matches(remoteId, l7) { - if logrus.IsLevelEnabled(logrus.DebugLevel) { - logrus.Debugf("NPDS::PortNetworkPolicies(port=%d, remoteId=%d): rule matches (%v)", port, remoteId, p) - } - return true - } - } - // No exact port match, try wildcard - rules, foundWc := p.Rules[0] - if foundWc { - if rules.Matches(remoteId, l7) { - if logrus.IsLevelEnabled(logrus.DebugLevel) { - logrus.Debugf("NPDS::PortNetworkPolicies(port=*, remoteId=%d): rule matches (%v)", remoteId, p) - } - return true - } - } - - // No policy for the port was found. Cilium always creates a policy for redirects it - // creates, so the host proxy never gets here. - // TODO: Change back to false only when non-bpf datapath is supported? - - // logrus.Debugf("NPDS::PortNetworkPolicies(port=%d, remoteId=%d): allowing traffic on port for which there is no policy, assuming L3/L4 has passed it! (%v)", port, remoteId, p) - // return !(found || foundWc) - if !(found || foundWc) { - logrus.Debugf("NPDS::PortNetworkPolicies(port=%d, remoteId=%d): Dropping traffic on port for which there is no policy! (%v)", port, remoteId, p) - } - return false -} - -type PolicyInstance struct { - protobuf *cilium.NetworkPolicy - Ingress PortNetworkPolicies - Egress PortNetworkPolicies -} - -func newPolicyInstance(config *cilium.NetworkPolicy) *PolicyInstance { - logrus.Debugf("NPDS::PolicyInstance: Inserting policy for %v", config.EndpointIps) - return &PolicyInstance{ - protobuf: config, - Ingress: newPortNetworkPolicies(config.GetIngressPerPortPolicies(), "ingress"), - Egress: newPortNetworkPolicies(config.GetEgressPerPortPolicies(), "egress"), - } -} - -func (p *PolicyInstance) Matches(ingress bool, port, remoteId uint32, l7 interface{}) bool { - if logrus.IsLevelEnabled(logrus.DebugLevel) { - logrus.Debugf("NPDS::PolicyInstance::Matches(ingress: %v, port: %d, remoteId: %d, l7: %v (policy: %s)", ingress, port, remoteId, l7, p.protobuf.String()) - } - if ingress { - return p.Ingress.Matches(port, remoteId, l7) - } - return p.Egress.Matches(port, remoteId, l7) -} - -// Network policies keyed by endpoint IPs -type PolicyMap map[string]*PolicyInstance - -func newPolicyMap() PolicyMap { - return make(PolicyMap) -} diff --git a/proxylib/proxylib/reader.go b/proxylib/proxylib/reader.go deleted file mode 100644 index 8a26db630..000000000 --- a/proxylib/proxylib/reader.go +++ /dev/null @@ -1,92 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package proxylib - -import ( - "io" -) - -type Reader struct { - buf [][]byte // buffer that shrinks as data is being read - read int // Number of byte read since last reset - endStream bool // connection is known to end (in this direction) after the current input -} - -func NewReader(input [][]byte, endStream bool) Reader { - return Reader{ - buf: input, - endStream: endStream, - } -} - -func (r *Reader) Reset() int { - read := r.read - r.read = 0 - return read -} - -func (r *Reader) Length() int { - length := 0 - for i := 0; i < len(r.buf); i++ { - length += len(r.buf[i]) - } - return length -} - -func (r *Reader) PeekFull(p []byte) (n int, err error) { - n = 0 - slice := 0 - index := 0 - for n < len(p) && slice < len(r.buf) { - bytes := len(r.buf[slice][index:]) - nc := copy(p[n:], r.buf[slice][index:]) - if nc == bytes { - // next slice please - slice++ - index = 0 - } else { - // move ahead in the same slice - index += nc - } - n += nc - } - if n < len(p) { - return n, io.EOF - } - return n, nil -} - -func (r *Reader) Read(p []byte) (n int, err error) { - n = 0 - for n < len(p) && len(r.buf) > 0 { - nc := copy(p[n:], r.buf[0]) - if nc == len(r.buf[0]) { - // next slice please - r.buf = r.buf[1:] - } else { - // move ahead in the same slice - r.buf[0] = r.buf[0][nc:] - } - n += nc - } - if n == 0 { - return 0, io.EOF - } - r.read += n - return n, nil -} - -// Skip bytes in input, or exhaust the input. -func (r *Reader) AdvanceInput(bytes int) { - for bytes > 0 && len(r.buf) > 0 { - rem := len(r.buf[0]) // this much data left in the first slice - if bytes < rem { - r.buf[0] = r.buf[0][bytes:] // skip 'bytes' bytes - return - } else { // go to the beginning of the next unit - bytes -= rem - r.buf = r.buf[1:] // may result in an empty slice - } - } -} diff --git a/proxylib/proxylib/test_util.go b/proxylib/proxylib/test_util.go deleted file mode 100644 index a546fc658..000000000 --- a/proxylib/proxylib/test_util.go +++ /dev/null @@ -1,122 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package proxylib - -import ( - "testing" - - "google.golang.org/protobuf/encoding/prototext" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/known/anypb" - "github.com/sirupsen/logrus" - "github.com/stretchr/testify/require" - - envoy_service_discovery "github.com/envoyproxy/go-control-plane/envoy/service/discovery/v3" - - cilium "github.com/cilium/proxy/go/cilium/api" -) - -var LogFatal = func(format string, args ...interface{}) { - logrus.Fatalf(format, args...) -} - -func (ins *Instance) CheckInsertPolicyText(tb testing.TB, version string, policies []string) { - err := ins.InsertPolicyText(version, policies, "") - require.NoError(tb, err) -} - -func (ins *Instance) InsertPolicyText(version string, policies []string, expectFail string) error { - typeUrl := "type.googleapis.com/cilium.NetworkPolicy" - resources := make([]*anypb.Any, 0, len(policies)) - - for _, policy := range policies { - pb := new(cilium.NetworkPolicy) - err := prototext.Unmarshal([]byte(policy), pb) - if err != nil { - if expectFail != "unmarshal" { - LogFatal("Policy UnmarshalText failed: %v", err) - } - return err - } - logrus.Debugf("Text -> proto.Message: %s -> %v", policy, pb) - data, err := proto.Marshal(pb) - if err != nil { - if expectFail != "marshal" { - LogFatal("Policy marshal failed: %v", err) - } - return err - } - - resources = append(resources, &anypb.Any{ - TypeUrl: typeUrl, - Value: data, - }) - } - - msg := &envoy_service_discovery.DiscoveryResponse{ - VersionInfo: version, - Canary: false, - TypeUrl: typeUrl, - Nonce: "randomNonce1", - Resources: resources, - } - - err := ins.PolicyUpdate(msg) - if err != nil { - if expectFail != "update" { - LogFatal("Policy Update failed: %v", err) - } - } - return err -} - -var connectionID uint64 - -func (ins *Instance) CheckNewConnectionOK(tb testing.TB, proto string, ingress bool, srcId, dstId uint32, srcAddr, dstAddr, policyName string) *Connection { - err, conn := ins.CheckNewConnection(proto, ingress, srcId, dstId, srcAddr, dstAddr, policyName) - require.NoError(tb, err) - require.NotNil(tb, conn) - return conn -} - -func (ins *Instance) CheckNewConnection(proto string, ingress bool, srcId, dstId uint32, srcAddr, dstAddr, policyName string) (error, *Connection) { - connectionID++ - bufSize := 1024 - origBuf := make([]byte, 0, bufSize) - replyBuf := make([]byte, 0, bufSize) - - return NewConnection(ins, proto, connectionID, ingress, srcId, dstId, srcAddr, dstAddr, policyName, &origBuf, &replyBuf) -} - -func (conn *Connection) CheckOnDataOK(tb testing.TB, reply, endStream bool, data *[][]byte, expReplyBuf []byte, expOps ...interface{}) { - conn.CheckOnData(tb, reply, endStream, data, OK, expReplyBuf, expOps...) -} - -func (conn *Connection) CheckOnData(tb testing.TB, reply, endStream bool, data *[][]byte, expResult FilterResult, expReplyBuf []byte, expOps ...interface{}) { - ops := make([][2]int64, 0, len(expOps)/2) - - res := conn.OnData(reply, endStream, data, &ops) - require.Equal(tb, expResult, res) - require.Equal(tb, len(expOps)/2, len(ops), "Unexpected number of filter operations") - - for i, op := range ops { - if i*2+1 < len(expOps) { - expOp, ok := expOps[i*2].(OpType) - require.Truef(tb, ok, "Invalid expected operation type") - require.Equal(tb, int64(expOp), op[0], "Unexpected filter operation") - expN, ok := expOps[i*2+1].(int) - require.Truef(tb, ok, "Invalid expected operation length (must be int)") - require.Equal(tb, int64(expN), op[1], "Unexpected operation length") - } - } - - buf := conn.ReplyBuf - require.ElementsMatch(tb, expReplyBuf, *buf) - *buf = (*buf)[:0] // make empty again - - // Clear the same-direction inject buffer, simulating the datapath forwarding the injected data - injectBuf := conn.getInjectBuf(reply) - *injectBuf = (*injectBuf)[:0] - logrus.Debugf("proxylib test helper: Cleared inject buf, used %d/%d", len(*injectBuf), cap(*injectBuf)) -} diff --git a/proxylib/proxylib/types.go b/proxylib/proxylib/types.go deleted file mode 100644 index ddbd736f3..000000000 --- a/proxylib/proxylib/types.go +++ /dev/null @@ -1,97 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package proxylib - -import "fmt" - -// OpType mirrors enum FilterOpType in types.h. -type OpType int64 - -const ( - MORE OpType = iota - PASS - DROP - INJECT - ERROR - - // Internal types not exposed to Caller - NOP OpType = 256 -) - -// OpError mirrors enum FilterOpError in types.h. -type OpError int64 - -const ( - ERROR_INVALID_OP_LENGTH OpError = iota + 1 - ERROR_INVALID_FRAME_TYPE - ERROR_INVALID_FRAME_LENGTH -) - -func (op OpType) String() string { - switch op { - case MORE: - return "MORE" - case PASS: - return "PASS" - case DROP: - return "DROP" - case INJECT: - return "INJECT" - case ERROR: - return "ERROR" - case NOP: - return "NOP" - } - return "UNKNOWN_OP" -} - -func (opErr OpError) String() string { - switch opErr { - case ERROR_INVALID_OP_LENGTH: - return "ERROR_INVALID_OP_LENGTH" - case ERROR_INVALID_FRAME_TYPE: - return "ERROR_INVALID_FRAME_TYPE" - case ERROR_INVALID_FRAME_LENGTH: - return "ERROR_INVALID_FRAME_LENGTH" - } - return "UNKNOWN_OP_ERROR" -} - -// FilterResult mirrors enum FilterResult in types.h. -type FilterResult int - -const ( - OK FilterResult = iota - POLICY_DROP - PARSER_ERROR - UNKNOWN_PARSER - UNKNOWN_CONNECTION - INVALID_ADDRESS - INVALID_INSTANCE - UNKNOWN_ERROR -) - -// Error() implements the error interface for FilterResult -func (r FilterResult) Error() string { - switch r { - case OK: - return "OK" - case POLICY_DROP: - return "POLICY_DROP" - case PARSER_ERROR: - return "PARSER_ERROR" - case UNKNOWN_PARSER: - return "UNKNOWN_PARSER" - case UNKNOWN_CONNECTION: - return "UNKNOWN_CONNECTION" - case INVALID_ADDRESS: - return "INVALID_ADDRESS" - case INVALID_INSTANCE: - return "INVALID_INSTANCE" - case UNKNOWN_ERROR: - return "UNKNOWN_ERROR" - } - - return fmt.Sprintf("%d", r) -} diff --git a/proxylib/r2d2/r2d2parser.go b/proxylib/r2d2/r2d2parser.go deleted file mode 100644 index 57df43e1a..000000000 --- a/proxylib/r2d2/r2d2parser.go +++ /dev/null @@ -1,196 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package r2d2 - -import ( - "bytes" - "fmt" - "regexp" - "strings" - - "github.com/sirupsen/logrus" - - cilium "github.com/cilium/proxy/go/cilium/api" - "github.com/cilium/proxy/proxylib/proxylib" -) - -// -// R2D2 Parser -// -// This is a toy protocol to teach people how to build a Cilium golang proxy parser. -// - -// Current R2D2 parser supports filtering on a basic text protocol with 4 request-types: -// "READ \r\n" - Read a file from the Droid -// "WRITE \r\n" - Write a file to the Droid -// "HALT\r\n" - Shutdown the Droid -// "RESET\r\n" - Reset the Droid to factory settings -// -// Replies include a status of either "OK\r\n", "ERROR\r\n" for "WRITE", "HALT", or "RESET". -// Replies for "READ" are either "OK \r\n" or "ERROR\r\n". -// -// -// Policy Examples: -// {cmd : "READ"} - Allow all reads, no other commands. -// {cmd : "READ", file : "/public/.*" } - Allow reads that are in the public directory -// {file : "/public/.*" } - Allow read/write on the public directory. -// {cmd : "HALT"} - Allow shutdown, but no other actions. - -type r2d2Rule struct { - cmdExact string - fileRegexCompiled *regexp.Regexp -} - -type r2d2RequestData struct { - cmd string - file string -} - -func (rule *r2d2Rule) Matches(data interface{}) bool { - // Cast 'data' to the type we give to 'Matches()' - - reqData, ok := data.(r2d2RequestData) - regexStr := "" - if rule.fileRegexCompiled != nil { - regexStr = rule.fileRegexCompiled.String() - } - - if !ok { - logrus.Warning("Matches() called with type other than R2d2RequestData") - return false - } - if len(rule.cmdExact) > 0 && rule.cmdExact != reqData.cmd { - logrus.Debugf("R2d2Rule: cmd mismatch %s, %s", rule.cmdExact, reqData.cmd) - return false - } - if rule.fileRegexCompiled != nil && - !rule.fileRegexCompiled.MatchString(reqData.file) { - logrus.Debugf("R2d2Rule: file mismatch %s, %s", rule.fileRegexCompiled.String(), reqData.file) - return false - } - logrus.Debugf("policy match for rule: '%s' '%s'", rule.cmdExact, regexStr) - return true -} - -// ruleParser parses protobuf L7 rules to enforcement objects -// May panic -func ruleParser(rule *cilium.PortNetworkPolicyRule) []proxylib.L7NetworkPolicyRule { - l7Rules := rule.GetL7Rules() - if l7Rules == nil { - return nil - } - - allowRules := l7Rules.GetL7AllowRules() - rules := make([]proxylib.L7NetworkPolicyRule, 0, len(allowRules)) - for _, l7Rule := range allowRules { - var rr r2d2Rule - for k, v := range l7Rule.Rule { - switch k { - case "cmd": - rr.cmdExact = v - case "file": - if v != "" { - rr.fileRegexCompiled = regexp.MustCompile(v) - } - default: - proxylib.ParseError(fmt.Sprintf("Unsupported key: %s", k), rule) - } - } - if rr.cmdExact != "" && - rr.cmdExact != "READ" && - rr.cmdExact != "WRITE" && - rr.cmdExact != "HALT" && - rr.cmdExact != "RESET" { - proxylib.ParseError(fmt.Sprintf("Unable to parse L7 r2d2 rule with invalid cmd: '%s'", rr.cmdExact), rule) - } - if (rr.fileRegexCompiled != nil) && !(rr.cmdExact == "" || rr.cmdExact == "READ" || rr.cmdExact == "WRITE") { - proxylib.ParseError(fmt.Sprintf("Unable to parse L7 r2d2 rule, cmd '%s' is not compatible with 'file'", rr.cmdExact), rule) - } - regexStr := "" - if rr.fileRegexCompiled != nil { - regexStr = rr.fileRegexCompiled.String() - } - logrus.Debugf("Parsed rule '%s' '%s'", rr.cmdExact, regexStr) - rules = append(rules, &rr) - } - return rules -} - -type factory struct{} - -func init() { - logrus.Debug("init(): Registering r2d2ParserFactory") - proxylib.RegisterParserFactory("r2d2", &factory{}) - proxylib.RegisterL7RuleParser("r2d2", ruleParser) -} - -type parser struct { - connection *proxylib.Connection -} - -func (f *factory) Create(connection *proxylib.Connection) interface{} { - logrus.Debugf("R2d2ParserFactory: Create: %v", connection) - - return &parser{connection: connection} -} - -func (p *parser) OnData(reply, endStream bool, dataArray [][]byte) (proxylib.OpType, int) { - - // inefficient, but simple - data := string(bytes.Join(dataArray, []byte{})) - - logrus.Debugf("OnData: '%s'", data) - msgLen := strings.Index(data, "\r\n") - if msgLen < 0 { - // No delimiter, request more data - logrus.Debugf("No delimiter found, requesting more bytes") - return proxylib.MORE, 1 - } - - msgStr := data[:msgLen] // read single request - msgLen += 2 // include "\r\n" - logrus.Debugf("Request = '%s'", msgStr) - - // we don't process reply traffic for now - if reply { - logrus.Debugf("reply, passing %d bytes", msgLen) - return proxylib.PASS, msgLen - } - - fields := strings.Split(msgStr, " ") - if len(fields) < 1 { - return proxylib.ERROR, int(proxylib.ERROR_INVALID_FRAME_TYPE) - } - reqData := r2d2RequestData{cmd: fields[0]} - if len(fields) == 2 { - reqData.file = fields[1] - } - - matches := true - access_log_entry_type := cilium.EntryType_Request - - if !p.connection.Matches(reqData) { - matches = false - access_log_entry_type = cilium.EntryType_Denied - } - - p.connection.Log(access_log_entry_type, - &cilium.LogEntry_GenericL7{ - GenericL7: &cilium.L7LogEntry{ - Proto: "r2d2", - Fields: map[string]string{ - "cmd": reqData.cmd, - "file": reqData.file, - }, - }, - }) - - if !matches { - p.connection.Inject(true, []byte("ERROR\r\n")) - logrus.Debugf("Policy mismatch, dropping %d bytes", msgLen) - return proxylib.DROP, msgLen - } - - return proxylib.PASS, msgLen -} diff --git a/proxylib/r2d2/r2d2parser_test.go b/proxylib/r2d2/r2d2parser_test.go deleted file mode 100644 index 7c05f842d..000000000 --- a/proxylib/r2d2/r2d2parser_test.go +++ /dev/null @@ -1,149 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package r2d2 - -import ( - "testing" - - "github.com/stretchr/testify/require" - - "github.com/cilium/proxy/proxylib/accesslog" - "github.com/cilium/proxy/proxylib/proxylib" - "github.com/cilium/proxy/proxylib/test" -) - -type R2d2Suite struct { - logServer *test.AccessLogServer - ins *proxylib.Instance -} - -// Set up access log server and Library instance for all the test cases -func setUpR2d2Suite(tb testing.TB) *R2d2Suite { - s := &R2d2Suite{} - s.logServer = test.StartAccessLogServer("access_log.sock", 10) - require.NotNil(tb, s.logServer) - s.ins = proxylib.NewInstance("node1", accesslog.NewClient(s.logServer.Path)) - require.NotNil(tb, s.ins) - tb.Cleanup(func() { - s.logServer.Clear() - s.logServer.Close() - }) - return s -} - -func TestR2d2OnDataIncomplete(t *testing.T) { - s := setUpR2d2Suite(t) - conn := s.ins.CheckNewConnectionOK(t, "r2d2", true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "no-policy") - data := [][]byte{[]byte("READ xssss")} - conn.CheckOnDataOK(t, false, false, &data, []byte{}, proxylib.MORE, 1) -} - -func TestR2d2OnDataBasicPass(t *testing.T) { - s := setUpR2d2Suite(t) - // allow all rule - s.ins.CheckInsertPolicyText(t, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2 - ingress_per_port_policies: < - port: 80 - rules: < - l7_proto: "r2d2" - > - > - `}) - conn := s.ins.CheckNewConnectionOK(t, "r2d2", true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1") - msg1 := "READ sssss\r\n" - msg2 := "WRITE sssss\r\n" - msg3 := "HALT\r\n" - msg4 := "RESET\r\n" - data := [][]byte{[]byte(msg1 + msg2 + msg3 + msg4)} - conn.CheckOnDataOK(t, false, false, &data, []byte{}, - proxylib.PASS, len(msg1), - proxylib.PASS, len(msg2), - proxylib.PASS, len(msg3), - proxylib.PASS, len(msg4), - proxylib.MORE, 1) -} - -func TestR2d2OnDataMultipleReq(t *testing.T) { - s := setUpR2d2Suite(t) - // allow all rule - s.ins.CheckInsertPolicyText(t, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2 - ingress_per_port_policies: < - port: 80 - rules: < - l7_proto: "r2d2" - > - > - `}) - conn := s.ins.CheckNewConnectionOK(t, "r2d2", true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1") - msg1Part1 := "RE" - msg1Part2 := "SET\r\n" - data := [][]byte{[]byte(msg1Part1), []byte(msg1Part2)} - conn.CheckOnDataOK(t, false, false, &data, []byte{}, - proxylib.PASS, len(msg1Part1+msg1Part2), - proxylib.MORE, 1) -} - -func TestR2d2OnDataAllowDenyCmd(t *testing.T) { - s := setUpR2d2Suite(t) - s.ins.CheckInsertPolicyText(t, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2 - ingress_per_port_policies: < - port: 80 - rules: < - l7_proto: "r2d2" - l7_rules: < - l7_allow_rules: < - rule: < - key: "cmd" - value: "READ" - > - > - > - > - > - `}) - conn := s.ins.CheckNewConnectionOK(t, "r2d2", true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1") - msg1 := "READ xssss\r\n" - msg2 := "WRITE xssss\r\n" - data := [][]byte{[]byte(msg1 + msg2)} - conn.CheckOnDataOK(t, false, false, &data, []byte("ERROR\r\n"), - proxylib.PASS, len(msg1), - proxylib.DROP, len(msg2), - proxylib.MORE, 1) -} - -func (s *R2d2Suite) TestR2d2OnDataAllowDenyRegex(t *testing.T) { - - s.ins.CheckInsertPolicyText(t, "1", []string{` - endpoint_ips: "1.1.1.1" - endpoint_id: 2 - ingress_per_port_policies: < - port: 80 - rules: < - l7_proto: "r2d2" - l7_rules: < - l7_allow_rules: < - rule: < - key: "file" - value: "s.*" - > - > - > - > - > - `}) - conn := s.ins.CheckNewConnectionOK(t, "r2d2", true, 1, 2, "1.1.1.1:34567", "10.0.0.2:80", "1.1.1.1") - msg1 := "READ ssss\r\n" - msg2 := "WRITE yyyyy\r\n" - data := [][]byte{[]byte(msg1 + msg2)} - conn.CheckOnDataOK(t, false, false, &data, []byte("ERROR\r\n"), - proxylib.PASS, len(msg1), - proxylib.DROP, len(msg2), - proxylib.MORE, 1) -} diff --git a/proxylib/test/accesslog_server.go b/proxylib/test/accesslog_server.go deleted file mode 100644 index 52de015ea..000000000 --- a/proxylib/test/accesslog_server.go +++ /dev/null @@ -1,169 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package test - -import ( - "errors" - "io" - "net" - "os" - "path/filepath" - "sync" - "syscall" - "time" - - "google.golang.org/protobuf/proto" - "github.com/sirupsen/logrus" - "golang.org/x/sys/unix" - - cilium "github.com/cilium/proxy/go/cilium/api" -) - -type AccessLogServer struct { - Path string - Logs chan cilium.EntryType - done chan struct{} - listener *net.UnixListener - mu sync.Mutex // protects conns - conns []*net.UnixConn -} - -// Close removes the unix domain socket from the filesystem -func (s *AccessLogServer) Close() { - if s != nil { - close(s.done) - s.listener.Close() - s.mu.Lock() - for _, conn := range s.conns { - conn.Close() - } - s.mu.Unlock() - os.Remove(s.Path) - } -} - -func (s *AccessLogServer) isClosing() bool { - select { - case <-s.done: - return true - default: - return false - } -} - -// Clear empties the access log server buffer, counting the passes and drops -func (s *AccessLogServer) Clear() (passed, drops int) { - passes, drops := 0, 0 - empty := false - for !empty { - select { - case entryType := <-s.Logs: - if entryType == cilium.EntryType_Denied { - drops++ - } else { - passes++ - } - case <-time.After(10 * time.Millisecond): - empty = true - } - } - return passes, drops -} - -// StartAccessLogServer starts the access log server. -func StartAccessLogServer(accessLogName string, bufSize int) *AccessLogServer { - accessLogPath := filepath.Join(Tmpdir, accessLogName) - - server := &AccessLogServer{ - Path: accessLogPath, - Logs: make(chan cilium.EntryType, bufSize), - done: make(chan struct{}), - } - - // Create the access log listener - os.Remove(accessLogPath) // Remove/Unlink the old unix domain socket, if any. - var err error - server.listener, err = net.ListenUnix("unixpacket", &net.UnixAddr{Name: accessLogPath, Net: "unixpacket"}) - if err != nil { - logrus.Fatalf("Failed to open access log listen socket at %s: %v", accessLogPath, err) - } - server.listener.SetUnlinkOnClose(true) - - // Make the socket accessible by non-root Envoy proxies. - if err = os.Chmod(accessLogPath, 0777); err != nil { - logrus.Fatalf("Failed to change mode of access log listen socket at %s: %v", accessLogPath, err) - } - - logrus.Debug("Starting Access Log Server") - go func() { - for { - // Each Envoy listener opens a new connection over the Unix domain socket. - // Multiple worker threads serving the listener share that same connection - uc, err := server.listener.AcceptUnix() - if err != nil { - // These errors are expected when we are closing down - if server.isClosing() || - errors.Is(err, net.ErrClosed) || - errors.Is(err, syscall.EINVAL) { - break - } - logrus.WithError(err).Warn("Failed to accept access log connection") - continue - } - - if server.isClosing() { - break - } - - logrus.Debug("Accepted access log connection") - - server.mu.Lock() - server.conns = append(server.conns, uc) - server.mu.Unlock() - // Serve this access log socket in a goroutine, so we can serve multiple - // connections concurrently. - go server.accessLogger(uc) - } - }() - - return server -} - -// isEOF returns true if the error message ends in "EOF". ReadMsgUnix returns extra info in the beginning. -func isEOF(err error) bool { - strerr := err.Error() - errlen := len(strerr) - return errlen >= 3 && strerr[errlen-3:] == io.EOF.Error() -} - -func (s *AccessLogServer) accessLogger(conn *net.UnixConn) { - defer func() { - logrus.Debug("Closing access log connection") - conn.Close() - }() - - buf := make([]byte, 4096) - for { - n, _, flags, _, err := conn.ReadMsgUnix(buf, nil) - if err != nil { - if !isEOF(err) && !s.isClosing() { - logrus.WithError(err).Error("Error while reading from access log connection") - } - break - } - if flags&unix.MSG_TRUNC != 0 { - logrus.Warning("Discarded truncated access log message") - continue - } - pblog := cilium.LogEntry{} - err = proto.Unmarshal(buf[:n], &pblog) - if err != nil { - logrus.WithError(err).Warning("Discarded invalid access log message") - continue - } - - logrus.Debugf("Access log message: %s", pblog.String()) - s.Logs <- pblog.EntryType - } -} diff --git a/proxylib/test/tmpdir.go b/proxylib/test/tmpdir.go deleted file mode 100644 index ce96347ec..000000000 --- a/proxylib/test/tmpdir.go +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package test - -import ( - "os" - - "github.com/sirupsen/logrus" -) - -var Tmpdir string - -func init() { - var err error - Tmpdir, err = os.MkdirTemp("", "cilium_envoy_go_test") - if err != nil { - logrus.Fatal("Failed to create a temporary directory for testing") - } -} diff --git a/proxylib/testparsers/blockparser.go b/proxylib/testparsers/blockparser.go deleted file mode 100644 index fea26e00e..000000000 --- a/proxylib/testparsers/blockparser.go +++ /dev/null @@ -1,178 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package testparsers - -import ( - "bytes" - "fmt" - "math" - "strconv" - - "github.com/sirupsen/logrus" - - cilium "github.com/cilium/proxy/go/cilium/api" - . "github.com/cilium/proxy/proxylib/proxylib" -) - -// -// Block parser used for testing -// - -type BlockParserFactory struct{} - -var blockParserFactory *BlockParserFactory - -const ( - blockParserName = "test.blockparser" -) - -func init() { - logrus.Debug("init(): Registering blockParserFactory") - RegisterParserFactory(blockParserName, blockParserFactory) -} - -type BlockParser struct { - connection *Connection - inserted int -} - -func (p *BlockParserFactory) Create(connection *Connection) interface{} { - logrus.Debugf("BlockParserFactory: Create: %v", connection) - return &BlockParser{connection: connection} -} - -func getBlock(data [][]byte) ([]byte, int, int, error) { - var block bytes.Buffer - - offset := 0 - blockLen := 0 - haveLength := false - missing := 0 - - for _, s := range data { - if !haveLength { - index := bytes.IndexByte(s[offset:], ':') - if index < 0 { - block.Write(s[offset:]) - if block.Len() > 0 { - missing = 1 // require at least one more if something was received - } - } else { - block.Write(s[offset : offset+index]) - offset += index - - // Now 'block' contains everything before the ':', parse it as a decimal number - // indicating the length of the frame AFTER the ':' - if lenUint64, err := strconv.ParseUint(block.String(), 10, 64); err != nil { - return block.Bytes(), 0, 0, err - } else if lenUint64 > math.MaxInt { - return block.Bytes(), 0, 0, fmt.Errorf("block length overflow") - } else { - blockLen = int(lenUint64) - } - if blockLen <= block.Len() { - return block.Bytes(), 0, 0, fmt.Errorf("block length too short") - } - haveLength = true - missing = blockLen - block.Len() - } - } - if haveLength { - s_len := len(s) - offset - - if missing <= s_len { - block.Write(s[offset : offset+missing]) - return block.Bytes(), blockLen, 0, nil - } else { - block.Write(s[offset:]) - missing -= s_len - } - } - offset = 0 - } - - return block.Bytes(), blockLen, missing, nil -} - -// Parses individual blocks that must start with one of: -// "PASS" the block is passed -// "DROP" the block is dropped -// "INJECT" the block is injected in reverse direction -// "INSERT" the block is injected in current direction -func (p *BlockParser) OnData(reply, endStream bool, data [][]byte) (OpType, int) { - block, block_len, missing, err := getBlock(data) - if err != nil { - logrus.WithError(err).Warnf("BlockParser: Invalid frame length") - return ERROR, int(ERROR_INVALID_FRAME_LENGTH) - } - - if p.inserted > 0 { - if p.inserted == block_len { - p.inserted = 0 - return DROP, block_len - } - // partial insert in progress - n := p.connection.Inject(reply, []byte(block)[p.inserted:]) - // Drop the INJECT block in the current direction - p.inserted += n - return INJECT, n - } - - if !reply { - logrus.Debugf("BlockParser: Request: %s", block) - } else { - logrus.Debugf("BlockParser: Response: %s", block) - } - - if missing == 0 && block_len == 0 { - // Nothing received, don't know if more will be coming; do nothing - return NOP, 0 - } - - logrus.Debugf("BlockParser: missing: %d", missing) - - if bytes.Contains(block, []byte("PASS")) { - p.connection.Log(cilium.EntryType_Request, &cilium.LogEntry_GenericL7{ - GenericL7: &cilium.L7LogEntry{ - Proto: blockParserName, - Fields: map[string]string{ - "status": "200", - }, - }, - }) - return PASS, block_len - } - if bytes.Contains(block, []byte("DROP")) { - p.connection.Log(cilium.EntryType_Denied, &cilium.LogEntry_GenericL7{ - GenericL7: &cilium.L7LogEntry{ - Proto: blockParserName, - Fields: map[string]string{ - "status": "503", - }, - }, - }) - return DROP, block_len - } - - if missing > 0 { - // Partial block received, ask for more - return MORE, missing - } - - if bytes.Contains(block, []byte("INJECT")) { - // Inject block in the reverse direction - p.connection.Inject(!reply, []byte(block)) - // Drop the INJECT block in the current direction - return DROP, block_len - } - if bytes.Contains(block, []byte("INSERT")) { - // Inject the block in the current direction - n := p.connection.Inject(reply, []byte(block)) - // Drop the INJECT block in the current direction - p.inserted = n - return INJECT, n - } - - return ERROR, int(ERROR_INVALID_FRAME_TYPE) -} diff --git a/proxylib/testparsers/headerparser.go b/proxylib/testparsers/headerparser.go deleted file mode 100644 index a216f678c..000000000 --- a/proxylib/testparsers/headerparser.go +++ /dev/null @@ -1,159 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -// -// Accompanying file `headerparser.policy` contains an example policy -// for this protocol. Install it with: -// $ cilium policy import proxylib/testparsers/headerparser.policy -// - -package testparsers - -import ( - "bytes" - "fmt" - - "github.com/sirupsen/logrus" - - cilium "github.com/cilium/proxy/go/cilium/api" - . "github.com/cilium/proxy/proxylib/proxylib" -) - -// -// Header parser used for testing -// - -type HeaderRule struct { - hasPrefix []byte - contains []byte - hasSuffix []byte -} - -// Matches returns true if the HeaderRule matches -func (rule *HeaderRule) Matches(data interface{}) bool { - logrus.Debugf("headerparser checking rule %v", *rule) - - // Trim whitespace from both ends - bs := bytes.TrimSpace(data.([]byte)) - - if len(rule.hasPrefix) > 0 && !bytes.HasPrefix(bs, rule.hasPrefix) { - logrus.Debugf("headerparser HasPrefix %s does not match %s", bs, rule.hasPrefix) - return false - } - - if len(rule.contains) > 0 && !bytes.Contains(bs, rule.contains) { - logrus.Debugf("headerparser Contains %s does not match %s", bs, rule.contains) - return false - } - - if len(rule.hasSuffix) > 0 && !bytes.HasSuffix(bs, rule.hasSuffix) { - logrus.Debugf("headerparser HasSuffix %s does not match %s", bs, rule.hasSuffix) - return false - } - logrus.Debug("headerparser rule matched!") - - return true -} - -// L7HeaderRuleParser parses protobuf L7 rules to and array of HeaderRules -func L7HeaderRuleParser(rule *cilium.PortNetworkPolicyRule) []L7NetworkPolicyRule { - l7Rules := rule.GetL7Rules() - if l7Rules == nil { - return nil - } - - allowRules := l7Rules.GetL7AllowRules() - rules := make([]L7NetworkPolicyRule, 0, len(allowRules)) - for _, l7Rule := range allowRules { - var hr HeaderRule - for k, v := range l7Rule.Rule { - switch k { - case "prefix": - hr.hasPrefix = []byte(v) - case "contains": - hr.contains = []byte(v) - case "suffix": - hr.hasSuffix = []byte(v) - default: - ParseError(fmt.Sprintf("Unsupported key: %s", k), rule) - } - } - logrus.Debugf("Parsed HeaderRule pair: %v", hr) - rules = append(rules, &hr) - } - return rules -} - -type HeaderParserFactory struct{} - -var headerParserFactory *HeaderParserFactory - -const ( - parserName = "test.headerparser" -) - -func init() { - logrus.Debug("init(): Registering headerParserFactory") - RegisterParserFactory(parserName, headerParserFactory) - RegisterL7RuleParser(parserName, L7HeaderRuleParser) -} - -type HeaderParser struct { - connection *Connection -} - -func (p *HeaderParserFactory) Create(connection *Connection) interface{} { - logrus.Debugf("HeaderParserFactory: Create: %v", connection) - return &HeaderParser{connection: connection} -} - -// Parses individual lines and verifies them against the policy -func (p *HeaderParser) OnData(reply, endStream bool, data [][]byte) (OpType, int) { - line, ok := getLine(data) - line_len := len(line) - - if !reply { - logrus.Debugf("HeaderParser: Request: %s", line) - } else { - logrus.Debugf("HeaderParser: Response: %s", line) - } - - if !ok { - if line_len > 0 { - // Partial line received, but no newline, ask for more - return MORE, 1 - } else { - // Nothing received, don't know if more will be coming; do nothing - return NOP, 0 - } - } - - // Replies pass unconditionally - if reply || p.connection.Matches(line) { - p.connection.Log(cilium.EntryType_Request, - &cilium.LogEntry_GenericL7{ - GenericL7: &cilium.L7LogEntry{ - Proto: parserName, - Fields: map[string]string{ - "status": "PASS", - }, - }, - }) - return PASS, line_len - } - - // Inject Error response to the reverse direction - p.connection.Inject(!reply, []byte(fmt.Sprintf("Line dropped: %s", line))) - // Drop the line in the current direction - p.connection.Log(cilium.EntryType_Denied, - &cilium.LogEntry_GenericL7{ - GenericL7: &cilium.L7LogEntry{ - Proto: parserName, - Fields: map[string]string{ - "status": "DROP", - }, - }, - }) - - return DROP, line_len -} diff --git a/proxylib/testparsers/headerparser.policy b/proxylib/testparsers/headerparser.policy deleted file mode 100644 index cd9dda63b..000000000 --- a/proxylib/testparsers/headerparser.policy +++ /dev/null @@ -1,36 +0,0 @@ -[{ - "endpointSelector": {"matchLabels":{"id.echoserver":""}}, - "ingress": [{ - "fromEndpoints": [ - {"matchLabels":{"reserved:host":""}}, - {"matchLabels":{"id.client":""}} - ], - "toPorts": [{ - "ports": [{"port": "2701", "protocol": "tcp"}], - "rules": { - "l7proto": "test.headerparser", - "l7": [{ - "prefix": "foo" - }] - } - }] - }] -},{ - "endpointSelector": {"matchLabels":{"id.echoserver":""}}, - "ingress": [{ - "fromEndpoints": [ - {"matchLabels":{"reserved:host":""}}, - {"matchLabels":{"id.client":""}} - ], - "toPorts": [{ - "ports": [{"port": "2701", "protocol": "tcp"}], - "rules": { - "l7proto": "test.headerparser", - "l7": [ - {"prefix": "bar", "contains": "beer"}, - {"suffix": "end"} - ] - } - }] - }] -}] diff --git a/proxylib/testparsers/lineparser.go b/proxylib/testparsers/lineparser.go deleted file mode 100644 index cbff22fa7..000000000 --- a/proxylib/testparsers/lineparser.go +++ /dev/null @@ -1,103 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package testparsers - -import ( - "bytes" - - "github.com/sirupsen/logrus" - - . "github.com/cilium/proxy/proxylib/proxylib" -) - -// -// Line parser used for testing -// - -type LineParserFactory struct{} - -var lineParserFactory *LineParserFactory - -func init() { - logrus.Debug("init(): Registering lineParserFactory") - RegisterParserFactory("test.lineparser", lineParserFactory) -} - -type LineParser struct { - connection *Connection - inserted bool -} - -func (p *LineParserFactory) Create(connection *Connection) interface{} { - logrus.Debugf("LineParserFactory: Create: %v", connection) - return &LineParser{connection: connection} -} - -func getLine(data [][]byte) ([]byte, bool) { - var line bytes.Buffer - for i, s := range data { - index := bytes.IndexByte(s, '\n') - if index < 0 { - line.Write(s) - } else { - logrus.Debugf("getLine: unit: %d length: %d index: %d", i, len(s), index) - line.Write(s[:index+1]) - return line.Bytes(), true - } - } - return line.Bytes(), false -} - -// Parses individual lines that must start with one of: -// "PASS" the line is passed -// "DROP" the line is dropped -// "INJECT" the line is injected in reverse direction -// "INSERT" the line is injected in current direction -func (p *LineParser) OnData(reply, endStream bool, data [][]byte) (OpType, int) { - line, ok := getLine(data) - line_len := len(line) - - if p.inserted { - p.inserted = false - return DROP, line_len - } - - if !reply { - logrus.Debugf("LineParser: Request: %s", line) - } else { - logrus.Debugf("LineParser: Response: %s", line) - } - - if !ok { - if line_len > 0 { - // Partial line received, but no newline, ask for more - return MORE, 1 - } else { - // Nothing received, don't know if more will be coming; do nothing - return NOP, 0 - } - } - - if bytes.HasPrefix(line, []byte("PASS")) { - return PASS, line_len - } - if bytes.HasPrefix(line, []byte("DROP")) { - return DROP, line_len - } - if bytes.HasPrefix(line, []byte("INJECT")) { - // Inject line in the reverse direction - p.connection.Inject(!reply, []byte(line)) - // Drop the INJECT line in the current direction - return DROP, line_len - } - if bytes.HasPrefix(line, []byte("INSERT")) { - // Inject the line in the current direction - p.connection.Inject(reply, []byte(line)) - // Drop the INJECT line in the current direction - p.inserted = true - return INJECT, line_len - } - - return ERROR, int(ERROR_INVALID_FRAME_TYPE) -} diff --git a/proxylib/testparsers/passer.go b/proxylib/testparsers/passer.go deleted file mode 100644 index 1f30cfa55..000000000 --- a/proxylib/testparsers/passer.go +++ /dev/null @@ -1,46 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package testparsers - -import ( - "github.com/sirupsen/logrus" - - . "github.com/cilium/proxy/proxylib/proxylib" -) - -type PasserParserFactory struct{} - -func init() { - logrus.Debug("init(): Registering PasserParserFactory") - RegisterParserFactory("test.passer", &PasserParserFactory{}) -} - -type PasserParser struct{} - -func (p *PasserParserFactory) Create(connection *Connection) interface{} { - // Reject invalid policy name for testing purposes - if connection.PolicyName == "invalid-policy" { - return nil - } - - logrus.Debugf("PasserParserFactory: Create: %v", connection) - return &PasserParser{} -} - -// OnData simply passes all data in either direction. -func (p *PasserParser) OnData(reply, endStream bool, data [][]byte) (OpType, int) { - n_bytes := 0 - for _, s := range data { - n_bytes += len(s) - } - if n_bytes == 0 { - return NOP, 0 - } - if !reply { - logrus.Debugf("PasserParser: Request: %d bytes", n_bytes) - } else { - logrus.Debugf("PasserParser: Response: %d bytes", n_bytes) - } - return PASS, n_bytes -} diff --git a/proxylib/types.h b/proxylib/types.h deleted file mode 100644 index f01b80ca2..000000000 --- a/proxylib/types.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2018 Authors of Cilium - * - * 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. - */ - -#ifndef PROXYLIB_TYPES_H -#define PROXYLIB_TYPES_H - -#include - -typedef enum { - FILTEROP_MORE, // Need more data - FILTEROP_PASS, // Pass N bytes - FILTEROP_DROP, // Drop N bytes - FILTEROP_INJECT, // Inject N>0 bytes - FILTEROP_ERROR, // Protocol parsing error -} FilterOpType; - -typedef enum { - FILTEROP_ERROR_INVALID_OP_LENGTH = 1, // Parser returned invalid operation length - FILTEROP_ERROR_INVALID_FRAME_TYPE, - FILTEROP_ERROR_INVALID_FRAME_LENGTH, -} FilterOpError; - -typedef struct { - uint64_t op; // FilterOpType - int64_t n_bytes; // >0 -} FilterOp; - -typedef enum { - FILTER_OK, // Operation was successful - FILTER_POLICY_DROP, // Connection needs to be dropped due to (L3/L4) policy - FILTER_PARSER_ERROR, // Connection needs to be dropped due to parser error - FILTER_UNKNOWN_PARSER, // Connection needs to be dropped due to unknown parser - FILTER_UNKNOWN_CONNECTION, // Connection needs to be dropped due to it being unknown - FILTER_INVALID_ADDRESS, // Destination address in invalid format - FILTER_INVALID_INSTANCE, // Destination address in invalid format - FILTER_UNKNOWN_ERROR, // Error type could not be cast to an error code -} FilterResult; - -#endif diff --git a/tests/BUILD b/tests/BUILD index 194a7fa3e..6c26d1b74 100644 --- a/tests/BUILD +++ b/tests/BUILD @@ -132,6 +132,7 @@ envoy_cc_test( ":bpf_metadata_lib", ":cilium_test_peer_lib", "//cilium:network_policy_lib", + "@envoy//test/mocks/secret:secret_mocks", "@envoy//test/mocks/server:factory_context_mocks", ], ) @@ -198,9 +199,6 @@ envoy_cc_test( envoy_cc_test( name = "cilium_tcp_integration_test", srcs = ["cilium_tcp_integration_test.cc"], - data = [ - "//proxylib:libcilium.so", - ], repository = "@envoy", deps = [ ":bpf_metadata_lib", @@ -239,7 +237,6 @@ envoy_cc_test( name = "cilium_tls_tcp_integration_test", srcs = ["cilium_tls_tcp_integration_test.cc"], data = [ - "//proxylib:libcilium.so", "@envoy//test/config/integration/certs", ], repository = "@envoy", @@ -260,9 +257,6 @@ envoy_cc_test( envoy_cc_test( name = "cilium_http_integration_test", srcs = ["cilium_http_integration_test.cc"], - data = [ - "//proxylib:libcilium.so", - ], repository = "@envoy", deps = [ ":bpf_metadata_lib", @@ -280,9 +274,6 @@ envoy_cc_test( envoy_cc_test( name = "cilium_http_upstream_integration_test", srcs = ["cilium_http_upstream_integration_test.cc"], - data = [ - "//proxylib:libcilium.so", - ], repository = "@envoy", deps = [ ":bpf_metadata_lib", @@ -302,9 +293,6 @@ envoy_cc_test( envoy_cc_test( name = "cilium_websocket_decap_integration_test", srcs = ["cilium_websocket_decap_integration_test.cc"], - data = [ - "//proxylib:libcilium.so", - ], repository = "@envoy", deps = [ ":bpf_metadata_lib", @@ -322,9 +310,6 @@ envoy_cc_test( envoy_cc_test( name = "cilium_websocket_codec_integration_test", srcs = ["cilium_websocket_codec_integration_test.cc"], - data = [ - "//proxylib:libcilium.so", - ], repository = "@envoy", deps = [ ":bpf_metadata_lib", @@ -341,9 +326,6 @@ envoy_cc_test( envoy_cc_test( name = "cilium_websocket_policy_integration_test", srcs = ["cilium_websocket_policy_integration_test.cc"], - data = [ - "//proxylib:libcilium.so", - ], repository = "@envoy", deps = [ ":bpf_metadata_lib", @@ -363,9 +345,6 @@ envoy_cc_test( envoy_cc_test( name = "cilium_websocket_encap_integration_test", srcs = ["cilium_websocket_encap_integration_test.cc"], - data = [ - "//proxylib:libcilium.so", - ], repository = "@envoy", deps = [ ":bpf_metadata_lib", diff --git a/tests/accesslog_server.cc b/tests/accesslog_server.cc index 9cffe6165..850d84fe7 100644 --- a/tests/accesslog_server.cc +++ b/tests/accesslog_server.cc @@ -2,6 +2,7 @@ #include #include +#include #include #include "source/common/common/logger.h" @@ -9,7 +10,6 @@ #include "absl/base/thread_annotations.h" #include "absl/synchronization/mutex.h" #include "absl/time/time.h" -#include "absl/types/optional.h" #include "cilium/api/accesslog.pb.h" #include "tests/uds_server.h" @@ -27,10 +27,10 @@ void AccessLogServer::clear() { messages_.clear(); } -absl::optional<::cilium::LogEntry> +std::optional<::cilium::LogEntry> AccessLogServer::waitForMessage(::cilium::EntryType entry_type, std::chrono::milliseconds timeout) { absl::MutexLock lock(&mutex_); - absl::optional<::cilium::LogEntry> entry; + std::optional<::cilium::LogEntry> entry; auto predicate = [this, &entry, entry_type]() ABSL_SHARED_LOCKS_REQUIRED(mutex_) { mutex_.AssertHeld(); for (auto& msg : messages_) { diff --git a/tests/accesslog_server.h b/tests/accesslog_server.h index 60f06c6fe..1f43f27de 100644 --- a/tests/accesslog_server.h +++ b/tests/accesslog_server.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -8,7 +9,6 @@ #include "absl/base/thread_annotations.h" #include "absl/synchronization/mutex.h" -#include "absl/types/optional.h" #include "cilium/api/accesslog.pb.h" #include "tests/uds_server.h" @@ -20,7 +20,7 @@ class AccessLogServer : public UDSServer { ~AccessLogServer() override; void clear(); - absl::optional<::cilium::LogEntry> + std::optional<::cilium::LogEntry> waitForMessage(::cilium::EntryType entry_type, std::chrono::milliseconds timeout = TestUtility::DefaultTimeout); diff --git a/tests/bpf_metadata.cc b/tests/bpf_metadata.cc index 2604586bc..d224e8eb1 100644 --- a/tests/bpf_metadata.cc +++ b/tests/bpf_metadata.cc @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -26,7 +27,6 @@ #include "test/test_common/environment.h" #include "absl/strings/string_view.h" -#include "absl/types/optional.h" #include "cilium/api/bpf_metadata.pb.h" #include "cilium/bpf_metadata.h" #include "cilium/host_map.h" @@ -164,7 +164,7 @@ TestConfig::~TestConfig() { npmap.reset(); } -absl::optional +std::optional TestConfig::extractSocketMetadata(Network::ConnectionSocket& socket) { // TLS filter chain matches this, make namespace part of this (e.g., // "default")? diff --git a/tests/bpf_metadata.h b/tests/bpf_metadata.h index 995a805ee..e7b794860 100644 --- a/tests/bpf_metadata.h +++ b/tests/bpf_metadata.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -9,7 +10,6 @@ #include "envoy/network/listen_socket.h" #include "envoy/server/factory_context.h" -#include "absl/types/optional.h" #include "cilium/bpf_metadata.h" #include "cilium/host_map.h" #include "cilium/network_policy.h" @@ -47,7 +47,7 @@ class TestConfig : public Config { Server::Configuration::ListenerFactoryContext& context); ~TestConfig() override; - absl::optional + std::optional extractSocketMetadata(Network::ConnectionSocket& socket) override; // Prevent socket options that require NET_ADMIN privileges from being applied during test diff --git a/tests/bpf_metadata_config_test.cc b/tests/bpf_metadata_config_test.cc index da8479774..4ef269755 100644 --- a/tests/bpf_metadata_config_test.cc +++ b/tests/bpf_metadata_config_test.cc @@ -397,11 +397,8 @@ TEST_F(MetadataConfigTest, NorthSouthL7LbIngressEnforcedMetadata) { log_entry.entry_.set_policy_name("pod"); // Expect policy accepts security ID 12345678 on ingress on port 80 - bool use_proxy_lib; std::string l7_proto; - EXPECT_TRUE( - policy_fs->enforceNetworkPolicy(conn_, 12345678, 80, "", use_proxy_lib, l7_proto, log_entry)); - EXPECT_FALSE(use_proxy_lib); + EXPECT_TRUE(policy_fs->enforceNetworkPolicy(conn_, 12345678, 80, "", l7_proto, log_entry)); EXPECT_EQ("", l7_proto); EXPECT_NE("pod", log_entry.entry_.policy_name()); @@ -467,29 +464,22 @@ TEST_F(MetadataConfigTest, NorthSouthL7LbPodAndIngressEnforcedMetadata) { log_entry.entry_.set_policy_name("pod"); // Expect pod policy denies security ID 12345678 on port 80 (only 222 allowed) - bool use_proxy_lib; std::string l7_proto; - EXPECT_FALSE( - policy_fs->enforceNetworkPolicy(conn_, 12345678, 80, "", use_proxy_lib, l7_proto, log_entry)); - EXPECT_FALSE(use_proxy_lib); + EXPECT_FALSE(policy_fs->enforceNetworkPolicy(conn_, 12345678, 80, "", l7_proto, log_entry)); EXPECT_EQ("", l7_proto); EXPECT_EQ("pod", log_entry.entry_.policy_name()); // Expect pod policy allows egress to security ID 222 on port 80 // Ingress policy allows ingress from 9999 (pod's security ID) // Ingress policy allows 222 egress - EXPECT_TRUE( - policy_fs->enforceNetworkPolicy(conn_, 222, 80, "", use_proxy_lib, l7_proto, log_entry)); - EXPECT_FALSE(use_proxy_lib); + EXPECT_TRUE(policy_fs->enforceNetworkPolicy(conn_, 222, 80, "", l7_proto, log_entry)); EXPECT_EQ("", l7_proto); EXPECT_NE("pod", log_entry.entry_.policy_name()); // Expect pod policy allows egress to security ID 333 on port 80 // Ingress policy allows ingress from 9999 // Ingress policy denies 333 egress - EXPECT_FALSE( - policy_fs->enforceNetworkPolicy(conn_, 333, 80, "", use_proxy_lib, l7_proto, log_entry)); - EXPECT_FALSE(use_proxy_lib); + EXPECT_FALSE(policy_fs->enforceNetworkPolicy(conn_, 333, 80, "", l7_proto, log_entry)); EXPECT_EQ("", l7_proto); EXPECT_NE("pod", log_entry.entry_.policy_name()); @@ -538,11 +528,8 @@ TEST_F(MetadataConfigTest, NorthSouthL7LbIngressEnforcedCIDRMetadata) { log_entry.entry_.set_policy_name("pod"); // Expect policy does not accept security ID 2 on port 80 - bool use_proxy_lib; std::string l7_proto; - EXPECT_FALSE( - policy_fs->enforceNetworkPolicy(conn_, 2, 80, "", use_proxy_lib, l7_proto, log_entry)); - EXPECT_FALSE(use_proxy_lib); + EXPECT_FALSE(policy_fs->enforceNetworkPolicy(conn_, 2, 80, "", l7_proto, log_entry)); EXPECT_EQ("", l7_proto); EXPECT_NE("pod", log_entry.entry_.policy_name()); diff --git a/tests/bpf_metadata_integration_test.cc b/tests/bpf_metadata_integration_test.cc index fd1d7189c..33884ab96 100644 --- a/tests/bpf_metadata_integration_test.cc +++ b/tests/bpf_metadata_integration_test.cc @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -213,7 +214,7 @@ class BpfMetadataIntegrationTest : public BaseIntegrationTest, setBpfMetadataNpdsConfig(bpf_config, use_ads, api_type); - listener_filter->mutable_typed_config()->PackFrom(bpf_config); + std::ignore = listener_filter->mutable_typed_config()->PackFrom(bpf_config); } void updateBpfMetadataListenerFilter(envoy::config::listener::v3::Listener& listener, @@ -227,7 +228,7 @@ class BpfMetadataIntegrationTest : public BaseIntegrationTest, RELEASE_ASSERT(listener_filter.typed_config().UnpackTo(&bpf_config), "failed to unpack cilium.bpf_metadata listener filter"); setBpfMetadataNpdsConfig(bpf_config, /*use_ads=*/false, api_type); - listener_filter.mutable_typed_config()->PackFrom(bpf_config); + std::ignore = listener_filter.mutable_typed_config()->PackFrom(bpf_config); return; } RELEASE_ASSERT(false, "cilium.bpf_metadata listener filter not found"); @@ -383,7 +384,7 @@ class BpfMetadataIntegrationTest : public BaseIntegrationTest, response.set_nonce(version); response.set_type_url(Envoy::Config::TestTypeUrl::get().Listener); for (const auto& listener_config : listener_configs) { - response.add_resources()->PackFrom(listener_config); + std::ignore = response.add_resources()->PackFrom(listener_config); } stream.sendGrpcMessage(response); } @@ -411,7 +412,7 @@ class BpfMetadataIntegrationTest : public BaseIntegrationTest, proto_configs.emplace_back(TestUtility::parseYaml(policy_config)); } for (const auto& policy_config : proto_configs) { - response.add_resources()->PackFrom(policy_config); + std::ignore = response.add_resources()->PackFrom(policy_config); } stream.sendGrpcMessage(response); } @@ -430,7 +431,7 @@ class BpfMetadataIntegrationTest : public BaseIntegrationTest, TestUtility::parseYaml(policy_host_config)); } for (const auto& policy_host_config : proto_configs) { - response.add_resources()->PackFrom(policy_host_config); + std::ignore = response.add_resources()->PackFrom(policy_host_config); } stream.sendGrpcMessage(response); } @@ -446,7 +447,7 @@ class BpfMetadataIntegrationTest : public BaseIntegrationTest, envoy::service::discovery::v3::Resource* resource = response.add_resources(); resource->set_name(resource_config.name); resource->set_version(resource_config.version); - resource->mutable_resource()->PackFrom( + std::ignore = resource->mutable_resource()->PackFrom( TestUtility::parseYaml(resource_config.yaml)); } for (const auto& removed_resource : removed_resources) { @@ -466,7 +467,7 @@ class BpfMetadataIntegrationTest : public BaseIntegrationTest, envoy::service::discovery::v3::Resource* resource = response.add_resources(); resource->set_name(resource_config.name); resource->set_version(resource_config.version); - resource->mutable_resource()->PackFrom( + std::ignore = resource->mutable_resource()->PackFrom( TestUtility::parseYaml(resource_config.yaml)); } for (const auto& removed_resource : removed_resources) { @@ -524,7 +525,8 @@ class BpfMetadataIntegrationTest : public BaseIntegrationTest, } uint64_t waitForPolicyStreamGenerationAfter(uint64_t previous_generation) { - test_server_->waitForGaugeGe("cilium.policy.policy_stream_generation", previous_generation + 1); + test_server_->waitForGauge("cilium.policy.policy_stream_generation", + testing::Ge(previous_generation + 1)); const uint64_t generation = policyStreamGeneration(); EXPECT_GT(generation, previous_generation); return generation; @@ -594,12 +596,12 @@ TEST_P(BpfMetadataIntegrationTest, BpfMetadataWithNpdsAndNpdhsViaAds) { }; initializeAds(); - test_server_->waitForCounterGe("listener_manager.lds.update_success", 1); + test_server_->waitForCounter("listener_manager.lds.update_success", testing::Ge(1)); EXPECT_EQ(test_server_->server().listenerManager().listeners().size(), 1); sendNpdsResponse(*ads_stream_, "1"); - test_server_->waitForCounterGe("cilium.policy.update_success", 1); + test_server_->waitForCounter("cilium.policy.update_success", testing::Ge(1)); sendNphdsResponse(*ads_stream_, "1"); - test_server_->waitForCounterGe("cilium.hostmap.update_success", 1); + test_server_->waitForCounter("cilium.hostmap.update_success", testing::Ge(1)); } TEST_P(BpfMetadataIntegrationTest, AdsPolicyMapsSurviveLastListenerRemoval) { @@ -617,11 +619,11 @@ TEST_P(BpfMetadataIntegrationTest, AdsPolicyMapsSurviveLastListenerRemoval) { }; initializeAds(); - test_server_->waitForCounterGe("listener_manager.lds.update_success", 1); + test_server_->waitForCounter("listener_manager.lds.update_success", testing::Ge(1)); sendNpdsResponse(*ads_stream_, "1"); - test_server_->waitForCounterGe("cilium.policy.update_success", 1); + test_server_->waitForCounter("cilium.policy.update_success", testing::Ge(1)); sendNphdsResponse(*ads_stream_, "1"); - test_server_->waitForCounterGe("cilium.hostmap.update_success", 1); + test_server_->waitForCounter("cilium.hostmap.update_success", testing::Ge(1)); { const auto policy_map = networkPolicyMap(); @@ -632,15 +634,15 @@ TEST_P(BpfMetadataIntegrationTest, AdsPolicyMapsSurviveLastListenerRemoval) { EXPECT_EQ(resolveHostPolicyId("10.2.2.2"), 222); sendLdsResponse(*ads_stream_, std::vector{}, "2"); - test_server_->waitForCounterGe("listener_manager.lds.update_success", 2); - test_server_->waitForCounterEq("listener_manager.listener_removed", 1); - test_server_->waitForGaugeEq("listener_manager.total_listeners_draining", 0); + test_server_->waitForCounter("listener_manager.lds.update_success", testing::Ge(2)); + test_server_->waitForCounter("listener_manager.listener_removed", testing::Eq(1)); + test_server_->waitForGauge("listener_manager.total_listeners_draining", testing::Eq(0)); EXPECT_TRUE(test_server_->server().listenerManager().listeners().empty()); sendNpdsResponse(*ads_stream_, "2", {policy2}); - test_server_->waitForCounterGe("cilium.policy.update_success", 2); + test_server_->waitForCounter("cilium.policy.update_success", testing::Ge(2)); sendNphdsResponse(*ads_stream_, "2", {policy_host2}); - test_server_->waitForCounterGe("cilium.hostmap.update_success", 2); + test_server_->waitForCounter("cilium.hostmap.update_success", testing::Ge(2)); const auto policy_map = networkPolicyMap(); EXPECT_FALSE(policy_map->exists("10.1.1.1")); @@ -664,15 +666,15 @@ TEST_P(BpfMetadataIntegrationTest, PolicyStreamGenerationTracksAcceptedAdsGrpcSt }; initializeAds(); - test_server_->waitForCounterGe("listener_manager.lds.update_success", 1); + test_server_->waitForCounter("listener_manager.lds.update_success", testing::Ge(1)); EXPECT_EQ(policyStreamGeneration(), 0); sendNpdsResponse(*ads_stream_, "1"); - test_server_->waitForCounterGe("cilium.policy.update_success", 1); + test_server_->waitForCounter("cilium.policy.update_success", testing::Ge(1)); const uint64_t first_generation = waitForPolicyStreamGenerationAfter(0); sendNpdsResponse(*ads_stream_, "2"); - test_server_->waitForCounterGe("cilium.policy.update_success", 2); + test_server_->waitForCounter("cilium.policy.update_success", testing::Ge(2)); EXPECT_EQ(policyStreamGeneration(), first_generation); resetConnections(); @@ -685,15 +687,15 @@ TEST_P(BpfMetadataIntegrationTest, PolicyStreamGenerationTracksAcceptedSotwGrpcS createSotWStreams("1"); }; initializeSotw(); - test_server_->waitForCounterGe("listener_manager.lds.update_success", 1); + test_server_->waitForCounter("listener_manager.lds.update_success", testing::Ge(1)); EXPECT_EQ(policyStreamGeneration(), 0); sendNpdsResponse(*npds_stream_, "1"); - test_server_->waitForCounterGe("cilium.policy.update_success", 1); + test_server_->waitForCounter("cilium.policy.update_success", testing::Ge(1)); const uint64_t first_generation = waitForPolicyStreamGenerationAfter(0); sendNpdsResponse(*npds_stream_, "2"); - test_server_->waitForCounterGe("cilium.policy.update_success", 2); + test_server_->waitForCounter("cilium.policy.update_success", testing::Ge(2)); EXPECT_EQ(policyStreamGeneration(), first_generation); resetConnections(); @@ -704,11 +706,11 @@ TEST_P(BpfMetadataIntegrationTest, PolicyStreamGenerationTracksAcceptedSotwGrpcS // The invalid policy is rejected by the real gRPC subscription decoder/validator before // NetworkPolicyMapImpl::onConfigUpdate() runs, so this increments NPDS subscription stats // rather than cilium.policy.updates_rejected. - test_server_->waitForCounterGe("cilium.npds.update_rejected", 1); + test_server_->waitForCounter("cilium.npds.update_rejected", testing::Ge(1)); EXPECT_EQ(policyStreamGeneration(), first_generation); sendNpdsResponse(*npds_stream_, "4"); - test_server_->waitForCounterGe("cilium.policy.update_success", 3); + test_server_->waitForCounter("cilium.policy.update_success", testing::Ge(3)); waitForPolicyStreamGenerationAfter(first_generation); } @@ -719,14 +721,14 @@ TEST_P(BpfMetadataIntegrationTest, PolicyStreamGenerationTracksAcceptedDeltaNpds createSotWStreams("1"); }; initializeSotw(); - test_server_->waitForCounterGe("listener_manager.lds.update_success", 1); + test_server_->waitForCounter("listener_manager.lds.update_success", testing::Ge(1)); auto policy_map = networkPolicyMap(); EXPECT_EQ(policyStreamGeneration(), 0); // Step 2: accept a real SotW NPDS response so the starting mode has installed policy. sendNpdsResponse(*npds_stream_, "1"); - test_server_->waitForCounterGe("cilium.policy.update_success", 1); + test_server_->waitForCounter("cilium.policy.update_success", testing::Ge(1)); const uint64_t sotw_generation = waitForPolicyStreamGenerationAfter(0); EXPECT_TRUE(policy_map->exists("10.1.1.1")); EXPECT_TRUE(policy_map->exists("10.2.2.2")); @@ -735,7 +737,7 @@ TEST_P(BpfMetadataIntegrationTest, PolicyStreamGenerationTracksAcceptedDeltaNpds updateBpfMetadataListenerFilter(listener_config_, envoy::config::core::v3::ApiConfigSource::DELTA_GRPC); sendLdsResponse(*lds_stream_, {listener_config_}, "2"); - test_server_->waitForCounterGe("listener_manager.lds.update_success", 2); + test_server_->waitForCounter("listener_manager.lds.update_success", testing::Ge(2)); // Step 4: observe the immediate switch to Delta NPDS without advancing accepted policy state. createStreamsUntil("2", NetworkPolicyTypeUrl, /*expect_delta=*/true); @@ -779,8 +781,8 @@ TEST_P(BpfMetadataIntegrationTest, PolicyStreamGenerationTracksAcceptedDeltaNphd createSotWStreams("1"); }; initializeSotw(); - test_server_->waitForCounterGe("listener_manager.lds.update_success", 1); - test_server_->waitForCounterGe("cilium.hostmap.update_success", 1); + test_server_->waitForCounter("listener_manager.lds.update_success", testing::Ge(1)); + test_server_->waitForCounter("cilium.hostmap.update_success", testing::Ge(1)); auto policy_map = networkPolicyMap(); EXPECT_EQ(policyStreamGeneration(), 0); @@ -789,7 +791,7 @@ TEST_P(BpfMetadataIntegrationTest, PolicyStreamGenerationTracksAcceptedDeltaNphd // Step 2: accept a real SotW NPDS response so the starting mode has installed policy. sendNpdsResponse(*npds_stream_, "1"); - test_server_->waitForCounterGe("cilium.policy.update_success", 1); + test_server_->waitForCounter("cilium.policy.update_success", testing::Ge(1)); const uint64_t sotw_generation = waitForPolicyStreamGenerationAfter(0); EXPECT_TRUE(policy_map->exists("10.1.1.1")); EXPECT_TRUE(policy_map->exists("10.2.2.2")); @@ -798,7 +800,7 @@ TEST_P(BpfMetadataIntegrationTest, PolicyStreamGenerationTracksAcceptedDeltaNphd updateBpfMetadataListenerFilter(listener_config_, envoy::config::core::v3::ApiConfigSource::DELTA_GRPC); sendLdsResponse(*lds_stream_, {listener_config_}, "2"); - test_server_->waitForCounterGe("listener_manager.lds.update_success", 2); + test_server_->waitForCounter("listener_manager.lds.update_success", testing::Ge(2)); // Step 4: observe the immediate switch to Delta NPHDS without advancing accepted policy state. createStreamsUntil("2", NetworkPolicyHostsTypeUrl, /*expect_delta=*/true); @@ -809,7 +811,7 @@ TEST_P(BpfMetadataIntegrationTest, PolicyStreamGenerationTracksAcceptedDeltaNphd // Step 5: accept the first Delta NPHDS update. This should not change policy stream generation. sendNphdsDeltaResponse(*nphds_stream_, "1", {policy_host1_resource, policy_host2_resource}); EXPECT_TRUE(compareNphdsAck()); - test_server_->waitForCounterGe("cilium.hostmap.update_success", 2); + test_server_->waitForCounter("cilium.hostmap.update_success", testing::Ge(2)); EXPECT_EQ(policyStreamGeneration(), sotw_generation); EXPECT_EQ(resolveHostPolicyId("10.1.1.1"), 111); EXPECT_EQ(resolveHostPolicyId("10.2.2.2"), 222); @@ -817,7 +819,7 @@ TEST_P(BpfMetadataIntegrationTest, PolicyStreamGenerationTracksAcceptedDeltaNphd // Step 6: accept a same-stream Delta update; omitted resources stay present on the same stream. sendNphdsDeltaResponse(*nphds_stream_, "2", {policy_host1_new_stream_resource}); EXPECT_TRUE(compareNphdsAck()); - test_server_->waitForCounterGe("cilium.hostmap.update_success", 3); + test_server_->waitForCounter("cilium.hostmap.update_success", testing::Ge(3)); EXPECT_EQ(policyStreamGeneration(), sotw_generation); EXPECT_EQ(resolveHostPolicyId("10.1.1.1"), 111); EXPECT_EQ(resolveHostPolicyId("10.2.2.2"), 222); @@ -833,7 +835,7 @@ TEST_P(BpfMetadataIntegrationTest, PolicyStreamGenerationTracksAcceptedDeltaNphd // Step 9: accept the first update on the new stream and retire resources from the old stream. sendNphdsDeltaResponse(*nphds_stream_, "3", {policy_host1_new_stream_resource}); EXPECT_TRUE(compareNphdsAck()); - test_server_->waitForCounterGe("cilium.hostmap.update_success", 4); + test_server_->waitForCounter("cilium.hostmap.update_success", testing::Ge(4)); EXPECT_EQ(policyStreamGeneration(), sotw_generation); EXPECT_EQ(resolveHostPolicyId("10.1.1.1"), 111); EXPECT_EQ(resolveHostPolicyId("10.2.2.2"), Cilium::ID::UNKNOWN); diff --git a/tests/cilium_http_integration.h b/tests/cilium_http_integration.h index 3ce1f1db7..aab9b4f15 100644 --- a/tests/cilium_http_integration.h +++ b/tests/cilium_http_integration.h @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -16,7 +17,6 @@ #include "test/integration/http_integration.h" #include "test/test_common/utility.h" -#include "absl/types/optional.h" #include "cilium/api/accesslog.pb.h" #include "tests/accesslog_server.h" @@ -41,7 +41,7 @@ class CiliumHttpIntegrationTest : public HttpIntegrationTest, return std::vector>{}; } - absl::optional<::cilium::LogEntry> + std::optional<::cilium::LogEntry> waitForAccessLogMessage(::cilium::EntryType entry_type, std::chrono::milliseconds timeout = TestUtility::DefaultTimeout) { return accessLogServer_.waitForMessage(entry_type, timeout); @@ -59,7 +59,7 @@ class CiliumHttpIntegrationTest : public HttpIntegrationTest, return accessLogServer_.expectDeniedTo(pred); } - static absl::optional + static std::optional getHeader(const Protobuf::RepeatedPtrField<::cilium::KeyValue>& headers, const std::string& name) { for (const auto& entry : headers) { @@ -67,7 +67,7 @@ class CiliumHttpIntegrationTest : public HttpIntegrationTest, return entry.value(); } } - return absl::nullopt; + return std::nullopt; } static bool hasHeader(const Protobuf::RepeatedPtrField<::cilium::KeyValue>& headers, diff --git a/tests/cilium_http_integration_test.cc b/tests/cilium_http_integration_test.cc index 7c13833ba..e4c9b28cc 100644 --- a/tests/cilium_http_integration_test.cc +++ b/tests/cilium_http_integration_test.cc @@ -6,6 +6,7 @@ #include #include +#include #include #include #include @@ -28,7 +29,6 @@ #include "absl/time/clock.h" #include "absl/time/time.h" -#include "absl/types/optional.h" #include "cilium/api/accesslog.pb.h" #include "cilium/host_map.h" #include "cilium/secret_watcher.h" @@ -275,6 +275,8 @@ const std::string cilium_proxy_config_fmt = R"EOF( "@type": type.googleapis.com/cilium.L7Policy access_log_path: "{{ test_udsdir }}/access_log.sock" - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router route_config: name: policy_enabled virtual_hosts: @@ -323,7 +325,7 @@ class CiliumIntegrationTest : public CiliumHttpIntegrationTest { ASSERT_TRUE(response->waitForEndStream()); // Validate that request access log message with x-request-id is logged - absl::optional maybe_x_request_id; + std::optional maybe_x_request_id; EXPECT_TRUE(expectAccessLogDeniedTo([&maybe_x_request_id](const ::cilium::LogEntry& entry) { maybe_x_request_id = getHeader(entry.http().headers(), "x-request-id"); return entry.http().status() == 0; @@ -331,7 +333,7 @@ class CiliumIntegrationTest : public CiliumHttpIntegrationTest { ASSERT_TRUE(maybe_x_request_id.has_value()); // Validate that response x-request-id is the same as in request - absl::optional maybe_x_request_id_resp; + std::optional maybe_x_request_id_resp; EXPECT_TRUE( expectAccessLogResponseTo([&maybe_x_request_id_resp](const ::cilium::LogEntry& entry) { maybe_x_request_id_resp = getHeader(entry.http().headers(), "x-request-id"); @@ -358,8 +360,8 @@ class CiliumIntegrationTest : public CiliumHttpIntegrationTest { IntegrationCodecClientPtr makeL3DeniedHttpConnection() { // L3/L4 policy denial happens in the network filter during connection setup, so the reset may // reach the client before Envoy's HTTP test helper observes the connection as established. - return makeRawHttpConnection(makeClientConnection(lookupPort("http")), absl::nullopt, - absl::nullopt, /*wait_till_connected=*/false); + return makeRawHttpConnection(makeClientConnection(lookupPort("http")), std::nullopt, + std::nullopt, /*wait_till_connected=*/false); } void accepted(Http::TestRequestHeaderMapImpl&& headers) { @@ -368,7 +370,7 @@ class CiliumIntegrationTest : public CiliumHttpIntegrationTest { auto response = sendRequestAndWaitForResponse(headers, 0, default_response_headers_, 0); // Validate that request access log message with x-request-id is logged - absl::optional maybe_x_request_id; + std::optional maybe_x_request_id; EXPECT_TRUE(expectAccessLogRequestTo([&maybe_x_request_id](const ::cilium::LogEntry& entry) { maybe_x_request_id = getHeader(entry.http().headers(), "x-request-id"); return entry.http().status() == 0; @@ -376,7 +378,7 @@ class CiliumIntegrationTest : public CiliumHttpIntegrationTest { ASSERT_TRUE(maybe_x_request_id.has_value()); // Validate that response x-request-id is the same as in request - absl::optional maybe_x_request_id_resp; + std::optional maybe_x_request_id_resp; EXPECT_TRUE( expectAccessLogResponseTo([&maybe_x_request_id_resp](const ::cilium::LogEntry& entry) { maybe_x_request_id_resp = getHeader(entry.http().headers(), "x-request-id"); diff --git a/tests/cilium_http_upstream_integration_test.cc b/tests/cilium_http_upstream_integration_test.cc index c93931c28..b7895ef42 100644 --- a/tests/cilium_http_upstream_integration_test.cc +++ b/tests/cilium_http_upstream_integration_test.cc @@ -3,6 +3,7 @@ #include #include +#include #include #include #include @@ -18,7 +19,6 @@ #include "absl/time/clock.h" #include "absl/time/time.h" -#include "absl/types/optional.h" #include "cilium/api/accesslog.pb.h" #include "cilium/secret_watcher.h" #include "tests/bpf_metadata.h" // host_map_config @@ -360,7 +360,7 @@ class CiliumIntegrationTest : public CiliumHttpIntegrationTest { ASSERT_TRUE(response->waitForEndStream()); // Validate that request access log message with x-request-id is logged - absl::optional maybe_x_request_id; + std::optional maybe_x_request_id; EXPECT_TRUE(expectAccessLogDeniedTo([&maybe_x_request_id](const ::cilium::LogEntry& entry) { maybe_x_request_id = getHeader(entry.http().headers(), "x-request-id"); return entry.http().status() == 0; @@ -368,7 +368,7 @@ class CiliumIntegrationTest : public CiliumHttpIntegrationTest { ASSERT_TRUE(maybe_x_request_id.has_value()); // Validate that response x-request-id is the same as in request - absl::optional maybe_x_request_id_resp; + std::optional maybe_x_request_id_resp; EXPECT_TRUE( expectAccessLogResponseTo([&maybe_x_request_id_resp](const ::cilium::LogEntry& entry) { maybe_x_request_id_resp = getHeader(entry.http().headers(), "x-request-id"); @@ -388,7 +388,7 @@ class CiliumIntegrationTest : public CiliumHttpIntegrationTest { auto response = sendRequestAndWaitForResponse(headers, 0, default_response_headers_, 0); // Validate that request access log message with x-request-id is logged - absl::optional maybe_x_request_id; + std::optional maybe_x_request_id; EXPECT_TRUE(expectAccessLogRequestTo([&maybe_x_request_id](const ::cilium::LogEntry& entry) { maybe_x_request_id = getHeader(entry.http().headers(), "x-request-id"); return entry.http().status() == 0; @@ -396,7 +396,7 @@ class CiliumIntegrationTest : public CiliumHttpIntegrationTest { ASSERT_TRUE(maybe_x_request_id.has_value()); // Validate that response x-request-id is the same as in request - absl::optional maybe_x_request_id_resp; + std::optional maybe_x_request_id_resp; EXPECT_TRUE( expectAccessLogResponseTo([&maybe_x_request_id_resp](const ::cilium::LogEntry& entry) { maybe_x_request_id_resp = getHeader(entry.http().headers(), "x-request-id"); diff --git a/tests/cilium_network_policy_benchmark.cc b/tests/cilium_network_policy_benchmark.cc index 725915f7c..21e65af6c 100644 --- a/tests/cilium_network_policy_benchmark.cc +++ b/tests/cilium_network_policy_benchmark.cc @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -72,7 +73,7 @@ envoy::service::discovery::v3::DiscoveryResponse makeNpdsResponse(bool use_heade kSharedValidationContextSdsSecret); } - response.add_resources()->PackFrom(policy); + std::ignore = response.add_resources()->PackFrom(policy); } return response; } diff --git a/tests/cilium_tcp_integration_test.cc b/tests/cilium_tcp_integration_test.cc index 35f9a0145..a2f9355f2 100644 --- a/tests/cilium_tcp_integration_test.cc +++ b/tests/cilium_tcp_integration_test.cc @@ -12,8 +12,6 @@ #include "test/test_common/environment.h" #include "test/test_common/utility.h" -#include "absl/time/clock.h" -#include "absl/time/time.h" #include "tests/cilium_tcp_integration.h" namespace Envoy { @@ -66,7 +64,6 @@ const std::string cilium_tcp_proxy_config_fmt = R"EOF( - name: cilium.network typed_config: "@type": type.googleapis.com/cilium.NetworkFilter - proxylib: "proxylib/libcilium.so" - name: envoy.tcp_proxy typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy @@ -199,7 +196,8 @@ TEST_P(CiliumTcpProxyIntegrationTest, CiliumTcpProxyDownstreamFlush) { ASSERT_TRUE(fake_upstream_connection->write(data, true)); - test_server_->waitForCounterGe("cluster.cluster1.upstream_flow_control_paused_reading_total", 1); + test_server_->waitForCounter("cluster.cluster1.upstream_flow_control_paused_reading_total", + testing::Ge(1)); EXPECT_EQ(test_server_->counter("cluster.cluster1.upstream_flow_control_resumed_reading_total") ->value(), 0); @@ -239,7 +237,7 @@ TEST_P(CiliumTcpProxyIntegrationTest, CiliumTcpProxyUpstreamFlush) { ASSERT_TRUE(tcp_client->write(data, true, true, std::chrono::milliseconds(30000))); - test_server_->waitForGaugeEq("tcp.tcp_stats.upstream_flush_active", 1); + test_server_->waitForGauge("tcp.tcp_stats.upstream_flush_active", testing::Eq(1)); ASSERT_TRUE(fake_upstream_connection->readDisable(false)); ASSERT_TRUE(fake_upstream_connection->waitForData(data.size())); ASSERT_TRUE(fake_upstream_connection->waitForHalfClose()); @@ -247,7 +245,7 @@ TEST_P(CiliumTcpProxyIntegrationTest, CiliumTcpProxyUpstreamFlush) { tcp_client->waitForHalfClose(); EXPECT_EQ(test_server_->counter("tcp.tcp_stats.upstream_flush_total")->value(), 1); - test_server_->waitForGaugeEq("tcp.tcp_stats.upstream_flush_active", 0); + test_server_->waitForGauge("tcp.tcp_stats.upstream_flush_active", testing::Eq(0)); } // Test that Envoy doesn't crash or assert when shutting down with an upstream @@ -273,7 +271,7 @@ TEST_P(CiliumTcpProxyIntegrationTest, CiliumTcpProxyUpstreamFlushEnvoyExit) { ASSERT_TRUE(tcp_client->write(data, true)); - test_server_->waitForGaugeEq("tcp.tcp_stats.upstream_flush_active", 1); + test_server_->waitForGauge("tcp.tcp_stats.upstream_flush_active", testing::Eq(1)); test_server_.reset(); ASSERT_TRUE(fake_upstream_connection->close()); ASSERT_TRUE(fake_upstream_connection->waitForDisconnect()); @@ -286,485 +284,5 @@ TEST_P(CiliumTcpProxyIntegrationTest, CiliumTcpProxyUpstreamFlushEnvoyExit) { // // params: is_ingress ("true", "false") -const std::string cilium_linetester_config_fmt = R"EOF( -admin: - address: - socket_address: - address: 127.0.0.1 - port_value: 0 -static_resources: - clusters: - - name: cluster1 - type: ORIGINAL_DST - lb_policy: CLUSTER_PROVIDED - connect_timeout: - seconds: 1 - - name: xds-grpc-cilium - connect_timeout: - seconds: 5 - type: STATIC - lb_policy: ROUND_ROBIN - http2_protocol_options: - load_assignment: - cluster_name: xds-grpc-cilium - endpoints: - - lb_endpoints: - - endpoint: - address: - pipe: - path: /var/run/cilium/xds.sock - listeners: - name: listener_0 - address: - socket_address: - address: 127.0.0.1 - port_value: 0 - listener_filters: - name: test_bpf_metadata - typed_config: - "@type": type.googleapis.com/cilium.TestBpfMetadata - is_ingress: {0} - filter_chains: - filters: - - name: cilium.network - typed_config: - "@type": type.googleapis.com/cilium.NetworkFilter - proxylib: "proxylib/libcilium.so" - - name: envoy.tcp_proxy - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy - stat_prefix: tcp_stats - cluster: cluster1 -)EOF"; - -const std::string TCP_POLICY_LINEPARSER_fmt = R"EOF(version_info: "0" -resources: -- "@type": type.googleapis.com/cilium.NetworkPolicy - endpoint_ips: - - '{{ ntop_ip_loopback_address }}' - endpoint_id: 42 - policy: 3 - ingress_per_port_policies: - - port: 1 - end_port: {0} - rules: - - remote_policies: [ 1 ] - l7_proto: "test.lineparser" - egress_per_port_policies: - - port: 1 - end_port: {0} - rules: - - remote_policies: [ 1 ] - l7_proto: "test.lineparser" -)EOF"; - -class CiliumGoLinetesterIntegrationTest : public CiliumTcpIntegrationTest { -public: - CiliumGoLinetesterIntegrationTest() - : CiliumTcpIntegrationTest(fmt::format( - fmt::runtime(TestEnvironment::substitute(cilium_linetester_config_fmt, GetParam())), - "true")) {} - - std::string testPolicyFmt() override { - return TestEnvironment::substitute(TCP_POLICY_LINEPARSER_fmt, GetParam()); - } -}; - -INSTANTIATE_TEST_SUITE_P(IpVersions, CiliumGoLinetesterIntegrationTest, - testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), - TestUtility::ipTestParamsToString); - -static FakeRawConnection::ValidatorFunction noMatch(const char* data_to_not_match) { - return [data_to_not_match](const std::string& data) -> bool { - auto found = data.find(data_to_not_match); - return found == std::string::npos; - }; -} - -TEST_P(CiliumGoLinetesterIntegrationTest, CiliumGoLineParserUpstreamWritesFirst) { - initialize(); - IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("tcp_proxy")); - FakeRawConnectionPtr fake_upstream_connection; - ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection)); - - ASSERT_TRUE(fake_upstream_connection->write("DROP reply direction\n")); - ASSERT_TRUE(fake_upstream_connection->write("PASS reply direction\n")); - tcp_client->waitForData("PASS reply direction\n"); - - ASSERT_TRUE(tcp_client->write("PASS original direction\n")); - ASSERT_TRUE( - fake_upstream_connection->waitForData(FakeRawConnection::waitForInexactMatch("PASS"))); - - ASSERT_TRUE(fake_upstream_connection->write("", true)); - tcp_client->waitForHalfClose(); - ASSERT_TRUE(tcp_client->write("", true)); - ASSERT_TRUE(fake_upstream_connection->waitForHalfClose()); - ASSERT_TRUE(fake_upstream_connection->waitForDisconnect()); -} - -TEST_P(CiliumGoLinetesterIntegrationTest, CiliumGoLineParserPartialLines) { - initialize(); - IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("tcp_proxy")); - FakeRawConnectionPtr fake_upstream_connection; - ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection)); - - ASSERT_TRUE(fake_upstream_connection->write("DROP reply ")); - absl::SleepFor(absl::Milliseconds(10)); - ASSERT_TRUE(fake_upstream_connection->write("direction\nPASS")); - absl::SleepFor(absl::Milliseconds(10)); - ASSERT_TRUE(fake_upstream_connection->write(" reply direction\n")); - tcp_client->waitForData("PASS reply direction\n"); - - ASSERT_TRUE(tcp_client->write("PASS original direction\n")); - ASSERT_TRUE(fake_upstream_connection->waitForData( - FakeRawConnection::waitForInexactMatch("PASS original direction\n"))); - - ASSERT_TRUE(fake_upstream_connection->write("", true)); - tcp_client->waitForHalfClose(); - ASSERT_TRUE(tcp_client->write("", true)); - ASSERT_TRUE(fake_upstream_connection->waitForHalfClose()); - ASSERT_TRUE(fake_upstream_connection->waitForDisconnect()); -} - -TEST_P(CiliumGoLinetesterIntegrationTest, CiliumGoLineParserInject) { - initialize(); - IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("tcp_proxy")); - FakeRawConnectionPtr fake_upstream_connection; - ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection)); - - ASSERT_TRUE(tcp_client->write("INJECT reply direction\n")); - ASSERT_TRUE(tcp_client->write("PASS original direction\n")); - ASSERT_TRUE(fake_upstream_connection->write("PASS reply direction\n")); - - // These can in principle arrive in either order - tcp_client->waitForData("PASS reply direction\n", false); - tcp_client->waitForData("INJECT reply direction\n", false); - - ASSERT_TRUE(fake_upstream_connection->waitForData( - FakeRawConnection::waitForInexactMatch("PASS original direction\n"))); - - ASSERT_TRUE(fake_upstream_connection->write("", true)); - tcp_client->waitForHalfClose(); - ASSERT_TRUE(tcp_client->write("", true)); - ASSERT_TRUE(fake_upstream_connection->waitForHalfClose()); - ASSERT_TRUE(fake_upstream_connection->waitForDisconnect()); -} - -TEST_P(CiliumGoLinetesterIntegrationTest, CiliumGoLineParserInjectPartial) { - initialize(); - IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("tcp_proxy")); - FakeRawConnectionPtr fake_upstream_connection; - ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection)); - - ASSERT_TRUE(fake_upstream_connection->write("PASS reply")); - ASSERT_TRUE(tcp_client->write("INJECT reply direction\n")); - ASSERT_TRUE(tcp_client->write("PASS original direction\n")); - - ASSERT_TRUE(fake_upstream_connection->write(" direction\n")); - - // These can in principle arrive in either order - tcp_client->waitForData("PASS reply direction\n", false); - tcp_client->waitForData("INJECT reply direction\n", false); - - ASSERT_TRUE(fake_upstream_connection->waitForData( - FakeRawConnection::waitForInexactMatch("PASS original direction\n"))); - - ASSERT_TRUE(fake_upstream_connection->write("", true)); - tcp_client->waitForHalfClose(); - ASSERT_TRUE(tcp_client->write("", true)); - ASSERT_TRUE(fake_upstream_connection->waitForHalfClose()); - ASSERT_TRUE(fake_upstream_connection->waitForDisconnect()); -} - -TEST_P(CiliumGoLinetesterIntegrationTest, CiliumGoLineParserInjectPartialMultiple) { - initialize(); - IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("tcp_proxy")); - FakeRawConnectionPtr fake_upstream_connection; - ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection)); - - ASSERT_TRUE(fake_upstream_connection->write("PASS reply")); - ASSERT_TRUE(tcp_client->write("INJECT reply direction\n")); - ASSERT_TRUE(tcp_client->write("DROP original direction\n")); - ASSERT_TRUE(tcp_client->write("INSERT original direction\n")); - - ASSERT_TRUE(fake_upstream_connection->write(" direction\n")); - - // These can in principle arrive in either order - absl::SleepFor(absl::Milliseconds(10)); - tcp_client->waitForData("PASS reply direction\n", false); - absl::SleepFor(absl::Milliseconds(10)); - tcp_client->waitForData("INJECT reply direction\n", false); - - ASSERT_TRUE(fake_upstream_connection->waitForData( - FakeRawConnection::waitForInexactMatch("INSERT original direction\n"))); - ASSERT_TRUE(fake_upstream_connection->waitForData(noMatch("DROP"))); - - ASSERT_TRUE(fake_upstream_connection->write("DROP reply direction\n")); - ASSERT_TRUE(fake_upstream_connection->write("PASS2 reply direction\n")); - tcp_client->waitForData("PASS2 reply direction\n", false); - - ASSERT_TRUE(fake_upstream_connection->write("", true)); - tcp_client->waitForHalfClose(); - ASSERT_TRUE(tcp_client->write("", true)); - ASSERT_TRUE(fake_upstream_connection->waitForHalfClose()); - ASSERT_TRUE(fake_upstream_connection->waitForDisconnect()); -} - -// -// Cilium Go test parser "blocktester" with TCP proxy -// - -// params: is_ingress ("true", "false") -const std::string cilium_blocktester_config_fmt = R"EOF( -admin: - address: - socket_address: - address: 127.0.0.1 - port_value: 0 -static_resources: - clusters: - - name: cluster1 - type: ORIGINAL_DST - lb_policy: CLUSTER_PROVIDED - connect_timeout: - seconds: 1 - - name: xds-grpc-cilium - connect_timeout: - seconds: 5 - type: STATIC - lb_policy: ROUND_ROBIN - http2_protocol_options: - load_assignment: - cluster_name: xds-grpc-cilium - endpoints: - - lb_endpoints: - - endpoint: - address: - pipe: - path: /var/run/cilium/xds.sock - listeners: - name: listener_0 - address: - socket_address: - address: 127.0.0.1 - port_value: 0 - listener_filters: - name: test_bpf_metadata - typed_config: - "@type": type.googleapis.com/cilium.TestBpfMetadata - is_ingress: {0} - filter_chains: - filters: - - name: cilium.network - typed_config: - "@type": type.googleapis.com/cilium.NetworkFilter - proxylib: "proxylib/libcilium.so" - proxylib_params: - access-log-path: "{{ test_udsdir }}/access_log.sock" - - name: envoy.tcp_proxy - typed_config: - "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy - stat_prefix: tcp_stats - cluster: cluster1 -)EOF"; - -const std::string TCP_POLICY_BLOCKPARSER_fmt = R"EOF(version_info: "0" -resources: -- "@type": type.googleapis.com/cilium.NetworkPolicy - endpoint_ips: - - '{{ ntop_ip_loopback_address }}' - endpoint_id: 42 - policy: 3 - ingress_per_port_policies: - - port: 1 - end_port: 65535 - rules: - - remote_policies: [ 1 ] - l7_proto: "test.blockparser" - egress_per_port_policies: - - port: 1 - end_port: 65535 - rules: - - remote_policies: [ 1 ] - l7_proto: "test.blockparser" -)EOF"; - -class CiliumGoBlocktesterIntegrationTest : public CiliumTcpIntegrationTest { -public: - CiliumGoBlocktesterIntegrationTest() - : CiliumTcpIntegrationTest(fmt::format( - fmt::runtime(TestEnvironment::substitute(cilium_blocktester_config_fmt, GetParam())), - "true")) {} - - std::string testPolicyFmt() override { - return TestEnvironment::substitute(TCP_POLICY_BLOCKPARSER_fmt, GetParam()); - } -}; - -INSTANTIATE_TEST_SUITE_P(IpVersions, CiliumGoBlocktesterIntegrationTest, - testing::ValuesIn(TestEnvironment::getIpVersionsForTest()), - TestUtility::ipTestParamsToString); - -TEST_P(CiliumGoBlocktesterIntegrationTest, CiliumGoBlockParserUpstreamWritesFirst) { - initialize(); - IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("tcp_proxy")); - FakeRawConnectionPtr fake_upstream_connection; - ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection)); - - ASSERT_TRUE(fake_upstream_connection->write("24:DROP reply direction\n")); - ASSERT_TRUE(fake_upstream_connection->write("24:PASS reply direction\n")); - tcp_client->waitForData("24:PASS reply direction\n"); - - ASSERT_TRUE(tcp_client->write("27:PASS original direction\n")); - ASSERT_TRUE( - fake_upstream_connection->waitForData(FakeRawConnection::waitForInexactMatch("PASS"))); - - ASSERT_TRUE(fake_upstream_connection->write("", true)); - tcp_client->waitForHalfClose(); - ASSERT_TRUE(tcp_client->write("", true)); - ASSERT_TRUE(fake_upstream_connection->waitForHalfClose()); - ASSERT_TRUE(fake_upstream_connection->waitForDisconnect()); -} - -TEST_P(CiliumGoBlocktesterIntegrationTest, CiliumGoBlockParserPartialBlocks) { - initialize(); - IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("tcp_proxy")); - FakeRawConnectionPtr fake_upstream_connection; - ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection)); - - ASSERT_TRUE(fake_upstream_connection->write("24:DROP reply ")); - ASSERT_TRUE(fake_upstream_connection->write("direction\n24:PASS")); - ASSERT_TRUE(fake_upstream_connection->write(" reply direction\n")); - tcp_client->waitForData("24:PASS reply direction\n"); - - ASSERT_TRUE(tcp_client->write("27:PASS original direction\n")); - ASSERT_TRUE(fake_upstream_connection->waitForData( - FakeRawConnection::waitForInexactMatch("27:PASS original direction\n"))); - - ASSERT_TRUE(fake_upstream_connection->write("", true)); - tcp_client->waitForHalfClose(); - ASSERT_TRUE(tcp_client->write("", true)); - ASSERT_TRUE(fake_upstream_connection->waitForHalfClose()); - ASSERT_TRUE(fake_upstream_connection->waitForDisconnect()); -} - -TEST_P(CiliumGoBlocktesterIntegrationTest, CiliumGoBlockParserInject) { - initialize(); - IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("tcp_proxy")); - FakeRawConnectionPtr fake_upstream_connection; - ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection)); - - ASSERT_TRUE(tcp_client->write("26:INJECT reply direction\n")); - ASSERT_TRUE(tcp_client->write("27:PASS original direction\n")); - ASSERT_TRUE(fake_upstream_connection->write("24:PASS reply direction\n")); - - // These can in principle arrive in either order - absl::SleepFor(absl::Milliseconds(10)); - tcp_client->waitForData("24:PASS reply direction\n", false); - absl::SleepFor(absl::Milliseconds(10)); - tcp_client->waitForData("26:INJECT reply direction\n", false); - - ASSERT_TRUE(fake_upstream_connection->waitForData( - FakeRawConnection::waitForInexactMatch("27:PASS original direction\n"))); - - ASSERT_TRUE(fake_upstream_connection->write("", true)); - tcp_client->waitForHalfClose(); - ASSERT_TRUE(tcp_client->write("", true)); - ASSERT_TRUE(fake_upstream_connection->waitForHalfClose()); - ASSERT_TRUE(fake_upstream_connection->waitForDisconnect()); -} - -TEST_P(CiliumGoBlocktesterIntegrationTest, CiliumGoBlockParserInjectPartial) { - initialize(); - IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("tcp_proxy")); - FakeRawConnectionPtr fake_upstream_connection; - ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection)); - - ASSERT_TRUE(fake_upstream_connection->write("24:PASS reply")); - ASSERT_TRUE(tcp_client->write("26:INJECT reply direction\n")); - ASSERT_TRUE(tcp_client->write("27:PASS original direction\n")); - - ASSERT_TRUE(fake_upstream_connection->write(" direction\n")); - - // These can in principle arrive in either order - tcp_client->waitForData("24:PASS reply direction\n", false); - tcp_client->waitForData("26:INJECT reply direction\n", false); - - ASSERT_TRUE(fake_upstream_connection->waitForData( - FakeRawConnection::waitForInexactMatch("27:PASS original direction\n"))); - - ASSERT_TRUE(fake_upstream_connection->write("", true)); - tcp_client->waitForHalfClose(); - ASSERT_TRUE(tcp_client->write("", true)); - ASSERT_TRUE(fake_upstream_connection->waitForHalfClose()); - ASSERT_TRUE(fake_upstream_connection->waitForDisconnect()); -} - -TEST_P(CiliumGoBlocktesterIntegrationTest, CiliumGoBlockParserInjectPartialMultiple) { - initialize(); - IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("tcp_proxy")); - FakeRawConnectionPtr fake_upstream_connection; - ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection)); - - ASSERT_TRUE(fake_upstream_connection->write("24:PASS reply")); - ASSERT_TRUE(tcp_client->write("26:INJECT reply direction\n")); - ASSERT_TRUE(tcp_client->write("27:DROP original direction\n")); - ASSERT_TRUE(tcp_client->write("29:INSERT original direction\n")); - - absl::SleepFor(absl::Milliseconds(100)); - ASSERT_TRUE(fake_upstream_connection->write(" dire")); - - absl::SleepFor(absl::Milliseconds(100)); - ASSERT_TRUE(fake_upstream_connection->write("ction\n")); - - // These can in principle arrive in either order - tcp_client->waitForData("24:PASS reply direction\n", false); - tcp_client->waitForData("26:INJECT reply direction\n", false); - - ASSERT_TRUE(fake_upstream_connection->waitForData( - FakeRawConnection::waitForInexactMatch("29:INSERT original direction\n"))); - ASSERT_TRUE(fake_upstream_connection->waitForData(noMatch("DROP"))); - - ASSERT_TRUE(fake_upstream_connection->write("24:DROP reply direction\n")); - ASSERT_TRUE(fake_upstream_connection->write("25:PASS2 reply direction\n")); - tcp_client->waitForData("25:PASS2 reply direction\n", false); - - ASSERT_TRUE(fake_upstream_connection->write("", true)); - tcp_client->waitForHalfClose(); - ASSERT_TRUE(tcp_client->write("", true)); - ASSERT_TRUE(fake_upstream_connection->waitForHalfClose()); - ASSERT_TRUE(fake_upstream_connection->waitForDisconnect()); -} - -TEST_P(CiliumGoBlocktesterIntegrationTest, CiliumGoBlockParserInjectBufferOverflow) { - initialize(); - IntegrationTcpClientPtr tcp_client = makeTcpConnection(lookupPort("tcp_proxy")); - FakeRawConnectionPtr fake_upstream_connection; - ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection)); - - ASSERT_TRUE(tcp_client->write("26:INJECT reply direction\n")); - ASSERT_TRUE(tcp_client->write("27:DROP original direction\n")); - - std::string buf(5000, 'A'); - buf.replace(0, 30, "5000:INSERT original direction"); - buf.back() = '\n'; - - ASSERT_TRUE(tcp_client->write(buf)); - tcp_client->waitForData("26:INJECT reply direction\n", false); - - ASSERT_TRUE(fake_upstream_connection->waitForData( - FakeRawConnection::waitForInexactMatch("INSERT original direction"))); - ASSERT_TRUE(fake_upstream_connection->waitForData(noMatch("DROP"))); - - ASSERT_TRUE(fake_upstream_connection->write("24:DROP reply direction\n")); - ASSERT_TRUE(fake_upstream_connection->write("25:PASS2 reply direction\n")); - tcp_client->waitForData("25:PASS2 reply direction\n", false); - - ASSERT_TRUE(fake_upstream_connection->write("", true)); - tcp_client->waitForHalfClose(); - ASSERT_TRUE(tcp_client->write("", true)); - ASSERT_TRUE(fake_upstream_connection->waitForHalfClose()); - ASSERT_TRUE(fake_upstream_connection->waitForDisconnect()); -} } // namespace Envoy diff --git a/tests/cilium_tls_http_integration_test.cc b/tests/cilium_tls_http_integration_test.cc index 9d277c945..58ee44564 100644 --- a/tests/cilium_tls_http_integration_test.cc +++ b/tests/cilium_tls_http_integration_test.cc @@ -105,6 +105,8 @@ const std::string cilium_tls_http_proxy_config_fmt = R"EOF( "@type": type.googleapis.com/cilium.L7Policy access_log_path: "{{ test_udsdir }}/access_log.sock" - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router route_config: name: policy_enabled virtual_hosts: @@ -136,6 +138,8 @@ const std::string cilium_tls_http_proxy_config_fmt = R"EOF( "@type": type.googleapis.com/cilium.L7Policy access_log_path: "{{ test_udsdir }}/access_log.sock" - name: envoy.filters.http.router + typed_config: + "@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router route_config: name: policy_enabled virtual_hosts: @@ -338,11 +342,11 @@ class CiliumHttpTLSIntegrationTest : public CiliumHttpIntegrationTest { EXPECT_TRUE(upstream_request_->complete()); EXPECT_EQ(0, upstream_request_->bodyLength()); - test_server_->waitForGaugeEq("http.config_test.downstream_cx_ssl_active", 1); + test_server_->waitForGauge("http.config_test.downstream_cx_ssl_active", testing::Eq(1)); cleanupUpstreamAndDownstream(); - test_server_->waitForGaugeEq("http.config_test.downstream_cx_ssl_active", 0); + test_server_->waitForGauge("http.config_test.downstream_cx_ssl_active", testing::Eq(0)); } // Upstream diff --git a/tests/cilium_tls_tcp_integration_test.cc b/tests/cilium_tls_tcp_integration_test.cc index 81e41b533..44f77106f 100644 --- a/tests/cilium_tls_tcp_integration_test.cc +++ b/tests/cilium_tls_tcp_integration_test.cc @@ -7,9 +7,9 @@ #include #include -#include #include #include +#include #include #include "envoy/buffer/buffer.h" @@ -33,6 +33,7 @@ #include "test/test_common/test_time_system.h" #include "test/test_common/utility.h" +#include "absl/functional/any_invocable.h" #include "tests/cilium_tcp_integration.h" #include "tests/cilium_tls_integration.h" @@ -93,7 +94,6 @@ const std::string cilium_tls_tcp_proxy_config_fmt = R"EOF( - name: cilium.network typed_config: "@type": type.googleapis.com/cilium.NetworkFilter - proxylib: "proxylib/libcilium.so" - name: envoy.tcp_proxy typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy @@ -197,20 +197,23 @@ class CiliumTLSIntegrationTest : public CiliumTcpIntegrationTest { EXPECT_CALL(*mock_buffer_factory_, createBuffer_(_, _, _)) .Times(AtLeast(1)) - .WillOnce(Invoke([&](std::function below_low, std::function above_high, - std::function above_overflow) -> Buffer::Instance* { - client_write_buffer_ = - new NiceMock(below_low, above_high, above_overflow); - ON_CALL(*client_write_buffer_, move(_)) - .WillByDefault(Invoke(client_write_buffer_, &MockWatermarkBuffer::baseMove)); - ON_CALL(*client_write_buffer_, drain(_)) - .WillByDefault(Invoke(client_write_buffer_, &MockWatermarkBuffer::trackDrains)); - return client_write_buffer_; - })) - .WillRepeatedly(Invoke([](std::function below_low, std::function above_high, - std::function above_overflow) -> Buffer::Instance* { - return new Buffer::WatermarkBuffer(below_low, above_high, above_overflow); - })); + .WillOnce( + Invoke([&](absl::AnyInvocable below_low, absl::AnyInvocable above_high, + absl::AnyInvocable above_overflow) -> Buffer::Instance* { + client_write_buffer_ = new NiceMock( + std::move(below_low), std::move(above_high), std::move(above_overflow)); + ON_CALL(*client_write_buffer_, move(_)) + .WillByDefault(Invoke(client_write_buffer_, &MockWatermarkBuffer::baseMove)); + ON_CALL(*client_write_buffer_, drain(_)) + .WillByDefault(Invoke(client_write_buffer_, &MockWatermarkBuffer::trackDrains)); + return client_write_buffer_; + })) + .WillRepeatedly( + Invoke([](absl::AnyInvocable below_low, absl::AnyInvocable above_high, + absl::AnyInvocable above_overflow) -> Buffer::Instance* { + return new Buffer::WatermarkBuffer(std::move(below_low), std::move(above_high), + std::move(above_overflow)); + })); // Set up the SSL client. Network::Address::InstanceConstSharedPtr address = Ssl::getSslAddress(version_, lookupPort("tcp_proxy")); @@ -466,8 +469,8 @@ TEST_P(CiliumTLSProxyIntegrationTest, CiliumTLSProxyDownstreamFlush) { ASSERT_TRUE(fake_upstream_connection->write(data, true)); - test_server_->waitForCounterGe("cluster.tls-cluster.upstream_flow_control_paused_reading_total", - 1); + test_server_->waitForCounter("cluster.tls-cluster.upstream_flow_control_paused_reading_total", + testing::Ge(1)); EXPECT_EQ(test_server_->counter("cluster.tls-cluster.upstream_flow_control_resumed_reading_total") ->value(), 0); @@ -512,7 +515,7 @@ TEST_P(CiliumTLSProxyIntegrationTest, CiliumTLSProxyUpstreamFlush) { ASSERT_TRUE(tcp_client->write(data, true, true, std::chrono::milliseconds(30000))); - test_server_->waitForGaugeEq("tcp.tcp_stats.upstream_flush_active", 1); + test_server_->waitForGauge("tcp.tcp_stats.upstream_flush_active", testing::Eq(1)); ASSERT_TRUE(fake_upstream_connection->readDisable(false)); ASSERT_TRUE( fake_upstream_connection->waitForData(data.size(), nullptr, 3 * TestUtility::DefaultTimeout)); @@ -522,7 +525,7 @@ TEST_P(CiliumTLSProxyIntegrationTest, CiliumTLSProxyUpstreamFlush) { tcp_client->waitForHalfClose(); EXPECT_EQ(test_server_->counter("tcp.tcp_stats.upstream_flush_total")->value(), 1); - test_server_->waitForGaugeEq("tcp.tcp_stats.upstream_flush_active", 0); + test_server_->waitForGauge("tcp.tcp_stats.upstream_flush_active", testing::Eq(0)); } // Test that Envoy doesn't crash or assert when shutting down with an upstream @@ -552,7 +555,7 @@ TEST_P(CiliumTLSProxyIntegrationTest, CiliumTLSProxyUpstreamFlushEnvoyExit) { ASSERT_TRUE(tcp_client->write(data, true)); - test_server_->waitForGaugeEq("tcp.tcp_stats.upstream_flush_active", 1); + test_server_->waitForGauge("tcp.tcp_stats.upstream_flush_active", testing::Eq(1)); test_server_.reset(); ASSERT_TRUE(fake_upstream_connection->close()); ASSERT_TRUE(fake_upstream_connection->waitForDisconnect()); @@ -615,7 +618,6 @@ const std::string cilium_tls_downstream_tcp_proxy_config_fmt = R"EOF( - name: cilium.network typed_config: "@type": type.googleapis.com/cilium.NetworkFilter - proxylib: "proxylib/libcilium.so" - name: envoy.tcp_proxy typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy @@ -631,7 +633,6 @@ const std::string cilium_tls_downstream_tcp_proxy_config_fmt = R"EOF( - name: cilium.network typed_config: "@type": type.googleapis.com/cilium.NetworkFilter - proxylib: "proxylib/libcilium.so" - name: envoy.tcp_proxy typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy diff --git a/tests/cilium_websocket_codec_integration_test.cc b/tests/cilium_websocket_codec_integration_test.cc index c4d1f70bf..03b2fed2e 100644 --- a/tests/cilium_websocket_codec_integration_test.cc +++ b/tests/cilium_websocket_codec_integration_test.cc @@ -124,7 +124,7 @@ TEST_P(CiliumWebSocketIntegrationTest, CiliumWebSocketUpstreamWritesFirst) { FakeRawConnectionPtr fake_upstream_connection; ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection)); - test_server_->waitForCounterGe("websocket.ping_sent_count", 1); + test_server_->waitForCounter("websocket.ping_sent_count", testing::Ge(1)); ASSERT_TRUE(fake_upstream_connection->write("hello")); tcp_client->waitForData("hello"); @@ -154,7 +154,7 @@ TEST_P(CiliumWebSocketIntegrationTest, CiliumWebSocketUpstreamDisconnect) { ASSERT_TRUE(fake_upstream_connection->waitForData(5, &received)); ASSERT_EQ(received, "hello"); - test_server_->waitForCounterGe("websocket.ping_sent_count", 1); + test_server_->waitForCounter("websocket.ping_sent_count", testing::Ge(1)); ASSERT_TRUE(fake_upstream_connection->write("world")); ASSERT_TRUE(fake_upstream_connection->close()); @@ -180,7 +180,7 @@ TEST_P(CiliumWebSocketIntegrationTest, CiliumWebSocketDownstreamDisconnect) { ASSERT_TRUE(fake_upstream_connection->write("world")); tcp_client->waitForData("world"); - test_server_->waitForCounterGe("websocket.ping_sent_count", 1); + test_server_->waitForCounter("websocket.ping_sent_count", testing::Ge(1)); ASSERT_TRUE(tcp_client->write("hello", true)); ASSERT_TRUE(fake_upstream_connection->waitForData(10, &received)); @@ -207,7 +207,7 @@ TEST_P(CiliumWebSocketIntegrationTest, CiliumWebSocketLargeWrite) { ASSERT_TRUE(fake_upstream_connection->write(data)); tcp_client->waitForData(data); - test_server_->waitForCounterGe("websocket.ping_sent_count", 1); + test_server_->waitForCounter("websocket.ping_sent_count", testing::Ge(1)); tcp_client->close(); ASSERT_TRUE(fake_upstream_connection->waitForHalfClose()); @@ -247,7 +247,7 @@ TEST_P(CiliumWebSocketIntegrationTest, CiliumWebSocketDownstreamFlush) { FakeRawConnectionPtr fake_upstream_connection; ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection)); - test_server_->waitForCounterGe("websocket.ping_sent_count", 1); + test_server_->waitForCounter("websocket.ping_sent_count", testing::Ge(1)); tcp_client->readDisable(true); ASSERT_TRUE(tcp_client->write("", true)); @@ -258,7 +258,8 @@ TEST_P(CiliumWebSocketIntegrationTest, CiliumWebSocketDownstreamFlush) { ASSERT_TRUE(fake_upstream_connection->write(data, true)); - test_server_->waitForCounterGe("cluster.cluster1.upstream_flow_control_paused_reading_total", 1); + test_server_->waitForCounter("cluster.cluster1.upstream_flow_control_paused_reading_total", + testing::Ge(1)); EXPECT_EQ(test_server_->counter("cluster.cluster1.upstream_flow_control_resumed_reading_total") ->value(), 0); @@ -290,7 +291,7 @@ TEST_P(CiliumWebSocketIntegrationTest, CiliumWebSocketUpstreamFlush) { FakeRawConnectionPtr fake_upstream_connection; ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection)); - test_server_->waitForCounterGe("websocket.ping_sent_count", 1); + test_server_->waitForCounter("websocket.ping_sent_count", testing::Ge(1)); ASSERT_TRUE(fake_upstream_connection->readDisable(true)); ASSERT_TRUE(fake_upstream_connection->write("", true)); @@ -301,7 +302,7 @@ TEST_P(CiliumWebSocketIntegrationTest, CiliumWebSocketUpstreamFlush) { ASSERT_TRUE(tcp_client->write(data, true, true, std::chrono::milliseconds(30000))); - test_server_->waitForGaugeEq("tcp.tcp_stats.upstream_flush_active", 1); + test_server_->waitForGauge("tcp.tcp_stats.upstream_flush_active", testing::Eq(1)); ASSERT_TRUE(fake_upstream_connection->readDisable(false)); std::string received; @@ -312,7 +313,7 @@ TEST_P(CiliumWebSocketIntegrationTest, CiliumWebSocketUpstreamFlush) { tcp_client->waitForHalfClose(); EXPECT_EQ(test_server_->counter("tcp.tcp_stats.upstream_flush_total")->value(), 1); - test_server_->waitForGaugeEq("tcp.tcp_stats.upstream_flush_active", 0); + test_server_->waitForGauge("tcp.tcp_stats.upstream_flush_active", testing::Eq(0)); } // Test that Envoy doesn't crash or assert when shutting down with an upstream @@ -336,11 +337,11 @@ TEST_P(CiliumWebSocketIntegrationTest, CiliumWebSocketUpstreamFlushEnvoyExit) { // it's thread before tcp_client starts writing. tcp_client->waitForHalfClose(); - test_server_->waitForCounterGe("websocket.ping_sent_count", 1); + test_server_->waitForCounter("websocket.ping_sent_count", testing::Ge(1)); ASSERT_TRUE(tcp_client->write(data, true)); - test_server_->waitForGaugeEq("tcp.tcp_stats.upstream_flush_active", 1); + test_server_->waitForGauge("tcp.tcp_stats.upstream_flush_active", testing::Eq(1)); test_server_.reset(); ASSERT_TRUE(fake_upstream_connection->close()); ASSERT_TRUE(fake_upstream_connection->waitForDisconnect()); diff --git a/tests/cilium_websocket_encap_integration_test.cc b/tests/cilium_websocket_encap_integration_test.cc index eb2fc758d..19968f691 100644 --- a/tests/cilium_websocket_encap_integration_test.cc +++ b/tests/cilium_websocket_encap_integration_test.cc @@ -82,7 +82,6 @@ const std::string cilium_tcp_proxy_config_fmt = R"EOF( - name: cilium.network typed_config: "@type": type.googleapis.com/cilium.NetworkFilter - proxylib: "proxylib/libcilium.so" - name: envoy.tcp_proxy typed_config: "@type": type.googleapis.com/envoy.extensions.filters.network.tcp_proxy.v3.TcpProxy @@ -195,7 +194,7 @@ TEST_P(CiliumWebSocketIntegrationTest, CiliumWebSocketHandshakeNonHTTPResponse) "world")); ASSERT_TRUE(fake_upstream_connection->close()); ASSERT_TRUE(fake_upstream_connection->waitForDisconnect()); - test_server_->waitForCounterGe("websocket.handshake_not_http", 1); + test_server_->waitForCounter("websocket.handshake_not_http", testing::Ge(1)); // Handshake errors close the downstream with NoFlush, which may be observed as either a // graceful FIN or an RST. The counter above is the behavior under test. @@ -226,7 +225,7 @@ TEST_P(CiliumWebSocketIntegrationTest, CiliumWebSocketHandshakeInvalidResponse) fmt::format(fmt::runtime(HANDSHAKE_RESPONSE_FMT), "invalid-hash"); ASSERT_TRUE(fake_upstream_connection->write(handshake_response)); - test_server_->waitForCounterGe("websocket.handshake_invalid_websocket_response", 1); + test_server_->waitForCounter("websocket.handshake_invalid_websocket_response", testing::Ge(1)); // Handshake errors close the downstream with NoFlush, which may be observed as either a // graceful FIN or an RST. The counter above is the behavior under test. @@ -452,7 +451,8 @@ TEST_P(CiliumWebSocketIntegrationTest, CiliumWebSocketDownstreamFlush) { ASSERT_TRUE(fake_upstream_connection->write("\x82\x7f\x03\x20\0\0"s)); ASSERT_TRUE(fake_upstream_connection->write(data, true)); - test_server_->waitForCounterGe("cluster.cluster1.upstream_flow_control_paused_reading_total", 1); + test_server_->waitForCounter("cluster.cluster1.upstream_flow_control_paused_reading_total", + testing::Ge(1)); EXPECT_EQ(test_server_->counter("cluster.cluster1.upstream_flow_control_resumed_reading_total") ->value(), 0); @@ -512,7 +512,7 @@ TEST_P(CiliumWebSocketIntegrationTest, CiliumWebSocketUpstreamFlush) { ASSERT_TRUE(fake_upstream_connection->waitForDisconnect()); tcp_client->waitForHalfClose(); - test_server_->waitForGaugeEq("tcp.tcp_stats.upstream_flush_active", 0); + test_server_->waitForGauge("tcp.tcp_stats.upstream_flush_active", testing::Eq(0)); EXPECT_EQ(test_server_->counter("tcp.tcp_stats.upstream_flush_total")->value(), 1); } @@ -551,7 +551,7 @@ TEST_P(CiliumWebSocketIntegrationTest, CiliumWebSocketUpstreamFlushEnvoyExit) { ASSERT_TRUE(tcp_client->write(data, true)); - // test_server_->waitForCounterGe("tcp.tcp_stats.upstream_flush_total", 1); + // test_server_->waitForCounter("tcp.tcp_stats.upstream_flush_total", testing::Ge(1)); test_server_.reset(); ASSERT_TRUE(fake_upstream_connection->close()); diff --git a/tests/cilium_websocket_policy_integration_test.cc b/tests/cilium_websocket_policy_integration_test.cc index 17b682c5a..aa98de3d7 100644 --- a/tests/cilium_websocket_policy_integration_test.cc +++ b/tests/cilium_websocket_policy_integration_test.cc @@ -268,7 +268,7 @@ TEST_P(CiliumWebSocketIntegrationTest, CiliumWebSocketUpstreamWritesFirst) { ASSERT_TRUE(fake_upstreams_[0]->waitForRawConnection(fake_upstream_connection)); // wait for at least one more ping to arrive as proof that the handshake is ready - test_server_->waitForCounterGe("websocket.ping_sent_count", previous_ping_count + 1); + test_server_->waitForCounter("websocket.ping_sent_count", testing::Ge(previous_ping_count + 1)); ASSERT_TRUE(fake_upstream_connection->write("hello")); tcp_client->waitForData("hello"); diff --git a/tests/health_check_sink_server.cc b/tests/health_check_sink_server.cc index 7a2915b05..bc81c98fb 100644 --- a/tests/health_check_sink_server.cc +++ b/tests/health_check_sink_server.cc @@ -2,6 +2,7 @@ #include #include +#include #include #include "envoy/data/core/v3/health_check_event.pb.h" @@ -11,7 +12,6 @@ #include "absl/base/thread_annotations.h" #include "absl/synchronization/mutex.h" #include "absl/time/time.h" -#include "absl/types/optional.h" #include "tests/uds_server.h" namespace Envoy { @@ -28,7 +28,7 @@ void HealthCheckSinkServer::clear() { events_.clear(); } -absl::optional +std::optional HealthCheckSinkServer::waitForEvent(std::chrono::milliseconds timeout) { absl::MutexLock lock(&mutex_); auto predicate = [this]() ABSL_SHARED_LOCKS_REQUIRED(mutex_) { diff --git a/tests/health_check_sink_server.h b/tests/health_check_sink_server.h index 4230e0153..ac88a4520 100644 --- a/tests/health_check_sink_server.h +++ b/tests/health_check_sink_server.h @@ -2,6 +2,7 @@ #include #include +#include #include #include "envoy/data/core/v3/health_check_event.pb.h" @@ -11,7 +12,6 @@ #include "absl/base/thread_annotations.h" #include "absl/synchronization/mutex.h" -#include "absl/types/optional.h" #include "tests/uds_server.h" namespace Envoy { @@ -22,7 +22,7 @@ class HealthCheckSinkServer : public UDSServer { ~HealthCheckSinkServer() override; void clear(); - absl::optional + std::optional waitForEvent(std::chrono::milliseconds timeout = TestUtility::DefaultTimeout); template diff --git a/tests/health_check_sink_test.cc b/tests/health_check_sink_test.cc index 6eb412f24..04be20fc3 100644 --- a/tests/health_check_sink_test.cc +++ b/tests/health_check_sink_test.cc @@ -1,6 +1,7 @@ #include #include +#include #include "envoy/data/core/v3/health_check_event.pb.h" #include "envoy/registry/registry.h" @@ -41,7 +42,7 @@ TEST(HealthCheckEventPipeSinkFactory, createHealthCheckEventSink) { cilium::HealthCheckEventPipeSink config; config.set_path("test_path"); Envoy::Protobuf::Any typed_config; - typed_config.PackFrom(config); + std::ignore = typed_config.PackFrom(config); NiceMock context; EXPECT_NE(factory->createHealthCheckEventSink(typed_config, context), nullptr); @@ -68,7 +69,7 @@ TEST(HealthCheckEventPipeSink, logTest) { EXPECT_TRUE(config.path().empty()); config.set_path(normal_path); Envoy::Protobuf::Any typed_config; - typed_config.PackFrom(config); + std::ignore = typed_config.PackFrom(config); NiceMock context; auto pipe_sink = factory->createHealthCheckEventSink(typed_config, context); EXPECT_NE(pipe_sink, nullptr); @@ -132,7 +133,7 @@ TEST(HealthCheckEventPipeSink, logTest) { // Set up 3rd client on a different socket cilium::HealthCheckEventPipeSink config3; config3.set_path(abstract_name); - typed_config.PackFrom(config3); + std::ignore = typed_config.PackFrom(config3); auto pipe_sink3 = factory->createHealthCheckEventSink(typed_config, context); EXPECT_NE(pipe_sink3, nullptr); diff --git a/tests/network_filter_test.cc b/tests/network_filter_test.cc index 12055972f..b90b54814 100644 --- a/tests/network_filter_test.cc +++ b/tests/network_filter_test.cc @@ -82,7 +82,7 @@ TEST(CiliumNetworkFilterTest, MissingMetadataNamespaceDoesNotCrash) { std::make_shared( 0, 456, false, false, 80, std::string("pod"), std::string(""), std::make_shared(), 7, ""), - StreamInfo::FilterState::StateType::ReadOnly, StreamInfo::FilterState::LifeSpan::Connection); + StreamInfo::FilterState::LifeSpan::Connection); instance.initializeReadFilterCallbacks(callbacks); Filter::CiliumL3::NetworkFilterTestPeer::setL7Proto(instance, "test.l7"); diff --git a/vendor/github.com/cilium/kafka/LICENSE b/vendor/github.com/cilium/kafka/LICENSE deleted file mode 100644 index 0666ffd32..000000000 --- a/vendor/github.com/cilium/kafka/LICENSE +++ /dev/null @@ -1,19 +0,0 @@ -Copyright (c) 2015-2016 Optiopay - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/vendor/github.com/cilium/kafka/proto/doc.go b/vendor/github.com/cilium/kafka/proto/doc.go deleted file mode 100644 index ae0ab26d3..000000000 --- a/vendor/github.com/cilium/kafka/proto/doc.go +++ /dev/null @@ -1,6 +0,0 @@ -/* - -Package proto provides kafka binary protocol implementation. - -*/ -package proto diff --git a/vendor/github.com/cilium/kafka/proto/errors.go b/vendor/github.com/cilium/kafka/proto/errors.go deleted file mode 100644 index f62ae460b..000000000 --- a/vendor/github.com/cilium/kafka/proto/errors.go +++ /dev/null @@ -1,101 +0,0 @@ -package proto - -import ( - "fmt" -) - -var ( - ErrUnknown = &KafkaError{-1, "unknown error"} - ErrOffsetOutOfRange = &KafkaError{1, "offset out of range"} - ErrInvalidMessage = &KafkaError{2, "invalid message"} - ErrUnknownTopicOrPartition = &KafkaError{3, "unknown topic or partition"} - ErrInvalidMessageSize = &KafkaError{4, "invalid message size"} - ErrLeaderNotAvailable = &KafkaError{5, "leader not available"} - ErrNotLeaderForPartition = &KafkaError{6, "not leader for partition"} - ErrRequestTimeout = &KafkaError{7, "request timeed out"} - ErrBrokerNotAvailable = &KafkaError{8, "broker not available"} - ErrReplicaNotAvailable = &KafkaError{9, "replica not available"} - ErrMessageSizeTooLarge = &KafkaError{10, "message size too large"} - ErrScaleControllerEpoch = &KafkaError{11, "scale controller epoch"} - ErrOffsetMetadataTooLarge = &KafkaError{12, "offset metadata too large"} - ErrNetwork = &KafkaError{13, "server disconnected before response was received"} - ErrOffsetLoadInProgress = &KafkaError{14, "offsets load in progress"} - ErrNoCoordinator = &KafkaError{15, "consumer coordinator not available"} - ErrNotCoordinator = &KafkaError{16, "not coordinator for consumer"} - ErrInvalidTopic = &KafkaError{17, "operation on an invalid topic"} - ErrRecordListTooLarge = &KafkaError{18, "message batch larger than the configured segment size"} - ErrNotEnoughReplicas = &KafkaError{19, "not enough in-sync replicas"} - ErrNotEnoughReplicasAfterAppend = &KafkaError{20, "messages are written to the log, but to fewer in-sync replicas than required"} - ErrInvalidRequiredAcks = &KafkaError{21, "invalid value for required acks"} - ErrIllegalGeneration = &KafkaError{22, "consumer generation id is not valid"} - ErrInconsistentPartitionAssignmentStrategy = &KafkaError{23, "partition assignment strategy does not match that of the group"} - ErrUnknownParititonAssignmentStrategy = &KafkaError{24, "partition assignment strategy is unknown to the broker"} - ErrUnknownConsumerID = &KafkaError{25, "coordinator is not aware of this consumer"} - ErrInvalidSessionTimeout = &KafkaError{26, "invalid session timeout"} - ErrRebalanceInProgress = &KafkaError{27, "group is rebalancing, so a rejoin is needed"} - ErrInvalidCommitOffsetSize = &KafkaError{28, "offset data size is not valid"} - ErrTopicAuthorizationFailed = &KafkaError{29, "topic authorization failed"} - ErrGroupAuthorizationFailed = &KafkaError{30, "group authorization failed"} - ErrClusterAuthorizationFailed = &KafkaError{31, "cluster authorization failed"} - ErrInvalidTimeStamp = &KafkaError{32, "timestamp of the message is out of acceptable range"} - - errnoToErr = map[int16]error{ - -1: ErrUnknown, - 1: ErrOffsetOutOfRange, - 2: ErrInvalidMessage, - 3: ErrUnknownTopicOrPartition, - 4: ErrInvalidMessageSize, - 5: ErrLeaderNotAvailable, - 6: ErrNotLeaderForPartition, - 7: ErrRequestTimeout, - 8: ErrBrokerNotAvailable, - 9: ErrReplicaNotAvailable, - 10: ErrMessageSizeTooLarge, - 11: ErrScaleControllerEpoch, - 12: ErrOffsetMetadataTooLarge, - 13: ErrNetwork, - 14: ErrOffsetLoadInProgress, - 15: ErrNoCoordinator, - 16: ErrNotCoordinator, - 17: ErrInvalidTopic, - 18: ErrRecordListTooLarge, - 19: ErrNotEnoughReplicas, - 20: ErrNotEnoughReplicasAfterAppend, - 21: ErrInvalidRequiredAcks, - 22: ErrIllegalGeneration, - 23: ErrInconsistentPartitionAssignmentStrategy, - 24: ErrUnknownParititonAssignmentStrategy, - 25: ErrUnknownConsumerID, - 26: ErrInvalidSessionTimeout, - 27: ErrRebalanceInProgress, - 28: ErrInvalidCommitOffsetSize, - 29: ErrTopicAuthorizationFailed, - 30: ErrGroupAuthorizationFailed, - 31: ErrClusterAuthorizationFailed, - 32: ErrInvalidCommitOffsetSize, - } -) - -type KafkaError struct { - errno int16 - message string -} - -func (err *KafkaError) Error() string { - return fmt.Sprintf("%s (%d)", err.message, err.errno) -} - -func (err *KafkaError) Errno() int { - return int(err.errno) -} - -func errFromNo(errno int16) error { - if errno == 0 { - return nil - } - err, ok := errnoToErr[errno] - if !ok { - return fmt.Errorf("unknown kafka error %d", errno) - } - return err -} diff --git a/vendor/github.com/cilium/kafka/proto/messages.go b/vendor/github.com/cilium/kafka/proto/messages.go deleted file mode 100644 index d3f237ba2..000000000 --- a/vendor/github.com/cilium/kafka/proto/messages.go +++ /dev/null @@ -1,2022 +0,0 @@ -package proto - -import ( - "bytes" - "compress/gzip" - "encoding/binary" - "errors" - "hash/crc32" - "io" - "io/ioutil" - "time" - - "github.com/golang/snappy" -) - -/* - -Kafka wire protocol implemented as described in -https://cwiki.apache.org/confluence/display/KAFKA/A+Guide+To+The+Kafka+Protocol#AGuideToTheKafkaProtocol-Messagesets - -*/ - -const ( - KafkaV0 int16 = iota - KafkaV1 - KafkaV2 - KafkaV3 - KafkaV4 - KafkaV5 -) - -const ( - ProduceReqKind = 0 - FetchReqKind = 1 - OffsetReqKind = 2 - MetadataReqKind = 3 - OffsetCommitReqKind = 8 - OffsetFetchReqKind = 9 - ConsumerMetadataReqKind = 10 - - // receive the latest offset (i.e. the offset of the next coming message) - OffsetReqTimeLatest = -1 - - // receive the earliest available offset. Note that because offsets are - // pulled in descending order, asking for the earliest offset will always - // return you a single element. - OffsetReqTimeEarliest = -2 - - // Server will not send any response. - RequiredAcksNone = 0 - - // Server will block until the message is committed by all in sync replicas - // before sending a response. - RequiredAcksAll = -1 - - // Server will wait the data is written to the local log before sending a - // response. - RequiredAcksLocal = 1 -) - -type Compression int8 - -const ( - CompressionNone Compression = 0 - CompressionGzip Compression = 1 - CompressionSnappy Compression = 2 -) - -// ParserConfig is optional configuration for the parser. It can be configured via -// SetParserConfig -type ParserConfig struct { - // SimplifiedMessageSetParsing enables a simplified version of the - // MessageSet parser which will not split MessageSet into slices of - // Message structures. Instead, the entire MessageSet will be read - // over. This mode improves parsing speed due to reduce memory read at - // the cost of not providing access to the message payload after - // parsing. - SimplifiedMessageSetParsing bool -} - -var ( - conf ParserConfig -) - -// ConfigureParser configures the parser. It must be called prior to parsing -// any messages as the structure is currently not prepared for concurrent -// access. -func ConfigureParser(c ParserConfig) error { - conf = c - return nil -} - -func boolToInt8(val bool) int8 { - res := int8(0) - if val { - res = 1 - } - return res -} - -// discard tries to discard bytes -// from the io.Reader in chunks of maxDiscardSize(4096) bytes -// to avoid allocating huge amount of memory in -// one go. -func discard(r io.Reader, n int32) { - remBytes := n - var delBytes int32 - - delBytes = 0 - for remBytes > 0 { - if remBytes > maxDiscardSize { - delBytes = maxDiscardSize - remBytes = remBytes - maxDiscardSize - } else { - delBytes = remBytes - remBytes = 0 - } - io.CopyN(ioutil.Discard, r, int64(delBytes)) - } -} - -// ReadReq returns request kind ID and byte representation of the whole message -// in wire protocol format. -func ReadReq(r io.Reader) (requestKind int16, b []byte, err error) { - dec := NewDecoder(r) - msgSize := dec.DecodeInt32() - if err := dec.Err(); err != nil { - return 0, nil, err - } - - if msgSize <= 0 { - return 0, nil, io.ErrUnexpectedEOF - } - - requestKind = dec.DecodeInt16() - if err := dec.Err(); err != nil { - discard(r, msgSize) - return 0, nil, err - } - // size of the message + size of the message itself - b, err = allocParseBuf(int(msgSize + 4)) - if err != nil { - if msgSize > 2 { - // We have already read the requestKind - discard(r, msgSize-2) - } - return 0, nil, err - } - - binary.BigEndian.PutUint32(b, uint32(msgSize)) - - // only write back requestKind if it was included in messageSize - if len(b) >= 6 { - binary.BigEndian.PutUint16(b[4:], uint16(requestKind)) - } - - // read rest of request into allocated buffer if we allocated for it - if len(b) > 6 { - if _, err := io.ReadFull(r, b[6:]); err != nil { - return 0, nil, err - } - } - - return requestKind, b, nil -} - -// ReadResp returns message correlation ID and byte representation of the whole -// message in wire protocol that is returned when reading from given stream, -// including 4 bytes of message size itself. -// Byte representation returned by ReadResp can be parsed by all response -// reeaders to transform it into specialized response structure. -func ReadResp(r io.Reader) (correlationID int32, b []byte, err error) { - dec := NewDecoder(r) - msgSize := dec.DecodeInt32() - if err := dec.Err(); err != nil { - return 0, nil, err - } - - if msgSize <= 0 { - return 0, nil, io.ErrUnexpectedEOF - } - - correlationID = dec.DecodeInt32() - if err := dec.Err(); err != nil { - discard(r, msgSize) - return 0, nil, err - } - // size of the message + size of the message itself - b, err = allocParseBuf(int(msgSize + 4)) - if err != nil { - if msgSize > 4 { - // We have already read the correlationID - discard(r, msgSize-4) - } - return 0, nil, err - } - - binary.BigEndian.PutUint32(b, uint32(msgSize)) - binary.BigEndian.PutUint32(b[4:], uint32(correlationID)) - _, err = io.ReadFull(r, b[8:]) - return correlationID, b, err -} - -// Message represents single entity of message set. -type Message struct { - Key []byte - Value []byte - Offset int64 // set when fetching and after successful producing - Crc uint32 // set when fetching, ignored when producing - Topic string // set when fetching, ignored when producing - Partition int32 // set when fetching, ignored when producing - TipOffset int64 // set when fetching, ignored when processing -} - -// ComputeCrc returns crc32 hash for given message content. -func ComputeCrc(m *Message, compression Compression) uint32 { - var buf bytes.Buffer - enc := NewEncoder(&buf) - enc.EncodeInt8(0) // magic byte is always 0 - enc.EncodeInt8(int8(compression)) - enc.EncodeBytes(m.Key) - enc.EncodeBytes(m.Value) - return crc32.ChecksumIEEE(buf.Bytes()) -} - -// writeMessageSet writes a Message Set into w. -// It returns the number of bytes written and any error. -func writeMessageSet(w io.Writer, messages []*Message, compression Compression) (int, error) { - // The RECORDS type is nullable. - if messages == nil { - return -1, nil - } - - if len(messages) == 0 { - return 0, nil - } - - // NOTE(caleb): it doesn't appear to be documented, but I observed that the - // Java client sets the offset of the synthesized message set for a group of - // compressed messages to be the offset of the last message in the set. - compressOffset := messages[len(messages)-1].Offset - switch compression { - case CompressionGzip: - var buf bytes.Buffer - gz := gzip.NewWriter(&buf) - if _, err := writeMessageSet(gz, messages, CompressionNone); err != nil { - return 0, err - } - if err := gz.Close(); err != nil { - return 0, err - } - messages = []*Message{ - { - Value: buf.Bytes(), - Offset: compressOffset, - }, - } - case CompressionSnappy: - var buf bytes.Buffer - if _, err := writeMessageSet(&buf, messages, CompressionNone); err != nil { - return 0, err - } - messages = []*Message{ - { - Value: snappy.Encode(nil, buf.Bytes()), - Offset: compressOffset, - }, - } - } - - totalSize := 0 - b, err := newSliceWriter(0) - if err != nil { - return 0, err - } - - for _, message := range messages { - bsize := 26 + len(message.Key) + len(message.Value) - if err := b.Reset(bsize); err != nil { - return 0, err - } - - enc := NewEncoder(b) - enc.EncodeInt64(message.Offset) - msize := int32(14 + len(message.Key) + len(message.Value)) - enc.EncodeInt32(msize) - enc.EncodeUint32(0) // crc32 placeholder - enc.EncodeInt8(0) // magic byte - enc.EncodeInt8(int8(compression)) - enc.EncodeBytes(message.Key) - enc.EncodeBytes(message.Value) - - if err := enc.Err(); err != nil { - return totalSize, err - } - - const hsize = 8 + 4 + 4 // offset + message size + crc32 - const crcoff = 8 + 4 // offset + message size - binary.BigEndian.PutUint32(b.buf[crcoff:crcoff+4], crc32.ChecksumIEEE(b.buf[hsize:bsize])) - - if n, err := w.Write(b.Slice()); err != nil { - return totalSize, err - } else { - totalSize += n - } - - } - return totalSize, nil -} - -type slicewriter struct { - buf []byte - pos int - size int -} - -func newSliceWriter(bufsize int) (*slicewriter, error) { - buf, err := allocParseBuf(bufsize) - if err != nil { - return nil, err - } - - return &slicewriter{ - buf: buf, - pos: 0, - }, nil -} - -func (w *slicewriter) Write(p []byte) (int, error) { - if len(w.buf) < w.pos+len(p) { - return 0, errors.New("buffer too small") - } - copy(w.buf[w.pos:], p) - w.pos += len(p) - return len(p), nil -} - -func (w *slicewriter) Reset(size int) error { - if size > len(w.buf) { - var err error - - w.buf, err = allocParseBuf(size + 1000) // allocate a bit more than required - if err != nil { - return err - } - } - w.size = size - w.pos = 0 - return nil -} - -func (w *slicewriter) Slice() []byte { - return w.buf[:w.pos] -} - -// readMessageSet reads and return messages from the stream. -// The size is known before a message set is decoded. -// Because kafka is sending message set directly from the drive, it might cut -// off part of the last message. This also means that the last message can be -// shorter than the header is saying. In such case just ignore the last -// malformed message from the set and returned earlier data. -// The version refers to the kafka version used for the requests and responses. -func readMessageSet(r io.Reader, size int32, version int16) ([]*Message, error) { - // The RECORDS type is nullable. - if size < 0 { // null array - return nil, nil - } - - if size > maxParseBufSize { - return nil, messageSizeError(int(size)) - } - - rd := io.LimitReader(r, int64(size)) - - if conf.SimplifiedMessageSetParsing { - msgbuf, err := allocParseBuf(int(size)) - if err != nil { - return nil, err - } - - if _, err := io.ReadFull(rd, msgbuf); err != nil { - return nil, err - } - return make([]*Message, 0, 0), nil - } - - dec := NewDecoder(rd) - set := make([]*Message, 0, 256) - - for { - offset := dec.DecodeInt64() - if err := dec.Err(); err != nil { - if err == io.EOF || err == io.ErrUnexpectedEOF { - return set, nil - } - return nil, err - } - // single message size - size := dec.DecodeInt32() - if err := dec.Err(); err != nil { - if err == io.EOF || err == io.ErrUnexpectedEOF { - return set, nil - } - return nil, err - } - - // Skip over empty messages - if size <= int32(0) { - return set, nil - } - - msgbuf, err := allocParseBuf(int(size)) - if err != nil { - return nil, err - } - - if _, err := io.ReadFull(rd, msgbuf); err != nil { - if err == io.EOF || err == io.ErrUnexpectedEOF { - return set, nil - } - return nil, err - } - msgdec := NewDecoder(bytes.NewBuffer(msgbuf)) - - msg := &Message{ - Offset: offset, - Crc: msgdec.DecodeUint32(), - } - - // MessageSet with no payload - if size <= int32(4) { - set = append(set, msg) - return set, nil - } - - if msg.Crc != crc32.ChecksumIEEE(msgbuf[4:]) { - // ignore this message and because we want to have constant - // history, do not process anything more - return set, nil - } - - // magic byte - _ = msgdec.DecodeInt8() - - attributes := msgdec.DecodeInt8() - - if version >= KafkaV1 { - // timestamp - _ = msgdec.DecodeInt64() - } - - switch compression := Compression(attributes & 3); compression { - case CompressionNone: - msg.Key = msgdec.DecodeBytes() - msg.Value = msgdec.DecodeBytes() - if err := msgdec.Err(); err != nil { - return nil, err - } - set = append(set, msg) - case CompressionGzip, CompressionSnappy: - _ = msgdec.DecodeBytes() // ignore key - val := msgdec.DecodeBytes() - if err := msgdec.Err(); err != nil { - return nil, err - } - var decoded []byte - switch compression { - case CompressionGzip: - cr, err := gzip.NewReader(bytes.NewReader(val)) - if err != nil { - return nil, err - } - decoded, err = ioutil.ReadAll(cr) - if err != nil { - return nil, err - } - _ = cr.Close() - case CompressionSnappy: - var err error - decoded, err = snappyDecode(val) - if err != nil { - return nil, err - } - } - msgs, err := readMessageSet(bytes.NewReader(decoded), int32(len(decoded)), version) - if err != nil { - return nil, err - } - set = append(set, msgs...) - default: - return nil, err - } - } -} - -type MetadataReq struct { - Version int16 - CorrelationID int32 - ClientID string - Topics []string - AllowAutoTopicCreation bool // >= KafkaV4 only -} - -func ReadMetadataReq(r io.Reader) (*MetadataReq, error) { - var req MetadataReq - dec := NewDecoder(r) - - // total message size - _ = dec.DecodeInt32() - // api key - _ = dec.DecodeInt16() - req.Version = dec.DecodeInt16() - req.CorrelationID = dec.DecodeInt32() - req.ClientID = dec.DecodeString() - len, err := dec.DecodeArrayLen(true) // nullable - if err != nil { - return nil, err - } - if len < 0 { // null array - req.Topics = nil - } else { - req.Topics = make([]string, len) - } - - for i := range req.Topics { - req.Topics[i] = dec.DecodeString() - } - - if req.Version >= KafkaV4 { - req.AllowAutoTopicCreation = dec.DecodeInt8() != 0 - } - - if dec.Err() != nil { - return nil, dec.Err() - } - return &req, nil -} - -func (r *MetadataReq) Bytes(version int16) ([]byte, error) { - var buf bytes.Buffer - enc := NewEncoder(&buf) - - // message size - for now just placeholder - enc.Encode(int32(0)) - enc.Encode(int16(MetadataReqKind)) - enc.Encode(r.Version) - enc.Encode(r.CorrelationID) - enc.Encode(r.ClientID) - - enc.EncodeArrayLen(r.Topics) - for _, name := range r.Topics { - enc.Encode(name) - } - - if version >= KafkaV4 { - enc.Encode(boolToInt8(r.AllowAutoTopicCreation)) - } - - if enc.Err() != nil { - return nil, enc.Err() - } - - // update the message size information - b := buf.Bytes() - binary.BigEndian.PutUint32(b, uint32(len(b)-4)) - - return b, nil -} - -func (r *MetadataReq) WriteTo(w io.Writer, version int16) (int64, error) { - b, err := r.Bytes(version) - if err != nil { - return 0, err - } - n, err := w.Write(b) - return int64(n), err -} - -type MetadataResp struct { - CorrelationID int32 - ThrottleTime time.Duration // >= KafkaV3 - Brokers []MetadataRespBroker - ClusterID string // >= KafkaV2 - ControllerID int32 // >= KafkaV1 - Topics []MetadataRespTopic -} - -type MetadataRespBroker struct { - NodeID int32 - Host string - Port int32 - Rack string // >= KafkaV1 -} - -type MetadataRespTopic struct { - Name string - Err error - IsInternal bool // >= KafkaV1 - Partitions []MetadataRespPartition -} - -type MetadataRespPartition struct { - Err error - ID int32 - Leader int32 - Replicas []int32 - Isrs []int32 -} - -func (r *MetadataResp) Bytes(version int16) ([]byte, error) { - var buf bytes.Buffer - enc := NewEncoder(&buf) - - // message size - for now just placeholder - enc.Encode(int32(0)) - enc.Encode(r.CorrelationID) - - if version >= KafkaV3 { - enc.Encode(r.ThrottleTime) - } - - enc.EncodeArrayLen(r.Brokers) - for _, broker := range r.Brokers { - enc.Encode(broker.NodeID) - enc.Encode(broker.Host) - enc.Encode(broker.Port) - - if version >= KafkaV1 { - enc.Encode(broker.Rack) - } - } - - if version >= KafkaV2 { - enc.Encode(r.ClusterID) - } - - if version >= KafkaV1 { - enc.Encode(r.ControllerID) - } - - enc.EncodeArrayLen(r.Topics) - for _, topic := range r.Topics { - enc.EncodeError(topic.Err) - enc.Encode(topic.Name) - - if version >= KafkaV1 { - enc.Encode(boolToInt8(topic.IsInternal)) - } - - enc.EncodeArrayLen(topic.Partitions) - for _, part := range topic.Partitions { - enc.EncodeError(part.Err) - enc.Encode(part.ID) - enc.Encode(part.Leader) - enc.Encode(part.Replicas) - enc.Encode(part.Isrs) - } - } - - if enc.Err() != nil { - return nil, enc.Err() - } - - // update the message size information - b := buf.Bytes() - binary.BigEndian.PutUint32(b, uint32(len(b)-4)) - - return b, nil -} - -func ReadMetadataResp(r io.Reader) (*MetadataResp, error) { - var resp MetadataResp - dec := NewDecoder(r) - - // total message size - _ = dec.DecodeInt32() - resp.CorrelationID = dec.DecodeInt32() - - len, err := dec.DecodeArrayLen(false) - if err != nil { - return nil, err - } - resp.Brokers = make([]MetadataRespBroker, len) - - for i := range resp.Brokers { - var b = &resp.Brokers[i] - b.NodeID = dec.DecodeInt32() - b.Host = dec.DecodeString() - b.Port = dec.DecodeInt32() - } - - len, err = dec.DecodeArrayLen(false) - if err != nil { - return nil, err - } - resp.Topics = make([]MetadataRespTopic, len) - - for ti := range resp.Topics { - var t = &resp.Topics[ti] - t.Err = errFromNo(dec.DecodeInt16()) - t.Name = dec.DecodeString() - len, err = dec.DecodeArrayLen(false) - if err != nil { - return nil, err - } - t.Partitions = make([]MetadataRespPartition, len) - - for pi := range t.Partitions { - var p = &t.Partitions[pi] - p.Err = errFromNo(dec.DecodeInt16()) - p.ID = dec.DecodeInt32() - p.Leader = dec.DecodeInt32() - - len, err = dec.DecodeArrayLen(false) - if err != nil { - return nil, err - } - p.Replicas = make([]int32, len) - - for ri := range p.Replicas { - p.Replicas[ri] = dec.DecodeInt32() - } - - len, err = dec.DecodeArrayLen(false) - if err != nil { - return nil, err - } - p.Isrs = make([]int32, len) - - for ii := range p.Isrs { - p.Isrs[ii] = dec.DecodeInt32() - } - } - } - - if dec.Err() != nil { - return nil, dec.Err() - } - return &resp, nil -} - -type FetchReq struct { - Version int16 - CorrelationID int32 - ClientID string - ReplicaID int32 - MaxWaitTime time.Duration - MinBytes int32 - MaxBytes int32 // >= KafkaV3 - IsolationLevel int8 // >= KafkaV4 - - Topics []FetchReqTopic -} - -type FetchReqTopic struct { - Name string - Partitions []FetchReqPartition -} - -type FetchReqPartition struct { - ID int32 - FetchOffset int64 - LogStartOffset int64 // >= KafkaV5 - MaxBytes int32 -} - -func ReadFetchReq(r io.Reader) (*FetchReq, error) { - var req FetchReq - dec := NewDecoder(r) - - // total message size - _ = dec.DecodeInt32() - // api key - _ = dec.DecodeInt16() - req.Version = dec.DecodeInt16() - req.CorrelationID = dec.DecodeInt32() - req.ClientID = dec.DecodeString() - - req.ReplicaID = dec.DecodeInt32() - req.MaxWaitTime = dec.DecodeDuration32() - req.MinBytes = dec.DecodeInt32() - - if req.Version >= KafkaV3 { - req.MaxBytes = dec.DecodeInt32() - } - - if req.Version >= KafkaV4 { - req.IsolationLevel = dec.DecodeInt8() - } - - len, err := dec.DecodeArrayLen(false) - if err != nil { - return nil, err - } - req.Topics = make([]FetchReqTopic, len) - - for ti := range req.Topics { - var topic = &req.Topics[ti] - topic.Name = dec.DecodeString() - - len, err = dec.DecodeArrayLen(false) - if err != nil { - return nil, err - } - topic.Partitions = make([]FetchReqPartition, len) - - for pi := range topic.Partitions { - var part = &topic.Partitions[pi] - part.ID = dec.DecodeInt32() - part.FetchOffset = dec.DecodeInt64() - - if req.Version >= KafkaV5 { - part.LogStartOffset = dec.DecodeInt64() - } - - part.MaxBytes = dec.DecodeInt32() - } - } - - if dec.Err() != nil { - return nil, dec.Err() - } - return &req, nil -} - -func (r *FetchReq) Bytes(version int16) ([]byte, error) { - var buf bytes.Buffer - enc := NewEncoder(&buf) - - // message size - for now just placeholder - enc.Encode(int32(0)) - enc.Encode(int16(FetchReqKind)) - enc.Encode(r.Version) - enc.Encode(r.CorrelationID) - enc.Encode(r.ClientID) - - enc.Encode(r.ReplicaID) - enc.Encode(r.MaxWaitTime) - enc.Encode(r.MinBytes) - - if version >= KafkaV3 { - enc.Encode(r.MaxBytes) - } - - if version >= KafkaV4 { - enc.Encode(r.IsolationLevel) - } - - enc.EncodeArrayLen(r.Topics) - for _, topic := range r.Topics { - enc.Encode(topic.Name) - enc.EncodeArrayLen(topic.Partitions) - for _, part := range topic.Partitions { - enc.Encode(part.ID) - enc.Encode(part.FetchOffset) - - if version >= KafkaV5 { - enc.Encode(part.LogStartOffset) - } - - enc.Encode(part.MaxBytes) - } - } - - if enc.Err() != nil { - return nil, enc.Err() - } - - // update the message size information - b := buf.Bytes() - binary.BigEndian.PutUint32(b, uint32(len(b)-4)) - - return b, nil -} - -func (r *FetchReq) WriteTo(w io.Writer, version int16) (int64, error) { - b, err := r.Bytes(version) - if err != nil { - return 0, err - } - n, err := w.Write(b) - return int64(n), err -} - -type FetchResp struct { - CorrelationID int32 - ThrottleTime time.Duration - Topics []FetchRespTopic -} - -type FetchRespTopic struct { - Name string - Partitions []FetchRespPartition -} - -type FetchRespPartition struct { - ID int32 - Err error - TipOffset int64 - LastStableOffset int64 - LogStartOffset int64 - AbortedTransactions []FetchRespAbortedTransaction - Messages []*Message -} - -type FetchRespAbortedTransaction struct { - ProducerID int64 - FirstOffset int64 -} - -func (r *FetchResp) Bytes(version int16) ([]byte, error) { - var buf buffer - enc := NewEncoder(&buf) - - enc.Encode(int32(0)) // placeholder - enc.Encode(r.CorrelationID) - - if version >= KafkaV1 { - enc.Encode(r.ThrottleTime) - } - - enc.EncodeArrayLen(r.Topics) - for _, topic := range r.Topics { - enc.Encode(topic.Name) - enc.EncodeArrayLen(topic.Partitions) - for _, part := range topic.Partitions { - enc.Encode(part.ID) - enc.EncodeError(part.Err) - enc.Encode(part.TipOffset) - - if version >= KafkaV4 { - enc.Encode(part.LastStableOffset) - - if version >= KafkaV5 { - enc.Encode(part.LogStartOffset) - } - - enc.EncodeArrayLen(part.AbortedTransactions) - for _, trans := range part.AbortedTransactions { - enc.Encode(trans.ProducerID) - enc.Encode(trans.FirstOffset) - } - } - - i := len(buf) - enc.Encode(int32(0)) // placeholder - // NOTE(caleb): writing compressed fetch response isn't implemented - // for now, since that's not needed for clients. - n, err := writeMessageSet(&buf, part.Messages, CompressionNone) - if err != nil { - return nil, err - } - binary.BigEndian.PutUint32(buf[i:i+4], uint32(n)) - } - } - - if enc.Err() != nil { - return nil, enc.Err() - } - - binary.BigEndian.PutUint32(buf[:4], uint32(len(buf)-4)) - return []byte(buf), nil -} - -func ReadFetchResp(r io.Reader) (*FetchResp, error) { - var err error - var resp FetchResp - - dec := NewDecoder(r) - - // total message size - _ = dec.DecodeInt32() - resp.CorrelationID = dec.DecodeInt32() - - len, err := dec.DecodeArrayLen(false) - if err != nil { - return nil, err - } - resp.Topics = make([]FetchRespTopic, len) - - for ti := range resp.Topics { - var topic = &resp.Topics[ti] - topic.Name = dec.DecodeString() - - len, err := dec.DecodeArrayLen(false) - if err != nil { - return nil, err - } - topic.Partitions = make([]FetchRespPartition, len) - - for pi := range topic.Partitions { - var part = &topic.Partitions[pi] - part.ID = dec.DecodeInt32() - part.Err = errFromNo(dec.DecodeInt16()) - part.TipOffset = dec.DecodeInt64() - if dec.Err() != nil { - return nil, dec.Err() - } - msgSetSize := dec.DecodeInt32() - if dec.Err() != nil { - return nil, dec.Err() - } - if part.Messages, err = readMessageSet(r, msgSetSize, 0); err != nil { - return nil, err - } - for _, msg := range part.Messages { - msg.Topic = topic.Name - msg.Partition = part.ID - msg.TipOffset = part.TipOffset - } - } - } - - if dec.Err() != nil { - return nil, dec.Err() - } - return &resp, nil -} - -const ( - CorrelationTypeGroup int8 = 0 - CorrelationTypeTransaction = 1 -) - -type ConsumerMetadataReq struct { - Version int16 - CorrelationID int32 - ClientID string - ConsumerGroup string - CoordinatorType int8 // >= KafkaV1 -} - -func ReadConsumerMetadataReq(r io.Reader) (*ConsumerMetadataReq, error) { - var req ConsumerMetadataReq - dec := NewDecoder(r) - - // total message size - _ = dec.DecodeInt32() - // api key - _ = dec.DecodeInt16() - req.Version = dec.DecodeInt16() - req.CorrelationID = dec.DecodeInt32() - req.ClientID = dec.DecodeString() - req.ConsumerGroup = dec.DecodeString() - - if req.Version >= KafkaV1 { - req.CoordinatorType = dec.DecodeInt8() - } - - if dec.Err() != nil { - return nil, dec.Err() - } - return &req, nil -} - -func (r *ConsumerMetadataReq) Bytes(version int16) ([]byte, error) { - var buf bytes.Buffer - enc := NewEncoder(&buf) - - // message size - for now just placeholder - enc.Encode(int32(0)) - enc.Encode(int16(ConsumerMetadataReqKind)) - enc.Encode(r.Version) - enc.Encode(r.CorrelationID) - enc.Encode(r.ClientID) - - enc.Encode(r.ConsumerGroup) - - if enc.Err() != nil { - return nil, enc.Err() - } - - // update the message size information - b := buf.Bytes() - binary.BigEndian.PutUint32(b, uint32(len(b)-4)) - - return b, nil -} - -func (r *ConsumerMetadataReq) WriteTo(w io.Writer, version int16) (int64, error) { - b, err := r.Bytes(version) - if err != nil { - return 0, err - } - n, err := w.Write(b) - return int64(n), err -} - -type ConsumerMetadataResp struct { - CorrelationID int32 - ThrottleTime time.Duration // >= KafkaV1 - Err error - ErrMsg string // >= KafkaV1 - CoordinatorID int32 - CoordinatorHost string - CoordinatorPort int32 -} - -func ReadConsumerMetadataResp(r io.Reader) (*ConsumerMetadataResp, error) { - var resp ConsumerMetadataResp - dec := NewDecoder(r) - - // total message size - _ = dec.DecodeInt32() - resp.CorrelationID = dec.DecodeInt32() - resp.Err = errFromNo(dec.DecodeInt16()) - resp.CoordinatorID = dec.DecodeInt32() - resp.CoordinatorHost = dec.DecodeString() - resp.CoordinatorPort = dec.DecodeInt32() - - if err := dec.Err(); err != nil { - return nil, err - } - return &resp, nil -} - -func (r *ConsumerMetadataResp) Bytes(version int16) ([]byte, error) { - var buf bytes.Buffer - enc := NewEncoder(&buf) - - // message size - for now just placeholder - enc.Encode(int32(0)) - enc.Encode(r.CorrelationID) - - if version >= KafkaV1 { - enc.Encode(r.ThrottleTime) - } - - enc.EncodeError(r.Err) - - if version >= KafkaV1 { - enc.Encode(r.ErrMsg) - } - - enc.Encode(r.CoordinatorID) - enc.Encode(r.CoordinatorHost) - enc.Encode(r.CoordinatorPort) - - if enc.Err() != nil { - return nil, enc.Err() - } - - // update the message size information - b := buf.Bytes() - binary.BigEndian.PutUint32(b, uint32(len(b)-4)) - - return b, nil -} - -type OffsetCommitReq struct { - Version int16 - CorrelationID int32 - ClientID string - ConsumerGroup string - GroupGenerationID int32 // >= KafkaV1 only - MemberID string // >= KafkaV1 only - RetentionTime int64 // >= KafkaV2 only - Topics []OffsetCommitReqTopic -} - -type OffsetCommitReqTopic struct { - Name string - Partitions []OffsetCommitReqPartition -} - -type OffsetCommitReqPartition struct { - ID int32 - Offset int64 - TimeStamp time.Time // == KafkaV1 only - Metadata string -} - -func ReadOffsetCommitReq(r io.Reader) (*OffsetCommitReq, error) { - var req OffsetCommitReq - dec := NewDecoder(r) - - // total message size - _ = dec.DecodeInt32() - // api key - _ = dec.DecodeInt16() - req.Version = dec.DecodeInt16() - req.CorrelationID = dec.DecodeInt32() - req.ClientID = dec.DecodeString() - req.ConsumerGroup = dec.DecodeString() - - if req.Version >= KafkaV1 { - req.GroupGenerationID = dec.DecodeInt32() - req.MemberID = dec.DecodeString() - } - - if req.Version >= KafkaV2 { - req.RetentionTime = dec.DecodeInt64() - } - - len, err := dec.DecodeArrayLen(false) - if err != nil { - return nil, err - } - req.Topics = make([]OffsetCommitReqTopic, len) - - for ti := range req.Topics { - var topic = &req.Topics[ti] - topic.Name = dec.DecodeString() - - len, err := dec.DecodeArrayLen(false) - if err != nil { - return nil, err - } - topic.Partitions = make([]OffsetCommitReqPartition, len) - - for pi := range topic.Partitions { - var part = &topic.Partitions[pi] - part.ID = dec.DecodeInt32() - part.Offset = dec.DecodeInt64() - - if req.Version == KafkaV1 { - part.TimeStamp = time.Unix(0, dec.DecodeInt64()*int64(time.Millisecond)) - } - - part.Metadata = dec.DecodeString() - } - } - - if dec.Err() != nil { - return nil, dec.Err() - } - return &req, nil -} - -func (r *OffsetCommitReq) Bytes(version int16) ([]byte, error) { - var buf bytes.Buffer - enc := NewEncoder(&buf) - - // message size - for now just placeholder - enc.Encode(int32(0)) - enc.Encode(int16(OffsetCommitReqKind)) - enc.Encode(r.Version) - enc.Encode(r.CorrelationID) - enc.Encode(r.ClientID) - - enc.Encode(r.ConsumerGroup) - - if version >= KafkaV1 { - enc.Encode(r.GroupGenerationID) - enc.Encode(r.MemberID) - } - - if version >= KafkaV2 { - enc.Encode(r.RetentionTime) - } - - enc.EncodeArrayLen(r.Topics) - for _, topic := range r.Topics { - enc.Encode(topic.Name) - enc.EncodeArrayLen(topic.Partitions) - for _, part := range topic.Partitions { - enc.Encode(part.ID) - enc.Encode(part.Offset) - - if version == KafkaV1 { - // TODO(husio) is this really in milliseconds? - enc.Encode(part.TimeStamp.UnixNano() / int64(time.Millisecond)) - } - - enc.Encode(part.Metadata) - } - } - - if enc.Err() != nil { - return nil, enc.Err() - } - - // update the message size information - b := buf.Bytes() - binary.BigEndian.PutUint32(b, uint32(len(b)-4)) - - return b, nil -} - -func (r *OffsetCommitReq) WriteTo(w io.Writer, version int16) (int64, error) { - b, err := r.Bytes(version) - if err != nil { - return 0, err - } - n, err := w.Write(b) - return int64(n), err -} - -type OffsetCommitResp struct { - CorrelationID int32 - ThrottleTime time.Duration // >= KafkaV3 only - Topics []OffsetCommitRespTopic -} - -type OffsetCommitRespTopic struct { - Name string - Partitions []OffsetCommitRespPartition -} - -type OffsetCommitRespPartition struct { - ID int32 - Err error -} - -func ReadOffsetCommitResp(r io.Reader) (*OffsetCommitResp, error) { - var resp OffsetCommitResp - dec := NewDecoder(r) - - // total message size - _ = dec.DecodeInt32() - resp.CorrelationID = dec.DecodeInt32() - - len, err := dec.DecodeArrayLen(false) - if err != nil { - return nil, err - } - resp.Topics = make([]OffsetCommitRespTopic, len) - - for ti := range resp.Topics { - var t = &resp.Topics[ti] - t.Name = dec.DecodeString() - - len, err := dec.DecodeArrayLen(false) - if err != nil { - return nil, err - } - t.Partitions = make([]OffsetCommitRespPartition, len) - - for pi := range t.Partitions { - var p = &t.Partitions[pi] - p.ID = dec.DecodeInt32() - p.Err = errFromNo(dec.DecodeInt16()) - } - } - - if err := dec.Err(); err != nil { - return nil, err - } - return &resp, nil -} - -func (r *OffsetCommitResp) Bytes(version int16) ([]byte, error) { - var buf bytes.Buffer - enc := NewEncoder(&buf) - - // message size - for now just placeholder - enc.Encode(int32(0)) - enc.Encode(r.CorrelationID) - - if version >= KafkaV3 { - enc.Encode(r.ThrottleTime) - } - - enc.EncodeArrayLen(r.Topics) - for _, t := range r.Topics { - enc.Encode(t.Name) - enc.EncodeArrayLen(t.Partitions) - for _, p := range t.Partitions { - enc.Encode(p.ID) - enc.EncodeError(p.Err) - } - } - - if enc.Err() != nil { - return nil, enc.Err() - } - - // update the message size information - b := buf.Bytes() - binary.BigEndian.PutUint32(b, uint32(len(b)-4)) - - return b, nil - -} - -type OffsetFetchReq struct { - Version int16 - CorrelationID int32 - ClientID string - ConsumerGroup string - Topics []OffsetFetchReqTopic -} - -type OffsetFetchReqTopic struct { - Name string - Partitions []int32 -} - -func ReadOffsetFetchReq(r io.Reader) (*OffsetFetchReq, error) { - var req OffsetFetchReq - dec := NewDecoder(r) - - // total message size - _ = dec.DecodeInt32() - // api key - _ = dec.DecodeInt16() - req.Version = dec.DecodeInt16() - req.CorrelationID = dec.DecodeInt32() - req.ClientID = dec.DecodeString() - req.ConsumerGroup = dec.DecodeString() - - len, err := dec.DecodeArrayLen(true) // nullable - if err != nil { - return nil, err - } - if len < 0 { // null array - req.Topics = nil - } else { - req.Topics = make([]OffsetFetchReqTopic, len) - } - - for ti := range req.Topics { - var topic = &req.Topics[ti] - topic.Name = dec.DecodeString() - len, err = dec.DecodeArrayLen(false) - if err != nil { - return nil, err - } - topic.Partitions = make([]int32, len) - - for pi := range topic.Partitions { - topic.Partitions[pi] = dec.DecodeInt32() - } - } - - if dec.Err() != nil { - return nil, dec.Err() - } - return &req, nil -} - -func (r *OffsetFetchReq) Bytes(version int16) ([]byte, error) { - var buf bytes.Buffer - enc := NewEncoder(&buf) - - // message size - for now just placeholder - enc.Encode(int32(0)) - enc.Encode(int16(OffsetFetchReqKind)) - enc.Encode(r.Version) - enc.Encode(r.CorrelationID) - enc.Encode(r.ClientID) - - enc.Encode(r.ConsumerGroup) - enc.EncodeArrayLen(r.Topics) - for _, t := range r.Topics { - enc.Encode(t.Name) - enc.EncodeArrayLen(t.Partitions) - for _, p := range t.Partitions { - enc.Encode(p) - } - } - - if enc.Err() != nil { - return nil, enc.Err() - } - - // update the message size information - b := buf.Bytes() - binary.BigEndian.PutUint32(b, uint32(len(b)-4)) - - return b, nil -} - -func (r *OffsetFetchReq) WriteTo(w io.Writer, version int16) (int64, error) { - b, err := r.Bytes(version) - if err != nil { - return 0, err - } - n, err := w.Write(b) - return int64(n), err -} - -type OffsetFetchResp struct { - CorrelationID int32 - ThrottleTime time.Duration // >= KafkaV3 - Topics []OffsetFetchRespTopic - Err error // >= KafkaV2 -} - -type OffsetFetchRespTopic struct { - Name string - Partitions []OffsetFetchRespPartition -} - -type OffsetFetchRespPartition struct { - ID int32 - Offset int64 - Metadata string - Err error -} - -func ReadOffsetFetchResp(r io.Reader) (*OffsetFetchResp, error) { - var resp OffsetFetchResp - dec := NewDecoder(r) - - // total message size - _ = dec.DecodeInt32() - resp.CorrelationID = dec.DecodeInt32() - - len, err := dec.DecodeArrayLen(false) - if err != nil { - return nil, err - } - resp.Topics = make([]OffsetFetchRespTopic, len) - - for ti := range resp.Topics { - var t = &resp.Topics[ti] - t.Name = dec.DecodeString() - - len, err = dec.DecodeArrayLen(false) - if err != nil { - return nil, err - } - t.Partitions = make([]OffsetFetchRespPartition, len) - - for pi := range t.Partitions { - var p = &t.Partitions[pi] - p.ID = dec.DecodeInt32() - p.Offset = dec.DecodeInt64() - p.Metadata = dec.DecodeString() - p.Err = errFromNo(dec.DecodeInt16()) - } - } - - if err := dec.Err(); err != nil { - return nil, err - } - return &resp, nil -} - -func (r *OffsetFetchResp) Bytes(version int16) ([]byte, error) { - var buf bytes.Buffer - enc := NewEncoder(&buf) - - // message size - for now just placeholder - enc.Encode(int32(0)) - enc.Encode(r.CorrelationID) - - if version >= KafkaV3 { - enc.Encode(r.ThrottleTime) - } - - enc.EncodeArrayLen(r.Topics) - for _, topic := range r.Topics { - enc.Encode(topic.Name) - enc.EncodeArrayLen(topic.Partitions) - for _, part := range topic.Partitions { - enc.Encode(part.ID) - enc.Encode(part.Offset) - enc.Encode(part.Metadata) - enc.EncodeError(part.Err) - } - } - - if version >= KafkaV2 { - enc.EncodeError(r.Err) - } - - if enc.Err() != nil { - return nil, enc.Err() - } - - // update the message size information - b := buf.Bytes() - binary.BigEndian.PutUint32(b, uint32(len(b)-4)) - - return b, nil -} - -type ProduceReq struct { - Version int16 - CorrelationID int32 - ClientID string - Compression Compression // only used when sending ProduceReqs - TransactionalID string - RequiredAcks int16 - Timeout time.Duration - Topics []ProduceReqTopic -} - -type ProduceReqTopic struct { - Name string - Partitions []ProduceReqPartition -} - -type ProduceReqPartition struct { - ID int32 - Messages []*Message -} - -func ReadProduceReq(r io.Reader) (*ProduceReq, error) { - var req ProduceReq - dec := NewDecoder(r) - - // total message size - _ = dec.DecodeInt32() - // api key - _ = dec.DecodeInt16() - req.Version = dec.DecodeInt16() - req.CorrelationID = dec.DecodeInt32() - req.ClientID = dec.DecodeString() - - if req.Version >= KafkaV3 { - req.TransactionalID = dec.DecodeString() - } - - req.RequiredAcks = dec.DecodeInt16() - req.Timeout = time.Duration(dec.DecodeInt32()) * time.Millisecond - - len, err := dec.DecodeArrayLen(false) - if err != nil { - return nil, err - } - req.Topics = make([]ProduceReqTopic, len) - - for ti := range req.Topics { - var topic = &req.Topics[ti] - topic.Name = dec.DecodeString() - - len, err = dec.DecodeArrayLen(false) - if err != nil { - return nil, err - } - topic.Partitions = make([]ProduceReqPartition, len) - - for pi := range topic.Partitions { - var part = &topic.Partitions[pi] - part.ID = dec.DecodeInt32() - if dec.Err() != nil { - return nil, dec.Err() - } - msgSetSize := dec.DecodeInt32() - if dec.Err() != nil { - return nil, dec.Err() - } - var err error - if part.Messages, err = readMessageSet(r, msgSetSize, req.Version); err != nil { - return nil, err - } - } - } - - if dec.Err() != nil { - return nil, dec.Err() - } - return &req, nil -} - -func (r *ProduceReq) Bytes(version int16) ([]byte, error) { - var buf buffer - enc := NewEncoder(&buf) - - enc.EncodeInt32(0) // placeholder - enc.EncodeInt16(ProduceReqKind) - enc.EncodeInt16(r.Version) - enc.EncodeInt32(r.CorrelationID) - enc.EncodeString(r.ClientID) - - if version >= KafkaV3 { - enc.EncodeString(r.TransactionalID) - } - - enc.EncodeInt16(r.RequiredAcks) - enc.EncodeInt32(int32(r.Timeout / time.Millisecond)) - enc.EncodeArrayLen(r.Topics) - for _, t := range r.Topics { - enc.EncodeString(t.Name) - enc.EncodeArrayLen(t.Partitions) - for _, p := range t.Partitions { - enc.EncodeInt32(p.ID) - i := len(buf) - enc.EncodeInt32(0) // placeholder - n, err := writeMessageSet(&buf, p.Messages, r.Compression) - if err != nil { - return nil, err - } - binary.BigEndian.PutUint32(buf[i:i+4], uint32(n)) - } - } - - if enc.Err() != nil { - return nil, enc.Err() - } - - binary.BigEndian.PutUint32(buf[0:4], uint32(len(buf)-4)) - return []byte(buf), nil -} - -func (r *ProduceReq) WriteTo(w io.Writer, version int16) (int64, error) { - b, err := r.Bytes(version) - if err != nil { - return 0, err - } - n, err := w.Write(b) - return int64(n), err -} - -type ProduceResp struct { - CorrelationID int32 - Topics []ProduceRespTopic - ThrottleTime time.Duration -} - -type ProduceRespTopic struct { - Name string - Partitions []ProduceRespPartition -} - -type ProduceRespPartition struct { - ID int32 - Err error - Offset int64 - LogAppendTime int64 -} - -func (r *ProduceResp) Bytes(version int16) ([]byte, error) { - var buf bytes.Buffer - enc := NewEncoder(&buf) - - // message size - for now just placeholder - enc.Encode(int32(0)) - enc.Encode(r.CorrelationID) - enc.EncodeArrayLen(r.Topics) - for _, topic := range r.Topics { - enc.Encode(topic.Name) - enc.EncodeArrayLen(topic.Partitions) - for _, part := range topic.Partitions { - enc.Encode(part.ID) - enc.EncodeError(part.Err) - enc.Encode(part.Offset) - - if version >= KafkaV2 { - enc.Encode(part.LogAppendTime) - } - } - } - - if version >= KafkaV1 { - enc.Encode(r.ThrottleTime) - } - - if enc.Err() != nil { - return nil, enc.Err() - } - - // update the message size information - b := buf.Bytes() - binary.BigEndian.PutUint32(b, uint32(len(b)-4)) - - return b, nil -} - -func ReadProduceResp(r io.Reader) (*ProduceResp, error) { - var resp ProduceResp - dec := NewDecoder(r) - - // total message size - _ = dec.DecodeInt32() - resp.CorrelationID = dec.DecodeInt32() - len, err := dec.DecodeArrayLen(false) - if err != nil { - return nil, err - } - resp.Topics = make([]ProduceRespTopic, len) - - for ti := range resp.Topics { - var t = &resp.Topics[ti] - t.Name = dec.DecodeString() - - len, err = dec.DecodeArrayLen(false) - if err != nil { - return nil, err - } - t.Partitions = make([]ProduceRespPartition, len) - - for pi := range t.Partitions { - var p = &t.Partitions[pi] - p.ID = dec.DecodeInt32() - p.Err = errFromNo(dec.DecodeInt16()) - p.Offset = dec.DecodeInt64() - } - } - - if err := dec.Err(); err != nil { - return nil, err - } - return &resp, nil -} - -type OffsetReq struct { - Version int16 - CorrelationID int32 - ClientID string - ReplicaID int32 - IsolationLevel int8 - Topics []OffsetReqTopic -} - -type OffsetReqTopic struct { - Name string - Partitions []OffsetReqPartition -} - -type OffsetReqPartition struct { - ID int32 - TimeMs int64 // cannot be time.Time because of negative values - MaxOffsets int32 // == KafkaV0 only -} - -func ReadOffsetReq(r io.Reader) (*OffsetReq, error) { - var req OffsetReq - dec := NewDecoder(r) - - // total message size - _ = dec.DecodeInt32() - // api key - _ = dec.DecodeInt16() - req.Version = dec.DecodeInt16() - req.CorrelationID = dec.DecodeInt32() - req.ClientID = dec.DecodeString() - req.ReplicaID = dec.DecodeInt32() - - if req.Version >= KafkaV2 { - req.IsolationLevel = dec.DecodeInt8() - } - - len, err := dec.DecodeArrayLen(false) - if err != nil { - return nil, err - } - req.Topics = make([]OffsetReqTopic, len) - - for ti := range req.Topics { - var topic = &req.Topics[ti] - topic.Name = dec.DecodeString() - - len, err = dec.DecodeArrayLen(false) - if err != nil { - return nil, err - } - topic.Partitions = make([]OffsetReqPartition, len) - - for pi := range topic.Partitions { - var part = &topic.Partitions[pi] - part.ID = dec.DecodeInt32() - part.TimeMs = dec.DecodeInt64() - - if req.Version == KafkaV0 { - part.MaxOffsets = dec.DecodeInt32() - } - } - } - - if dec.Err() != nil { - return nil, dec.Err() - } - return &req, nil -} - -func (r *OffsetReq) Bytes(version int16) ([]byte, error) { - var buf bytes.Buffer - enc := NewEncoder(&buf) - - // message size - for now just placeholder - enc.Encode(int32(0)) - enc.Encode(int16(OffsetReqKind)) - enc.Encode(r.Version) - enc.Encode(r.CorrelationID) - enc.Encode(r.ClientID) - - enc.Encode(r.ReplicaID) - - if version >= KafkaV2 { - enc.Encode(r.IsolationLevel) - } - - enc.EncodeArrayLen(r.Topics) - for _, topic := range r.Topics { - enc.Encode(topic.Name) - enc.EncodeArrayLen(topic.Partitions) - for _, part := range topic.Partitions { - enc.Encode(part.ID) - enc.Encode(part.TimeMs) - - if version == KafkaV0 { - enc.Encode(part.MaxOffsets) - } - } - } - - if enc.Err() != nil { - return nil, enc.Err() - } - - // update the message size information - b := buf.Bytes() - binary.BigEndian.PutUint32(b, uint32(len(b)-4)) - - return b, nil -} - -func (r *OffsetReq) WriteTo(w io.Writer, version int16) (int64, error) { - b, err := r.Bytes(version) - if err != nil { - return 0, err - } - n, err := w.Write(b) - return int64(n), err -} - -type OffsetResp struct { - CorrelationID int32 - ThrottleTime time.Duration - Topics []OffsetRespTopic -} - -type OffsetRespTopic struct { - Name string - Partitions []OffsetRespPartition -} - -type OffsetRespPartition struct { - ID int32 - Err error - TimeStamp time.Time // >= KafkaV1 only - Offsets []int64 -} - -func ReadOffsetResp(r io.Reader) (*OffsetResp, error) { - var resp OffsetResp - dec := NewDecoder(r) - - // total message size - _ = dec.DecodeInt32() - resp.CorrelationID = dec.DecodeInt32() - - len, err := dec.DecodeArrayLen(false) - if err != nil { - return nil, err - } - resp.Topics = make([]OffsetRespTopic, len) - - for ti := range resp.Topics { - var t = &resp.Topics[ti] - t.Name = dec.DecodeString() - - len, err = dec.DecodeArrayLen(false) - if err != nil { - return nil, err - } - t.Partitions = make([]OffsetRespPartition, len) - - for pi := range t.Partitions { - var p = &t.Partitions[pi] - p.ID = dec.DecodeInt32() - p.Err = errFromNo(dec.DecodeInt16()) - len, err = dec.DecodeArrayLen(false) - if err != nil { - return nil, err - } - p.Offsets = make([]int64, len) - - for oi := range p.Offsets { - p.Offsets[oi] = dec.DecodeInt64() - } - } - } - - if err := dec.Err(); err != nil { - return nil, err - } - return &resp, nil -} - -func (r *OffsetResp) Bytes(version int16) ([]byte, error) { - var buf bytes.Buffer - enc := NewEncoder(&buf) - - // message size - for now just placeholder - enc.Encode(int32(0)) - enc.Encode(r.CorrelationID) - - if version >= KafkaV2 { - enc.Encode(r.ThrottleTime) - } - - enc.EncodeArrayLen(r.Topics) - for _, topic := range r.Topics { - enc.Encode(topic.Name) - enc.EncodeArrayLen(topic.Partitions) - for _, part := range topic.Partitions { - enc.Encode(part.ID) - enc.EncodeError(part.Err) - - if version >= KafkaV1 { - enc.Encode(part.TimeStamp.UnixNano() / int64(time.Millisecond)) - } - - enc.EncodeArrayLen(part.Offsets) - for _, off := range part.Offsets { - enc.Encode(off) - } - } - } - - if enc.Err() != nil { - return nil, enc.Err() - } - - // update the message size information - b := buf.Bytes() - binary.BigEndian.PutUint32(b, uint32(len(b)-4)) - - return b, nil -} - -type buffer []byte - -func (b *buffer) Write(p []byte) (int, error) { - *b = append(*b, p...) - return len(p), nil -} diff --git a/vendor/github.com/cilium/kafka/proto/serialization.go b/vendor/github.com/cilium/kafka/proto/serialization.go deleted file mode 100644 index c47c325de..000000000 --- a/vendor/github.com/cilium/kafka/proto/serialization.go +++ /dev/null @@ -1,408 +0,0 @@ -package proto - -import ( - "encoding/binary" - "errors" - "fmt" - "io" - "reflect" - "time" -) - -const ( - maxParseArrayLen = 256 -) - -var ErrNotEnoughData = errors.New("not enough data") -var ErrInvalidArrayLen = errors.New("invalid array length") - -type decoder struct { - buf []byte - r io.Reader - err error -} - -func NewDecoder(r io.Reader) *decoder { - return &decoder{ - r: r, - buf: make([]byte, 1024), - } -} - -func (d *decoder) DecodeInt8() int8 { - if d.err != nil { - return 0 - } - b := d.buf[:1] - n, err := io.ReadFull(d.r, b) - if err != nil { - d.err = err - return 0 - } - if n != 1 { - d.err = ErrNotEnoughData - return 0 - } - return int8(b[0]) -} - -func (d *decoder) DecodeInt16() int16 { - if d.err != nil { - return 0 - } - b := d.buf[:2] - n, err := io.ReadFull(d.r, b) - if err != nil { - d.err = err - return 0 - } - if n != 2 { - d.err = ErrNotEnoughData - return 0 - } - return int16(binary.BigEndian.Uint16(b)) -} - -func (d *decoder) DecodeInt32() int32 { - if d.err != nil { - return 0 - } - b := d.buf[:4] - n, err := io.ReadFull(d.r, b) - if err != nil { - d.err = err - return 0 - } - if n != 4 { - d.err = ErrNotEnoughData - return 0 - } - return int32(binary.BigEndian.Uint32(b)) -} - -func (d *decoder) DecodeUint32() uint32 { - if d.err != nil { - return 0 - } - b := d.buf[:4] - n, err := io.ReadFull(d.r, b) - if err != nil { - d.err = err - return 0 - } - if n != 4 { - d.err = ErrNotEnoughData - return 0 - } - return binary.BigEndian.Uint32(b) -} - -func (d *decoder) DecodeInt64() int64 { - if d.err != nil { - return 0 - } - b := d.buf[:8] - n, err := io.ReadFull(d.r, b) - if err != nil { - d.err = err - return 0 - } - if n != 8 { - d.err = ErrNotEnoughData - return 0 - } - return int64(binary.BigEndian.Uint64(b)) -} - -func (d *decoder) DecodeDuration32() time.Duration { - return time.Duration(d.DecodeInt32()) * time.Millisecond -} - -func (d *decoder) DecodeString() string { - if d.err != nil { - return "" - } - slen := d.DecodeInt16() - if d.err != nil { - return "" - } - if slen < 1 { - return "" - } - - var b []byte - if int(slen) > len(d.buf) { - var err error - b, err = allocParseBuf(int(slen)) - if err != nil { - d.err = err - return "" - } - } else { - b = d.buf[:int(slen)] - } - n, err := io.ReadFull(d.r, b) - if err != nil { - d.err = err - return "" - } - if n != int(slen) { - d.err = ErrNotEnoughData - return "" - } - return string(b) -} - -func (d *decoder) DecodeArrayLen(nullable bool) (int, error) { - len := int(d.DecodeInt32()) - - if len < 0 { - if nullable { // null array. - return -1, nil - } else { - return 0, ErrInvalidArrayLen - } - } else if len > maxParseBufSize { - return 0, ErrInvalidArrayLen - } - - return len, nil -} - -func (d *decoder) DecodeBytes() []byte { - if d.err != nil { - return nil - } - slen := d.DecodeInt32() - if d.err != nil { - return nil - } - if slen < 1 { - return nil - } - - b, err := allocParseBuf(int(slen)) - if err != nil { - d.err = err - return nil - } - n, err := io.ReadFull(d.r, b) - if err != nil { - d.err = err - return nil - } - if n != int(slen) { - d.err = ErrNotEnoughData - return nil - } - return b -} - -func (d *decoder) Err() error { - return d.err -} - -type encoder struct { - w io.Writer - err error - buf [8]byte -} - -func NewEncoder(w io.Writer) *encoder { - return &encoder{w: w} -} - -func (e *encoder) Encode(value interface{}) { - if e.err != nil { - return - } - var b []byte - - switch val := value.(type) { - case int8: - _, e.err = e.w.Write([]byte{byte(val)}) - case int16: - b = e.buf[:2] - binary.BigEndian.PutUint16(b, uint16(val)) - case int32: - b = e.buf[:4] - binary.BigEndian.PutUint32(b, uint32(val)) - case int64: - b = e.buf[:8] - binary.BigEndian.PutUint64(b, uint64(val)) - case uint16: - b = e.buf[:2] - binary.BigEndian.PutUint16(b, val) - case uint32: - b = e.buf[:4] - binary.BigEndian.PutUint32(b, val) - case uint64: - b = e.buf[:8] - binary.BigEndian.PutUint64(b, val) - case string: - buf := e.buf[:2] - binary.BigEndian.PutUint16(buf, uint16(len(val))) - e.err = writeAll(e.w, buf) - if e.err == nil { - e.err = writeAll(e.w, []byte(val)) - } - case []byte: - buf := e.buf[:4] - - if val == nil { - no := int32(-1) - binary.BigEndian.PutUint32(buf, uint32(no)) - e.err = writeAll(e.w, buf) - return - } - - binary.BigEndian.PutUint32(buf, uint32(len(val))) - e.err = writeAll(e.w, buf) - if e.err == nil { - e.err = writeAll(e.w, val) - } - case []int32: - e.EncodeArrayLen(val) - for _, v := range val { - e.Encode(v) - } - case time.Duration: - intVal := uint32(val / time.Millisecond) - b = e.buf[:4] - binary.BigEndian.PutUint32(b, intVal) - default: - e.err = fmt.Errorf("cannot encode type %T", value) - } - - if b != nil { - e.err = writeAll(e.w, b) - return - } -} - -func (e *encoder) EncodeInt8(val int8) { - if e.err != nil { - return - } - - _, e.err = e.w.Write([]byte{byte(val)}) -} - -func (e *encoder) EncodeInt16(val int16) { - if e.err != nil { - return - } - - b := e.buf[:2] - binary.BigEndian.PutUint16(b, uint16(val)) - e.err = writeAll(e.w, b) -} - -func (e *encoder) EncodeInt32(val int32) { - if e.err != nil { - return - } - - b := e.buf[:4] - binary.BigEndian.PutUint32(b, uint32(val)) - e.err = writeAll(e.w, b) -} - -func (e *encoder) EncodeInt64(val int64) { - if e.err != nil { - return - } - - b := e.buf[:8] - binary.BigEndian.PutUint64(b, uint64(val)) - e.err = writeAll(e.w, b) -} - -func (e *encoder) EncodeUint32(val uint32) { - if e.err != nil { - return - } - - b := e.buf[:4] - binary.BigEndian.PutUint32(b, val) - e.err = writeAll(e.w, b) -} - -func (e *encoder) EncodeBytes(val []byte) { - if e.err != nil { - return - } - - buf := e.buf[:4] - - if val == nil { - no := int32(-1) - binary.BigEndian.PutUint32(buf, uint32(no)) - e.err = writeAll(e.w, buf) - return - } - - binary.BigEndian.PutUint32(buf, uint32(len(val))) - e.err = writeAll(e.w, buf) - if e.err == nil { - e.err = writeAll(e.w, val) - } -} - -func (e *encoder) EncodeString(val string) { - if e.err != nil { - return - } - - buf := e.buf[:2] - - binary.BigEndian.PutUint16(buf, uint16(len(val))) - e.err = writeAll(e.w, buf) - if e.err == nil { - e.err = writeAll(e.w, []byte(val)) - } -} - -func (e *encoder) EncodeError(err error) { - b := e.buf[:2] - - if err == nil { - binary.BigEndian.PutUint16(b, uint16(0)) - e.err = writeAll(e.w, b) - return - } - kerr, ok := err.(*KafkaError) - if !ok { - e.err = fmt.Errorf("cannot encode error of type %T", err) - } - - binary.BigEndian.PutUint16(b, uint16(kerr.errno)) - e.err = writeAll(e.w, b) -} - -func (e *encoder) EncodeArrayLen(s interface{}) { - v := reflect.ValueOf(s) - if v.Type().Kind() != reflect.Slice { - panic(fmt.Sprintf("EncodeArraylen called with a non-slice argument: %v", s)) - } - if v.IsNil() { - e.EncodeInt32(-1) - } else { - e.EncodeInt32(int32(v.Len())) - } -} - -func (e *encoder) Err() error { - return e.err -} - -func writeAll(w io.Writer, b []byte) error { - n, err := w.Write(b) - if err != nil { - return err - } - if n != len(b) { - return fmt.Errorf("cannot write %d: %d written", len(b), n) - } - return nil -} diff --git a/vendor/github.com/cilium/kafka/proto/snappy.go b/vendor/github.com/cilium/kafka/proto/snappy.go deleted file mode 100644 index f000cc342..000000000 --- a/vendor/github.com/cilium/kafka/proto/snappy.go +++ /dev/null @@ -1,50 +0,0 @@ -package proto - -import ( - "bytes" - "encoding/binary" - "fmt" - - "github.com/golang/snappy" -) - -// Snappy-encoded messages from the official Java client are encoded using -// snappy-java: see github.com/xerial/snappy-java. -// This does its own non-standard framing. We can detect this encoding -// by sniffing its special header. -// -// That library will still read plain (unframed) snappy-encoded messages, -// so we don't need to implement that codec on the compression side. -// -// (This is the same behavior as several of the other popular Kafka clients.) - -var snappyJavaMagic = []byte("\x82SNAPPY\x00") - -func snappyDecode(b []byte) ([]byte, error) { - if !bytes.HasPrefix(b, snappyJavaMagic) { - return snappy.Decode(nil, b) - } - - // See https://github.com/xerial/snappy-java/blob/develop/src/main/java/org/xerial/snappy/SnappyInputStream.java - version := binary.BigEndian.Uint32(b[8:12]) - if version != 1 { - return nil, fmt.Errorf("cannot handle snappy-java codec version other than 1 (got %d)", version) - } - // b[12:16] is the "compatible version"; ignore for now - var ( - decoded = make([]byte, 0, len(b)) - chunk []byte - err error - ) - for i := 16; i < len(b); { - n := int(binary.BigEndian.Uint32(b[i : i+4])) - i += 4 - chunk, err = snappy.Decode(chunk, b[i:i+n]) - if err != nil { - return nil, err - } - i += n - decoded = append(decoded, chunk...) - } - return decoded, nil -} diff --git a/vendor/github.com/cilium/kafka/proto/utils.go b/vendor/github.com/cilium/kafka/proto/utils.go deleted file mode 100644 index 37ed2e87f..000000000 --- a/vendor/github.com/cilium/kafka/proto/utils.go +++ /dev/null @@ -1,24 +0,0 @@ -package proto - -import ( - "fmt" - "math" -) - -const ( - maxParseBufSize = 100 * math.MaxUint16 - maxDiscardSize = 4096 -) - -func messageSizeError(size int) error { - return fmt.Errorf("unreasonable message/block size %d (max:%d)", size, maxParseBufSize) -} - -// allocParseBuf is used to allocate buffers used for parsing -func allocParseBuf(size int) ([]byte, error) { - if size < 0 || size > maxParseBufSize { - return nil, messageSizeError(size) - } - - return make([]byte, size), nil -} diff --git a/vendor/github.com/golang/snappy/.gitignore b/vendor/github.com/golang/snappy/.gitignore deleted file mode 100644 index 042091d9b..000000000 --- a/vendor/github.com/golang/snappy/.gitignore +++ /dev/null @@ -1,16 +0,0 @@ -cmd/snappytool/snappytool -testdata/bench - -# These explicitly listed benchmark data files are for an obsolete version of -# snappy_test.go. -testdata/alice29.txt -testdata/asyoulik.txt -testdata/fireworks.jpeg -testdata/geo.protodata -testdata/html -testdata/html_x_4 -testdata/kppkn.gtb -testdata/lcet10.txt -testdata/paper-100k.pdf -testdata/plrabn12.txt -testdata/urls.10K diff --git a/vendor/github.com/golang/snappy/AUTHORS b/vendor/github.com/golang/snappy/AUTHORS deleted file mode 100644 index 52ccb5a93..000000000 --- a/vendor/github.com/golang/snappy/AUTHORS +++ /dev/null @@ -1,18 +0,0 @@ -# This is the official list of Snappy-Go authors for copyright purposes. -# This file is distinct from the CONTRIBUTORS files. -# See the latter for an explanation. - -# Names should be added to this file as -# Name or Organization -# The email address is not required for organizations. - -# Please keep the list sorted. - -Amazon.com, Inc -Damian Gryski -Eric Buth -Google Inc. -Jan Mercl <0xjnml@gmail.com> -Klaus Post -Rodolfo Carvalho -Sebastien Binet diff --git a/vendor/github.com/golang/snappy/CONTRIBUTORS b/vendor/github.com/golang/snappy/CONTRIBUTORS deleted file mode 100644 index ea6524ddd..000000000 --- a/vendor/github.com/golang/snappy/CONTRIBUTORS +++ /dev/null @@ -1,41 +0,0 @@ -# This is the official list of people who can contribute -# (and typically have contributed) code to the Snappy-Go repository. -# The AUTHORS file lists the copyright holders; this file -# lists people. For example, Google employees are listed here -# but not in AUTHORS, because Google holds the copyright. -# -# The submission process automatically checks to make sure -# that people submitting code are listed in this file (by email address). -# -# Names should be added to this file only after verifying that -# the individual or the individual's organization has agreed to -# the appropriate Contributor License Agreement, found here: -# -# http://code.google.com/legal/individual-cla-v1.0.html -# http://code.google.com/legal/corporate-cla-v1.0.html -# -# The agreement for individuals can be filled out on the web. -# -# When adding J Random Contributor's name to this file, -# either J's name or J's organization's name should be -# added to the AUTHORS file, depending on whether the -# individual or corporate CLA was used. - -# Names should be added to this file like so: -# Name - -# Please keep the list sorted. - -Alex Legg -Damian Gryski -Eric Buth -Jan Mercl <0xjnml@gmail.com> -Jonathan Swinney -Kai Backman -Klaus Post -Marc-Antoine Ruel -Nigel Tao -Rob Pike -Rodolfo Carvalho -Russ Cox -Sebastien Binet diff --git a/vendor/github.com/golang/snappy/LICENSE b/vendor/github.com/golang/snappy/LICENSE deleted file mode 100644 index 6050c10f4..000000000 --- a/vendor/github.com/golang/snappy/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2011 The Snappy-Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/golang/snappy/README b/vendor/github.com/golang/snappy/README deleted file mode 100644 index cea12879a..000000000 --- a/vendor/github.com/golang/snappy/README +++ /dev/null @@ -1,107 +0,0 @@ -The Snappy compression format in the Go programming language. - -To download and install from source: -$ go get github.com/golang/snappy - -Unless otherwise noted, the Snappy-Go source files are distributed -under the BSD-style license found in the LICENSE file. - - - -Benchmarks. - -The golang/snappy benchmarks include compressing (Z) and decompressing (U) ten -or so files, the same set used by the C++ Snappy code (github.com/google/snappy -and note the "google", not "golang"). On an "Intel(R) Core(TM) i7-3770 CPU @ -3.40GHz", Go's GOARCH=amd64 numbers as of 2016-05-29: - -"go test -test.bench=." - -_UFlat0-8 2.19GB/s ± 0% html -_UFlat1-8 1.41GB/s ± 0% urls -_UFlat2-8 23.5GB/s ± 2% jpg -_UFlat3-8 1.91GB/s ± 0% jpg_200 -_UFlat4-8 14.0GB/s ± 1% pdf -_UFlat5-8 1.97GB/s ± 0% html4 -_UFlat6-8 814MB/s ± 0% txt1 -_UFlat7-8 785MB/s ± 0% txt2 -_UFlat8-8 857MB/s ± 0% txt3 -_UFlat9-8 719MB/s ± 1% txt4 -_UFlat10-8 2.84GB/s ± 0% pb -_UFlat11-8 1.05GB/s ± 0% gaviota - -_ZFlat0-8 1.04GB/s ± 0% html -_ZFlat1-8 534MB/s ± 0% urls -_ZFlat2-8 15.7GB/s ± 1% jpg -_ZFlat3-8 740MB/s ± 3% jpg_200 -_ZFlat4-8 9.20GB/s ± 1% pdf -_ZFlat5-8 991MB/s ± 0% html4 -_ZFlat6-8 379MB/s ± 0% txt1 -_ZFlat7-8 352MB/s ± 0% txt2 -_ZFlat8-8 396MB/s ± 1% txt3 -_ZFlat9-8 327MB/s ± 1% txt4 -_ZFlat10-8 1.33GB/s ± 1% pb -_ZFlat11-8 605MB/s ± 1% gaviota - - - -"go test -test.bench=. -tags=noasm" - -_UFlat0-8 621MB/s ± 2% html -_UFlat1-8 494MB/s ± 1% urls -_UFlat2-8 23.2GB/s ± 1% jpg -_UFlat3-8 1.12GB/s ± 1% jpg_200 -_UFlat4-8 4.35GB/s ± 1% pdf -_UFlat5-8 609MB/s ± 0% html4 -_UFlat6-8 296MB/s ± 0% txt1 -_UFlat7-8 288MB/s ± 0% txt2 -_UFlat8-8 309MB/s ± 1% txt3 -_UFlat9-8 280MB/s ± 1% txt4 -_UFlat10-8 753MB/s ± 0% pb -_UFlat11-8 400MB/s ± 0% gaviota - -_ZFlat0-8 409MB/s ± 1% html -_ZFlat1-8 250MB/s ± 1% urls -_ZFlat2-8 12.3GB/s ± 1% jpg -_ZFlat3-8 132MB/s ± 0% jpg_200 -_ZFlat4-8 2.92GB/s ± 0% pdf -_ZFlat5-8 405MB/s ± 1% html4 -_ZFlat6-8 179MB/s ± 1% txt1 -_ZFlat7-8 170MB/s ± 1% txt2 -_ZFlat8-8 189MB/s ± 1% txt3 -_ZFlat9-8 164MB/s ± 1% txt4 -_ZFlat10-8 479MB/s ± 1% pb -_ZFlat11-8 270MB/s ± 1% gaviota - - - -For comparison (Go's encoded output is byte-for-byte identical to C++'s), here -are the numbers from C++ Snappy's - -make CXXFLAGS="-O2 -DNDEBUG -g" clean snappy_unittest.log && cat snappy_unittest.log - -BM_UFlat/0 2.4GB/s html -BM_UFlat/1 1.4GB/s urls -BM_UFlat/2 21.8GB/s jpg -BM_UFlat/3 1.5GB/s jpg_200 -BM_UFlat/4 13.3GB/s pdf -BM_UFlat/5 2.1GB/s html4 -BM_UFlat/6 1.0GB/s txt1 -BM_UFlat/7 959.4MB/s txt2 -BM_UFlat/8 1.0GB/s txt3 -BM_UFlat/9 864.5MB/s txt4 -BM_UFlat/10 2.9GB/s pb -BM_UFlat/11 1.2GB/s gaviota - -BM_ZFlat/0 944.3MB/s html (22.31 %) -BM_ZFlat/1 501.6MB/s urls (47.78 %) -BM_ZFlat/2 14.3GB/s jpg (99.95 %) -BM_ZFlat/3 538.3MB/s jpg_200 (73.00 %) -BM_ZFlat/4 8.3GB/s pdf (83.30 %) -BM_ZFlat/5 903.5MB/s html4 (22.52 %) -BM_ZFlat/6 336.0MB/s txt1 (57.88 %) -BM_ZFlat/7 312.3MB/s txt2 (61.91 %) -BM_ZFlat/8 353.1MB/s txt3 (54.99 %) -BM_ZFlat/9 289.9MB/s txt4 (66.26 %) -BM_ZFlat/10 1.2GB/s pb (19.68 %) -BM_ZFlat/11 527.4MB/s gaviota (37.72 %) diff --git a/vendor/github.com/golang/snappy/decode.go b/vendor/github.com/golang/snappy/decode.go deleted file mode 100644 index 23c6e26c6..000000000 --- a/vendor/github.com/golang/snappy/decode.go +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright 2011 The Snappy-Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package snappy - -import ( - "encoding/binary" - "errors" - "io" -) - -var ( - // ErrCorrupt reports that the input is invalid. - ErrCorrupt = errors.New("snappy: corrupt input") - // ErrTooLarge reports that the uncompressed length is too large. - ErrTooLarge = errors.New("snappy: decoded block is too large") - // ErrUnsupported reports that the input isn't supported. - ErrUnsupported = errors.New("snappy: unsupported input") - - errUnsupportedLiteralLength = errors.New("snappy: unsupported literal length") -) - -// DecodedLen returns the length of the decoded block. -func DecodedLen(src []byte) (int, error) { - v, _, err := decodedLen(src) - return v, err -} - -// decodedLen returns the length of the decoded block and the number of bytes -// that the length header occupied. -func decodedLen(src []byte) (blockLen, headerLen int, err error) { - v, n := binary.Uvarint(src) - if n <= 0 || v > 0xffffffff { - return 0, 0, ErrCorrupt - } - - const wordSize = 32 << (^uint(0) >> 32 & 1) - if wordSize == 32 && v > 0x7fffffff { - return 0, 0, ErrTooLarge - } - return int(v), n, nil -} - -const ( - decodeErrCodeCorrupt = 1 - decodeErrCodeUnsupportedLiteralLength = 2 -) - -// Decode returns the decoded form of src. The returned slice may be a sub- -// slice of dst if dst was large enough to hold the entire decoded block. -// Otherwise, a newly allocated slice will be returned. -// -// The dst and src must not overlap. It is valid to pass a nil dst. -// -// Decode handles the Snappy block format, not the Snappy stream format. -func Decode(dst, src []byte) ([]byte, error) { - dLen, s, err := decodedLen(src) - if err != nil { - return nil, err - } - if dLen <= len(dst) { - dst = dst[:dLen] - } else { - dst = make([]byte, dLen) - } - switch decode(dst, src[s:]) { - case 0: - return dst, nil - case decodeErrCodeUnsupportedLiteralLength: - return nil, errUnsupportedLiteralLength - } - return nil, ErrCorrupt -} - -// NewReader returns a new Reader that decompresses from r, using the framing -// format described at -// https://github.com/google/snappy/blob/master/framing_format.txt -func NewReader(r io.Reader) *Reader { - return &Reader{ - r: r, - decoded: make([]byte, maxBlockSize), - buf: make([]byte, maxEncodedLenOfMaxBlockSize+checksumSize), - } -} - -// Reader is an io.Reader that can read Snappy-compressed bytes. -// -// Reader handles the Snappy stream format, not the Snappy block format. -type Reader struct { - r io.Reader - err error - decoded []byte - buf []byte - // decoded[i:j] contains decoded bytes that have not yet been passed on. - i, j int - readHeader bool -} - -// Reset discards any buffered data, resets all state, and switches the Snappy -// reader to read from r. This permits reusing a Reader rather than allocating -// a new one. -func (r *Reader) Reset(reader io.Reader) { - r.r = reader - r.err = nil - r.i = 0 - r.j = 0 - r.readHeader = false -} - -func (r *Reader) readFull(p []byte, allowEOF bool) (ok bool) { - if _, r.err = io.ReadFull(r.r, p); r.err != nil { - if r.err == io.ErrUnexpectedEOF || (r.err == io.EOF && !allowEOF) { - r.err = ErrCorrupt - } - return false - } - return true -} - -func (r *Reader) fill() error { - for r.i >= r.j { - if !r.readFull(r.buf[:4], true) { - return r.err - } - chunkType := r.buf[0] - if !r.readHeader { - if chunkType != chunkTypeStreamIdentifier { - r.err = ErrCorrupt - return r.err - } - r.readHeader = true - } - chunkLen := int(r.buf[1]) | int(r.buf[2])<<8 | int(r.buf[3])<<16 - if chunkLen > len(r.buf) { - r.err = ErrUnsupported - return r.err - } - - // The chunk types are specified at - // https://github.com/google/snappy/blob/master/framing_format.txt - switch chunkType { - case chunkTypeCompressedData: - // Section 4.2. Compressed data (chunk type 0x00). - if chunkLen < checksumSize { - r.err = ErrCorrupt - return r.err - } - buf := r.buf[:chunkLen] - if !r.readFull(buf, false) { - return r.err - } - checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 - buf = buf[checksumSize:] - - n, err := DecodedLen(buf) - if err != nil { - r.err = err - return r.err - } - if n > len(r.decoded) { - r.err = ErrCorrupt - return r.err - } - if _, err := Decode(r.decoded, buf); err != nil { - r.err = err - return r.err - } - if crc(r.decoded[:n]) != checksum { - r.err = ErrCorrupt - return r.err - } - r.i, r.j = 0, n - continue - - case chunkTypeUncompressedData: - // Section 4.3. Uncompressed data (chunk type 0x01). - if chunkLen < checksumSize { - r.err = ErrCorrupt - return r.err - } - buf := r.buf[:checksumSize] - if !r.readFull(buf, false) { - return r.err - } - checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 - // Read directly into r.decoded instead of via r.buf. - n := chunkLen - checksumSize - if n > len(r.decoded) { - r.err = ErrCorrupt - return r.err - } - if !r.readFull(r.decoded[:n], false) { - return r.err - } - if crc(r.decoded[:n]) != checksum { - r.err = ErrCorrupt - return r.err - } - r.i, r.j = 0, n - continue - - case chunkTypeStreamIdentifier: - // Section 4.1. Stream identifier (chunk type 0xff). - if chunkLen != len(magicBody) { - r.err = ErrCorrupt - return r.err - } - if !r.readFull(r.buf[:len(magicBody)], false) { - return r.err - } - for i := 0; i < len(magicBody); i++ { - if r.buf[i] != magicBody[i] { - r.err = ErrCorrupt - return r.err - } - } - continue - } - - if chunkType <= 0x7f { - // Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f). - r.err = ErrUnsupported - return r.err - } - // Section 4.4 Padding (chunk type 0xfe). - // Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd). - if !r.readFull(r.buf[:chunkLen], false) { - return r.err - } - } - - return nil -} - -// Read satisfies the io.Reader interface. -func (r *Reader) Read(p []byte) (int, error) { - if r.err != nil { - return 0, r.err - } - - if err := r.fill(); err != nil { - return 0, err - } - - n := copy(p, r.decoded[r.i:r.j]) - r.i += n - return n, nil -} - -// ReadByte satisfies the io.ByteReader interface. -func (r *Reader) ReadByte() (byte, error) { - if r.err != nil { - return 0, r.err - } - - if err := r.fill(); err != nil { - return 0, err - } - - c := r.decoded[r.i] - r.i++ - return c, nil -} diff --git a/vendor/github.com/golang/snappy/decode_amd64.s b/vendor/github.com/golang/snappy/decode_amd64.s deleted file mode 100644 index e6179f65e..000000000 --- a/vendor/github.com/golang/snappy/decode_amd64.s +++ /dev/null @@ -1,490 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !appengine -// +build gc -// +build !noasm - -#include "textflag.h" - -// The asm code generally follows the pure Go code in decode_other.go, except -// where marked with a "!!!". - -// func decode(dst, src []byte) int -// -// All local variables fit into registers. The non-zero stack size is only to -// spill registers and push args when issuing a CALL. The register allocation: -// - AX scratch -// - BX scratch -// - CX length or x -// - DX offset -// - SI &src[s] -// - DI &dst[d] -// + R8 dst_base -// + R9 dst_len -// + R10 dst_base + dst_len -// + R11 src_base -// + R12 src_len -// + R13 src_base + src_len -// - R14 used by doCopy -// - R15 used by doCopy -// -// The registers R8-R13 (marked with a "+") are set at the start of the -// function, and after a CALL returns, and are not otherwise modified. -// -// The d variable is implicitly DI - R8, and len(dst)-d is R10 - DI. -// The s variable is implicitly SI - R11, and len(src)-s is R13 - SI. -TEXT ·decode(SB), NOSPLIT, $48-56 - // Initialize SI, DI and R8-R13. - MOVQ dst_base+0(FP), R8 - MOVQ dst_len+8(FP), R9 - MOVQ R8, DI - MOVQ R8, R10 - ADDQ R9, R10 - MOVQ src_base+24(FP), R11 - MOVQ src_len+32(FP), R12 - MOVQ R11, SI - MOVQ R11, R13 - ADDQ R12, R13 - -loop: - // for s < len(src) - CMPQ SI, R13 - JEQ end - - // CX = uint32(src[s]) - // - // switch src[s] & 0x03 - MOVBLZX (SI), CX - MOVL CX, BX - ANDL $3, BX - CMPL BX, $1 - JAE tagCopy - - // ---------------------------------------- - // The code below handles literal tags. - - // case tagLiteral: - // x := uint32(src[s] >> 2) - // switch - SHRL $2, CX - CMPL CX, $60 - JAE tagLit60Plus - - // case x < 60: - // s++ - INCQ SI - -doLit: - // This is the end of the inner "switch", when we have a literal tag. - // - // We assume that CX == x and x fits in a uint32, where x is the variable - // used in the pure Go decode_other.go code. - - // length = int(x) + 1 - // - // Unlike the pure Go code, we don't need to check if length <= 0 because - // CX can hold 64 bits, so the increment cannot overflow. - INCQ CX - - // Prepare to check if copying length bytes will run past the end of dst or - // src. - // - // AX = len(dst) - d - // BX = len(src) - s - MOVQ R10, AX - SUBQ DI, AX - MOVQ R13, BX - SUBQ SI, BX - - // !!! Try a faster technique for short (16 or fewer bytes) copies. - // - // if length > 16 || len(dst)-d < 16 || len(src)-s < 16 { - // goto callMemmove // Fall back on calling runtime·memmove. - // } - // - // The C++ snappy code calls this TryFastAppend. It also checks len(src)-s - // against 21 instead of 16, because it cannot assume that all of its input - // is contiguous in memory and so it needs to leave enough source bytes to - // read the next tag without refilling buffers, but Go's Decode assumes - // contiguousness (the src argument is a []byte). - CMPQ CX, $16 - JGT callMemmove - CMPQ AX, $16 - JLT callMemmove - CMPQ BX, $16 - JLT callMemmove - - // !!! Implement the copy from src to dst as a 16-byte load and store. - // (Decode's documentation says that dst and src must not overlap.) - // - // This always copies 16 bytes, instead of only length bytes, but that's - // OK. If the input is a valid Snappy encoding then subsequent iterations - // will fix up the overrun. Otherwise, Decode returns a nil []byte (and a - // non-nil error), so the overrun will be ignored. - // - // Note that on amd64, it is legal and cheap to issue unaligned 8-byte or - // 16-byte loads and stores. This technique probably wouldn't be as - // effective on architectures that are fussier about alignment. - MOVOU 0(SI), X0 - MOVOU X0, 0(DI) - - // d += length - // s += length - ADDQ CX, DI - ADDQ CX, SI - JMP loop - -callMemmove: - // if length > len(dst)-d || length > len(src)-s { etc } - CMPQ CX, AX - JGT errCorrupt - CMPQ CX, BX - JGT errCorrupt - - // copy(dst[d:], src[s:s+length]) - // - // This means calling runtime·memmove(&dst[d], &src[s], length), so we push - // DI, SI and CX as arguments. Coincidentally, we also need to spill those - // three registers to the stack, to save local variables across the CALL. - MOVQ DI, 0(SP) - MOVQ SI, 8(SP) - MOVQ CX, 16(SP) - MOVQ DI, 24(SP) - MOVQ SI, 32(SP) - MOVQ CX, 40(SP) - CALL runtime·memmove(SB) - - // Restore local variables: unspill registers from the stack and - // re-calculate R8-R13. - MOVQ 24(SP), DI - MOVQ 32(SP), SI - MOVQ 40(SP), CX - MOVQ dst_base+0(FP), R8 - MOVQ dst_len+8(FP), R9 - MOVQ R8, R10 - ADDQ R9, R10 - MOVQ src_base+24(FP), R11 - MOVQ src_len+32(FP), R12 - MOVQ R11, R13 - ADDQ R12, R13 - - // d += length - // s += length - ADDQ CX, DI - ADDQ CX, SI - JMP loop - -tagLit60Plus: - // !!! This fragment does the - // - // s += x - 58; if uint(s) > uint(len(src)) { etc } - // - // checks. In the asm version, we code it once instead of once per switch case. - ADDQ CX, SI - SUBQ $58, SI - MOVQ SI, BX - SUBQ R11, BX - CMPQ BX, R12 - JA errCorrupt - - // case x == 60: - CMPL CX, $61 - JEQ tagLit61 - JA tagLit62Plus - - // x = uint32(src[s-1]) - MOVBLZX -1(SI), CX - JMP doLit - -tagLit61: - // case x == 61: - // x = uint32(src[s-2]) | uint32(src[s-1])<<8 - MOVWLZX -2(SI), CX - JMP doLit - -tagLit62Plus: - CMPL CX, $62 - JA tagLit63 - - // case x == 62: - // x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16 - MOVWLZX -3(SI), CX - MOVBLZX -1(SI), BX - SHLL $16, BX - ORL BX, CX - JMP doLit - -tagLit63: - // case x == 63: - // x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24 - MOVL -4(SI), CX - JMP doLit - -// The code above handles literal tags. -// ---------------------------------------- -// The code below handles copy tags. - -tagCopy4: - // case tagCopy4: - // s += 5 - ADDQ $5, SI - - // if uint(s) > uint(len(src)) { etc } - MOVQ SI, BX - SUBQ R11, BX - CMPQ BX, R12 - JA errCorrupt - - // length = 1 + int(src[s-5])>>2 - SHRQ $2, CX - INCQ CX - - // offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24) - MOVLQZX -4(SI), DX - JMP doCopy - -tagCopy2: - // case tagCopy2: - // s += 3 - ADDQ $3, SI - - // if uint(s) > uint(len(src)) { etc } - MOVQ SI, BX - SUBQ R11, BX - CMPQ BX, R12 - JA errCorrupt - - // length = 1 + int(src[s-3])>>2 - SHRQ $2, CX - INCQ CX - - // offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8) - MOVWQZX -2(SI), DX - JMP doCopy - -tagCopy: - // We have a copy tag. We assume that: - // - BX == src[s] & 0x03 - // - CX == src[s] - CMPQ BX, $2 - JEQ tagCopy2 - JA tagCopy4 - - // case tagCopy1: - // s += 2 - ADDQ $2, SI - - // if uint(s) > uint(len(src)) { etc } - MOVQ SI, BX - SUBQ R11, BX - CMPQ BX, R12 - JA errCorrupt - - // offset = int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1])) - MOVQ CX, DX - ANDQ $0xe0, DX - SHLQ $3, DX - MOVBQZX -1(SI), BX - ORQ BX, DX - - // length = 4 + int(src[s-2])>>2&0x7 - SHRQ $2, CX - ANDQ $7, CX - ADDQ $4, CX - -doCopy: - // This is the end of the outer "switch", when we have a copy tag. - // - // We assume that: - // - CX == length && CX > 0 - // - DX == offset - - // if offset <= 0 { etc } - CMPQ DX, $0 - JLE errCorrupt - - // if d < offset { etc } - MOVQ DI, BX - SUBQ R8, BX - CMPQ BX, DX - JLT errCorrupt - - // if length > len(dst)-d { etc } - MOVQ R10, BX - SUBQ DI, BX - CMPQ CX, BX - JGT errCorrupt - - // forwardCopy(dst[d:d+length], dst[d-offset:]); d += length - // - // Set: - // - R14 = len(dst)-d - // - R15 = &dst[d-offset] - MOVQ R10, R14 - SUBQ DI, R14 - MOVQ DI, R15 - SUBQ DX, R15 - - // !!! Try a faster technique for short (16 or fewer bytes) forward copies. - // - // First, try using two 8-byte load/stores, similar to the doLit technique - // above. Even if dst[d:d+length] and dst[d-offset:] can overlap, this is - // still OK if offset >= 8. Note that this has to be two 8-byte load/stores - // and not one 16-byte load/store, and the first store has to be before the - // second load, due to the overlap if offset is in the range [8, 16). - // - // if length > 16 || offset < 8 || len(dst)-d < 16 { - // goto slowForwardCopy - // } - // copy 16 bytes - // d += length - CMPQ CX, $16 - JGT slowForwardCopy - CMPQ DX, $8 - JLT slowForwardCopy - CMPQ R14, $16 - JLT slowForwardCopy - MOVQ 0(R15), AX - MOVQ AX, 0(DI) - MOVQ 8(R15), BX - MOVQ BX, 8(DI) - ADDQ CX, DI - JMP loop - -slowForwardCopy: - // !!! If the forward copy is longer than 16 bytes, or if offset < 8, we - // can still try 8-byte load stores, provided we can overrun up to 10 extra - // bytes. As above, the overrun will be fixed up by subsequent iterations - // of the outermost loop. - // - // The C++ snappy code calls this technique IncrementalCopyFastPath. Its - // commentary says: - // - // ---- - // - // The main part of this loop is a simple copy of eight bytes at a time - // until we've copied (at least) the requested amount of bytes. However, - // if d and d-offset are less than eight bytes apart (indicating a - // repeating pattern of length < 8), we first need to expand the pattern in - // order to get the correct results. For instance, if the buffer looks like - // this, with the eight-byte and patterns marked as - // intervals: - // - // abxxxxxxxxxxxx - // [------] d-offset - // [------] d - // - // a single eight-byte copy from to will repeat the pattern - // once, after which we can move two bytes without moving : - // - // ababxxxxxxxxxx - // [------] d-offset - // [------] d - // - // and repeat the exercise until the two no longer overlap. - // - // This allows us to do very well in the special case of one single byte - // repeated many times, without taking a big hit for more general cases. - // - // The worst case of extra writing past the end of the match occurs when - // offset == 1 and length == 1; the last copy will read from byte positions - // [0..7] and write to [4..11], whereas it was only supposed to write to - // position 1. Thus, ten excess bytes. - // - // ---- - // - // That "10 byte overrun" worst case is confirmed by Go's - // TestSlowForwardCopyOverrun, which also tests the fixUpSlowForwardCopy - // and finishSlowForwardCopy algorithm. - // - // if length > len(dst)-d-10 { - // goto verySlowForwardCopy - // } - SUBQ $10, R14 - CMPQ CX, R14 - JGT verySlowForwardCopy - -makeOffsetAtLeast8: - // !!! As above, expand the pattern so that offset >= 8 and we can use - // 8-byte load/stores. - // - // for offset < 8 { - // copy 8 bytes from dst[d-offset:] to dst[d:] - // length -= offset - // d += offset - // offset += offset - // // The two previous lines together means that d-offset, and therefore - // // R15, is unchanged. - // } - CMPQ DX, $8 - JGE fixUpSlowForwardCopy - MOVQ (R15), BX - MOVQ BX, (DI) - SUBQ DX, CX - ADDQ DX, DI - ADDQ DX, DX - JMP makeOffsetAtLeast8 - -fixUpSlowForwardCopy: - // !!! Add length (which might be negative now) to d (implied by DI being - // &dst[d]) so that d ends up at the right place when we jump back to the - // top of the loop. Before we do that, though, we save DI to AX so that, if - // length is positive, copying the remaining length bytes will write to the - // right place. - MOVQ DI, AX - ADDQ CX, DI - -finishSlowForwardCopy: - // !!! Repeat 8-byte load/stores until length <= 0. Ending with a negative - // length means that we overrun, but as above, that will be fixed up by - // subsequent iterations of the outermost loop. - CMPQ CX, $0 - JLE loop - MOVQ (R15), BX - MOVQ BX, (AX) - ADDQ $8, R15 - ADDQ $8, AX - SUBQ $8, CX - JMP finishSlowForwardCopy - -verySlowForwardCopy: - // verySlowForwardCopy is a simple implementation of forward copy. In C - // parlance, this is a do/while loop instead of a while loop, since we know - // that length > 0. In Go syntax: - // - // for { - // dst[d] = dst[d - offset] - // d++ - // length-- - // if length == 0 { - // break - // } - // } - MOVB (R15), BX - MOVB BX, (DI) - INCQ R15 - INCQ DI - DECQ CX - JNZ verySlowForwardCopy - JMP loop - -// The code above handles copy tags. -// ---------------------------------------- - -end: - // This is the end of the "for s < len(src)". - // - // if d != len(dst) { etc } - CMPQ DI, R10 - JNE errCorrupt - - // return 0 - MOVQ $0, ret+48(FP) - RET - -errCorrupt: - // return decodeErrCodeCorrupt - MOVQ $1, ret+48(FP) - RET diff --git a/vendor/github.com/golang/snappy/decode_arm64.s b/vendor/github.com/golang/snappy/decode_arm64.s deleted file mode 100644 index 7a3ead17e..000000000 --- a/vendor/github.com/golang/snappy/decode_arm64.s +++ /dev/null @@ -1,494 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !appengine -// +build gc -// +build !noasm - -#include "textflag.h" - -// The asm code generally follows the pure Go code in decode_other.go, except -// where marked with a "!!!". - -// func decode(dst, src []byte) int -// -// All local variables fit into registers. The non-zero stack size is only to -// spill registers and push args when issuing a CALL. The register allocation: -// - R2 scratch -// - R3 scratch -// - R4 length or x -// - R5 offset -// - R6 &src[s] -// - R7 &dst[d] -// + R8 dst_base -// + R9 dst_len -// + R10 dst_base + dst_len -// + R11 src_base -// + R12 src_len -// + R13 src_base + src_len -// - R14 used by doCopy -// - R15 used by doCopy -// -// The registers R8-R13 (marked with a "+") are set at the start of the -// function, and after a CALL returns, and are not otherwise modified. -// -// The d variable is implicitly R7 - R8, and len(dst)-d is R10 - R7. -// The s variable is implicitly R6 - R11, and len(src)-s is R13 - R6. -TEXT ·decode(SB), NOSPLIT, $56-56 - // Initialize R6, R7 and R8-R13. - MOVD dst_base+0(FP), R8 - MOVD dst_len+8(FP), R9 - MOVD R8, R7 - MOVD R8, R10 - ADD R9, R10, R10 - MOVD src_base+24(FP), R11 - MOVD src_len+32(FP), R12 - MOVD R11, R6 - MOVD R11, R13 - ADD R12, R13, R13 - -loop: - // for s < len(src) - CMP R13, R6 - BEQ end - - // R4 = uint32(src[s]) - // - // switch src[s] & 0x03 - MOVBU (R6), R4 - MOVW R4, R3 - ANDW $3, R3 - MOVW $1, R1 - CMPW R1, R3 - BGE tagCopy - - // ---------------------------------------- - // The code below handles literal tags. - - // case tagLiteral: - // x := uint32(src[s] >> 2) - // switch - MOVW $60, R1 - LSRW $2, R4, R4 - CMPW R4, R1 - BLS tagLit60Plus - - // case x < 60: - // s++ - ADD $1, R6, R6 - -doLit: - // This is the end of the inner "switch", when we have a literal tag. - // - // We assume that R4 == x and x fits in a uint32, where x is the variable - // used in the pure Go decode_other.go code. - - // length = int(x) + 1 - // - // Unlike the pure Go code, we don't need to check if length <= 0 because - // R4 can hold 64 bits, so the increment cannot overflow. - ADD $1, R4, R4 - - // Prepare to check if copying length bytes will run past the end of dst or - // src. - // - // R2 = len(dst) - d - // R3 = len(src) - s - MOVD R10, R2 - SUB R7, R2, R2 - MOVD R13, R3 - SUB R6, R3, R3 - - // !!! Try a faster technique for short (16 or fewer bytes) copies. - // - // if length > 16 || len(dst)-d < 16 || len(src)-s < 16 { - // goto callMemmove // Fall back on calling runtime·memmove. - // } - // - // The C++ snappy code calls this TryFastAppend. It also checks len(src)-s - // against 21 instead of 16, because it cannot assume that all of its input - // is contiguous in memory and so it needs to leave enough source bytes to - // read the next tag without refilling buffers, but Go's Decode assumes - // contiguousness (the src argument is a []byte). - CMP $16, R4 - BGT callMemmove - CMP $16, R2 - BLT callMemmove - CMP $16, R3 - BLT callMemmove - - // !!! Implement the copy from src to dst as a 16-byte load and store. - // (Decode's documentation says that dst and src must not overlap.) - // - // This always copies 16 bytes, instead of only length bytes, but that's - // OK. If the input is a valid Snappy encoding then subsequent iterations - // will fix up the overrun. Otherwise, Decode returns a nil []byte (and a - // non-nil error), so the overrun will be ignored. - // - // Note that on arm64, it is legal and cheap to issue unaligned 8-byte or - // 16-byte loads and stores. This technique probably wouldn't be as - // effective on architectures that are fussier about alignment. - LDP 0(R6), (R14, R15) - STP (R14, R15), 0(R7) - - // d += length - // s += length - ADD R4, R7, R7 - ADD R4, R6, R6 - B loop - -callMemmove: - // if length > len(dst)-d || length > len(src)-s { etc } - CMP R2, R4 - BGT errCorrupt - CMP R3, R4 - BGT errCorrupt - - // copy(dst[d:], src[s:s+length]) - // - // This means calling runtime·memmove(&dst[d], &src[s], length), so we push - // R7, R6 and R4 as arguments. Coincidentally, we also need to spill those - // three registers to the stack, to save local variables across the CALL. - MOVD R7, 8(RSP) - MOVD R6, 16(RSP) - MOVD R4, 24(RSP) - MOVD R7, 32(RSP) - MOVD R6, 40(RSP) - MOVD R4, 48(RSP) - CALL runtime·memmove(SB) - - // Restore local variables: unspill registers from the stack and - // re-calculate R8-R13. - MOVD 32(RSP), R7 - MOVD 40(RSP), R6 - MOVD 48(RSP), R4 - MOVD dst_base+0(FP), R8 - MOVD dst_len+8(FP), R9 - MOVD R8, R10 - ADD R9, R10, R10 - MOVD src_base+24(FP), R11 - MOVD src_len+32(FP), R12 - MOVD R11, R13 - ADD R12, R13, R13 - - // d += length - // s += length - ADD R4, R7, R7 - ADD R4, R6, R6 - B loop - -tagLit60Plus: - // !!! This fragment does the - // - // s += x - 58; if uint(s) > uint(len(src)) { etc } - // - // checks. In the asm version, we code it once instead of once per switch case. - ADD R4, R6, R6 - SUB $58, R6, R6 - MOVD R6, R3 - SUB R11, R3, R3 - CMP R12, R3 - BGT errCorrupt - - // case x == 60: - MOVW $61, R1 - CMPW R1, R4 - BEQ tagLit61 - BGT tagLit62Plus - - // x = uint32(src[s-1]) - MOVBU -1(R6), R4 - B doLit - -tagLit61: - // case x == 61: - // x = uint32(src[s-2]) | uint32(src[s-1])<<8 - MOVHU -2(R6), R4 - B doLit - -tagLit62Plus: - CMPW $62, R4 - BHI tagLit63 - - // case x == 62: - // x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16 - MOVHU -3(R6), R4 - MOVBU -1(R6), R3 - ORR R3<<16, R4 - B doLit - -tagLit63: - // case x == 63: - // x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24 - MOVWU -4(R6), R4 - B doLit - - // The code above handles literal tags. - // ---------------------------------------- - // The code below handles copy tags. - -tagCopy4: - // case tagCopy4: - // s += 5 - ADD $5, R6, R6 - - // if uint(s) > uint(len(src)) { etc } - MOVD R6, R3 - SUB R11, R3, R3 - CMP R12, R3 - BGT errCorrupt - - // length = 1 + int(src[s-5])>>2 - MOVD $1, R1 - ADD R4>>2, R1, R4 - - // offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24) - MOVWU -4(R6), R5 - B doCopy - -tagCopy2: - // case tagCopy2: - // s += 3 - ADD $3, R6, R6 - - // if uint(s) > uint(len(src)) { etc } - MOVD R6, R3 - SUB R11, R3, R3 - CMP R12, R3 - BGT errCorrupt - - // length = 1 + int(src[s-3])>>2 - MOVD $1, R1 - ADD R4>>2, R1, R4 - - // offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8) - MOVHU -2(R6), R5 - B doCopy - -tagCopy: - // We have a copy tag. We assume that: - // - R3 == src[s] & 0x03 - // - R4 == src[s] - CMP $2, R3 - BEQ tagCopy2 - BGT tagCopy4 - - // case tagCopy1: - // s += 2 - ADD $2, R6, R6 - - // if uint(s) > uint(len(src)) { etc } - MOVD R6, R3 - SUB R11, R3, R3 - CMP R12, R3 - BGT errCorrupt - - // offset = int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1])) - MOVD R4, R5 - AND $0xe0, R5 - MOVBU -1(R6), R3 - ORR R5<<3, R3, R5 - - // length = 4 + int(src[s-2])>>2&0x7 - MOVD $7, R1 - AND R4>>2, R1, R4 - ADD $4, R4, R4 - -doCopy: - // This is the end of the outer "switch", when we have a copy tag. - // - // We assume that: - // - R4 == length && R4 > 0 - // - R5 == offset - - // if offset <= 0 { etc } - MOVD $0, R1 - CMP R1, R5 - BLE errCorrupt - - // if d < offset { etc } - MOVD R7, R3 - SUB R8, R3, R3 - CMP R5, R3 - BLT errCorrupt - - // if length > len(dst)-d { etc } - MOVD R10, R3 - SUB R7, R3, R3 - CMP R3, R4 - BGT errCorrupt - - // forwardCopy(dst[d:d+length], dst[d-offset:]); d += length - // - // Set: - // - R14 = len(dst)-d - // - R15 = &dst[d-offset] - MOVD R10, R14 - SUB R7, R14, R14 - MOVD R7, R15 - SUB R5, R15, R15 - - // !!! Try a faster technique for short (16 or fewer bytes) forward copies. - // - // First, try using two 8-byte load/stores, similar to the doLit technique - // above. Even if dst[d:d+length] and dst[d-offset:] can overlap, this is - // still OK if offset >= 8. Note that this has to be two 8-byte load/stores - // and not one 16-byte load/store, and the first store has to be before the - // second load, due to the overlap if offset is in the range [8, 16). - // - // if length > 16 || offset < 8 || len(dst)-d < 16 { - // goto slowForwardCopy - // } - // copy 16 bytes - // d += length - CMP $16, R4 - BGT slowForwardCopy - CMP $8, R5 - BLT slowForwardCopy - CMP $16, R14 - BLT slowForwardCopy - MOVD 0(R15), R2 - MOVD R2, 0(R7) - MOVD 8(R15), R3 - MOVD R3, 8(R7) - ADD R4, R7, R7 - B loop - -slowForwardCopy: - // !!! If the forward copy is longer than 16 bytes, or if offset < 8, we - // can still try 8-byte load stores, provided we can overrun up to 10 extra - // bytes. As above, the overrun will be fixed up by subsequent iterations - // of the outermost loop. - // - // The C++ snappy code calls this technique IncrementalCopyFastPath. Its - // commentary says: - // - // ---- - // - // The main part of this loop is a simple copy of eight bytes at a time - // until we've copied (at least) the requested amount of bytes. However, - // if d and d-offset are less than eight bytes apart (indicating a - // repeating pattern of length < 8), we first need to expand the pattern in - // order to get the correct results. For instance, if the buffer looks like - // this, with the eight-byte and patterns marked as - // intervals: - // - // abxxxxxxxxxxxx - // [------] d-offset - // [------] d - // - // a single eight-byte copy from to will repeat the pattern - // once, after which we can move two bytes without moving : - // - // ababxxxxxxxxxx - // [------] d-offset - // [------] d - // - // and repeat the exercise until the two no longer overlap. - // - // This allows us to do very well in the special case of one single byte - // repeated many times, without taking a big hit for more general cases. - // - // The worst case of extra writing past the end of the match occurs when - // offset == 1 and length == 1; the last copy will read from byte positions - // [0..7] and write to [4..11], whereas it was only supposed to write to - // position 1. Thus, ten excess bytes. - // - // ---- - // - // That "10 byte overrun" worst case is confirmed by Go's - // TestSlowForwardCopyOverrun, which also tests the fixUpSlowForwardCopy - // and finishSlowForwardCopy algorithm. - // - // if length > len(dst)-d-10 { - // goto verySlowForwardCopy - // } - SUB $10, R14, R14 - CMP R14, R4 - BGT verySlowForwardCopy - -makeOffsetAtLeast8: - // !!! As above, expand the pattern so that offset >= 8 and we can use - // 8-byte load/stores. - // - // for offset < 8 { - // copy 8 bytes from dst[d-offset:] to dst[d:] - // length -= offset - // d += offset - // offset += offset - // // The two previous lines together means that d-offset, and therefore - // // R15, is unchanged. - // } - CMP $8, R5 - BGE fixUpSlowForwardCopy - MOVD (R15), R3 - MOVD R3, (R7) - SUB R5, R4, R4 - ADD R5, R7, R7 - ADD R5, R5, R5 - B makeOffsetAtLeast8 - -fixUpSlowForwardCopy: - // !!! Add length (which might be negative now) to d (implied by R7 being - // &dst[d]) so that d ends up at the right place when we jump back to the - // top of the loop. Before we do that, though, we save R7 to R2 so that, if - // length is positive, copying the remaining length bytes will write to the - // right place. - MOVD R7, R2 - ADD R4, R7, R7 - -finishSlowForwardCopy: - // !!! Repeat 8-byte load/stores until length <= 0. Ending with a negative - // length means that we overrun, but as above, that will be fixed up by - // subsequent iterations of the outermost loop. - MOVD $0, R1 - CMP R1, R4 - BLE loop - MOVD (R15), R3 - MOVD R3, (R2) - ADD $8, R15, R15 - ADD $8, R2, R2 - SUB $8, R4, R4 - B finishSlowForwardCopy - -verySlowForwardCopy: - // verySlowForwardCopy is a simple implementation of forward copy. In C - // parlance, this is a do/while loop instead of a while loop, since we know - // that length > 0. In Go syntax: - // - // for { - // dst[d] = dst[d - offset] - // d++ - // length-- - // if length == 0 { - // break - // } - // } - MOVB (R15), R3 - MOVB R3, (R7) - ADD $1, R15, R15 - ADD $1, R7, R7 - SUB $1, R4, R4 - CBNZ R4, verySlowForwardCopy - B loop - - // The code above handles copy tags. - // ---------------------------------------- - -end: - // This is the end of the "for s < len(src)". - // - // if d != len(dst) { etc } - CMP R10, R7 - BNE errCorrupt - - // return 0 - MOVD $0, ret+48(FP) - RET - -errCorrupt: - // return decodeErrCodeCorrupt - MOVD $1, R2 - MOVD R2, ret+48(FP) - RET diff --git a/vendor/github.com/golang/snappy/decode_asm.go b/vendor/github.com/golang/snappy/decode_asm.go deleted file mode 100644 index 7082b3491..000000000 --- a/vendor/github.com/golang/snappy/decode_asm.go +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2016 The Snappy-Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !appengine -// +build gc -// +build !noasm -// +build amd64 arm64 - -package snappy - -// decode has the same semantics as in decode_other.go. -// -//go:noescape -func decode(dst, src []byte) int diff --git a/vendor/github.com/golang/snappy/decode_other.go b/vendor/github.com/golang/snappy/decode_other.go deleted file mode 100644 index 2f672be55..000000000 --- a/vendor/github.com/golang/snappy/decode_other.go +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2016 The Snappy-Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !amd64,!arm64 appengine !gc noasm - -package snappy - -// decode writes the decoding of src to dst. It assumes that the varint-encoded -// length of the decompressed bytes has already been read, and that len(dst) -// equals that length. -// -// It returns 0 on success or a decodeErrCodeXxx error code on failure. -func decode(dst, src []byte) int { - var d, s, offset, length int - for s < len(src) { - switch src[s] & 0x03 { - case tagLiteral: - x := uint32(src[s] >> 2) - switch { - case x < 60: - s++ - case x == 60: - s += 2 - if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. - return decodeErrCodeCorrupt - } - x = uint32(src[s-1]) - case x == 61: - s += 3 - if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. - return decodeErrCodeCorrupt - } - x = uint32(src[s-2]) | uint32(src[s-1])<<8 - case x == 62: - s += 4 - if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. - return decodeErrCodeCorrupt - } - x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16 - case x == 63: - s += 5 - if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. - return decodeErrCodeCorrupt - } - x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24 - } - length = int(x) + 1 - if length <= 0 { - return decodeErrCodeUnsupportedLiteralLength - } - if length > len(dst)-d || length > len(src)-s { - return decodeErrCodeCorrupt - } - copy(dst[d:], src[s:s+length]) - d += length - s += length - continue - - case tagCopy1: - s += 2 - if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. - return decodeErrCodeCorrupt - } - length = 4 + int(src[s-2])>>2&0x7 - offset = int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1])) - - case tagCopy2: - s += 3 - if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. - return decodeErrCodeCorrupt - } - length = 1 + int(src[s-3])>>2 - offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8) - - case tagCopy4: - s += 5 - if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. - return decodeErrCodeCorrupt - } - length = 1 + int(src[s-5])>>2 - offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24) - } - - if offset <= 0 || d < offset || length > len(dst)-d { - return decodeErrCodeCorrupt - } - // Copy from an earlier sub-slice of dst to a later sub-slice. - // If no overlap, use the built-in copy: - if offset >= length { - copy(dst[d:d+length], dst[d-offset:]) - d += length - continue - } - - // Unlike the built-in copy function, this byte-by-byte copy always runs - // forwards, even if the slices overlap. Conceptually, this is: - // - // d += forwardCopy(dst[d:d+length], dst[d-offset:]) - // - // We align the slices into a and b and show the compiler they are the same size. - // This allows the loop to run without bounds checks. - a := dst[d : d+length] - b := dst[d-offset:] - b = b[:len(a)] - for i := range a { - a[i] = b[i] - } - d += length - } - if d != len(dst) { - return decodeErrCodeCorrupt - } - return 0 -} diff --git a/vendor/github.com/golang/snappy/encode.go b/vendor/github.com/golang/snappy/encode.go deleted file mode 100644 index 7f2365707..000000000 --- a/vendor/github.com/golang/snappy/encode.go +++ /dev/null @@ -1,289 +0,0 @@ -// Copyright 2011 The Snappy-Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package snappy - -import ( - "encoding/binary" - "errors" - "io" -) - -// Encode returns the encoded form of src. The returned slice may be a sub- -// slice of dst if dst was large enough to hold the entire encoded block. -// Otherwise, a newly allocated slice will be returned. -// -// The dst and src must not overlap. It is valid to pass a nil dst. -// -// Encode handles the Snappy block format, not the Snappy stream format. -func Encode(dst, src []byte) []byte { - if n := MaxEncodedLen(len(src)); n < 0 { - panic(ErrTooLarge) - } else if len(dst) < n { - dst = make([]byte, n) - } - - // The block starts with the varint-encoded length of the decompressed bytes. - d := binary.PutUvarint(dst, uint64(len(src))) - - for len(src) > 0 { - p := src - src = nil - if len(p) > maxBlockSize { - p, src = p[:maxBlockSize], p[maxBlockSize:] - } - if len(p) < minNonLiteralBlockSize { - d += emitLiteral(dst[d:], p) - } else { - d += encodeBlock(dst[d:], p) - } - } - return dst[:d] -} - -// inputMargin is the minimum number of extra input bytes to keep, inside -// encodeBlock's inner loop. On some architectures, this margin lets us -// implement a fast path for emitLiteral, where the copy of short (<= 16 byte) -// literals can be implemented as a single load to and store from a 16-byte -// register. That literal's actual length can be as short as 1 byte, so this -// can copy up to 15 bytes too much, but that's OK as subsequent iterations of -// the encoding loop will fix up the copy overrun, and this inputMargin ensures -// that we don't overrun the dst and src buffers. -const inputMargin = 16 - 1 - -// minNonLiteralBlockSize is the minimum size of the input to encodeBlock that -// could be encoded with a copy tag. This is the minimum with respect to the -// algorithm used by encodeBlock, not a minimum enforced by the file format. -// -// The encoded output must start with at least a 1 byte literal, as there are -// no previous bytes to copy. A minimal (1 byte) copy after that, generated -// from an emitCopy call in encodeBlock's main loop, would require at least -// another inputMargin bytes, for the reason above: we want any emitLiteral -// calls inside encodeBlock's main loop to use the fast path if possible, which -// requires being able to overrun by inputMargin bytes. Thus, -// minNonLiteralBlockSize equals 1 + 1 + inputMargin. -// -// The C++ code doesn't use this exact threshold, but it could, as discussed at -// https://groups.google.com/d/topic/snappy-compression/oGbhsdIJSJ8/discussion -// The difference between Go (2+inputMargin) and C++ (inputMargin) is purely an -// optimization. It should not affect the encoded form. This is tested by -// TestSameEncodingAsCppShortCopies. -const minNonLiteralBlockSize = 1 + 1 + inputMargin - -// MaxEncodedLen returns the maximum length of a snappy block, given its -// uncompressed length. -// -// It will return a negative value if srcLen is too large to encode. -func MaxEncodedLen(srcLen int) int { - n := uint64(srcLen) - if n > 0xffffffff { - return -1 - } - // Compressed data can be defined as: - // compressed := item* literal* - // item := literal* copy - // - // The trailing literal sequence has a space blowup of at most 62/60 - // since a literal of length 60 needs one tag byte + one extra byte - // for length information. - // - // Item blowup is trickier to measure. Suppose the "copy" op copies - // 4 bytes of data. Because of a special check in the encoding code, - // we produce a 4-byte copy only if the offset is < 65536. Therefore - // the copy op takes 3 bytes to encode, and this type of item leads - // to at most the 62/60 blowup for representing literals. - // - // Suppose the "copy" op copies 5 bytes of data. If the offset is big - // enough, it will take 5 bytes to encode the copy op. Therefore the - // worst case here is a one-byte literal followed by a five-byte copy. - // That is, 6 bytes of input turn into 7 bytes of "compressed" data. - // - // This last factor dominates the blowup, so the final estimate is: - n = 32 + n + n/6 - if n > 0xffffffff { - return -1 - } - return int(n) -} - -var errClosed = errors.New("snappy: Writer is closed") - -// NewWriter returns a new Writer that compresses to w. -// -// The Writer returned does not buffer writes. There is no need to Flush or -// Close such a Writer. -// -// Deprecated: the Writer returned is not suitable for many small writes, only -// for few large writes. Use NewBufferedWriter instead, which is efficient -// regardless of the frequency and shape of the writes, and remember to Close -// that Writer when done. -func NewWriter(w io.Writer) *Writer { - return &Writer{ - w: w, - obuf: make([]byte, obufLen), - } -} - -// NewBufferedWriter returns a new Writer that compresses to w, using the -// framing format described at -// https://github.com/google/snappy/blob/master/framing_format.txt -// -// The Writer returned buffers writes. Users must call Close to guarantee all -// data has been forwarded to the underlying io.Writer. They may also call -// Flush zero or more times before calling Close. -func NewBufferedWriter(w io.Writer) *Writer { - return &Writer{ - w: w, - ibuf: make([]byte, 0, maxBlockSize), - obuf: make([]byte, obufLen), - } -} - -// Writer is an io.Writer that can write Snappy-compressed bytes. -// -// Writer handles the Snappy stream format, not the Snappy block format. -type Writer struct { - w io.Writer - err error - - // ibuf is a buffer for the incoming (uncompressed) bytes. - // - // Its use is optional. For backwards compatibility, Writers created by the - // NewWriter function have ibuf == nil, do not buffer incoming bytes, and - // therefore do not need to be Flush'ed or Close'd. - ibuf []byte - - // obuf is a buffer for the outgoing (compressed) bytes. - obuf []byte - - // wroteStreamHeader is whether we have written the stream header. - wroteStreamHeader bool -} - -// Reset discards the writer's state and switches the Snappy writer to write to -// w. This permits reusing a Writer rather than allocating a new one. -func (w *Writer) Reset(writer io.Writer) { - w.w = writer - w.err = nil - if w.ibuf != nil { - w.ibuf = w.ibuf[:0] - } - w.wroteStreamHeader = false -} - -// Write satisfies the io.Writer interface. -func (w *Writer) Write(p []byte) (nRet int, errRet error) { - if w.ibuf == nil { - // Do not buffer incoming bytes. This does not perform or compress well - // if the caller of Writer.Write writes many small slices. This - // behavior is therefore deprecated, but still supported for backwards - // compatibility with code that doesn't explicitly Flush or Close. - return w.write(p) - } - - // The remainder of this method is based on bufio.Writer.Write from the - // standard library. - - for len(p) > (cap(w.ibuf)-len(w.ibuf)) && w.err == nil { - var n int - if len(w.ibuf) == 0 { - // Large write, empty buffer. - // Write directly from p to avoid copy. - n, _ = w.write(p) - } else { - n = copy(w.ibuf[len(w.ibuf):cap(w.ibuf)], p) - w.ibuf = w.ibuf[:len(w.ibuf)+n] - w.Flush() - } - nRet += n - p = p[n:] - } - if w.err != nil { - return nRet, w.err - } - n := copy(w.ibuf[len(w.ibuf):cap(w.ibuf)], p) - w.ibuf = w.ibuf[:len(w.ibuf)+n] - nRet += n - return nRet, nil -} - -func (w *Writer) write(p []byte) (nRet int, errRet error) { - if w.err != nil { - return 0, w.err - } - for len(p) > 0 { - obufStart := len(magicChunk) - if !w.wroteStreamHeader { - w.wroteStreamHeader = true - copy(w.obuf, magicChunk) - obufStart = 0 - } - - var uncompressed []byte - if len(p) > maxBlockSize { - uncompressed, p = p[:maxBlockSize], p[maxBlockSize:] - } else { - uncompressed, p = p, nil - } - checksum := crc(uncompressed) - - // Compress the buffer, discarding the result if the improvement - // isn't at least 12.5%. - compressed := Encode(w.obuf[obufHeaderLen:], uncompressed) - chunkType := uint8(chunkTypeCompressedData) - chunkLen := 4 + len(compressed) - obufEnd := obufHeaderLen + len(compressed) - if len(compressed) >= len(uncompressed)-len(uncompressed)/8 { - chunkType = chunkTypeUncompressedData - chunkLen = 4 + len(uncompressed) - obufEnd = obufHeaderLen - } - - // Fill in the per-chunk header that comes before the body. - w.obuf[len(magicChunk)+0] = chunkType - w.obuf[len(magicChunk)+1] = uint8(chunkLen >> 0) - w.obuf[len(magicChunk)+2] = uint8(chunkLen >> 8) - w.obuf[len(magicChunk)+3] = uint8(chunkLen >> 16) - w.obuf[len(magicChunk)+4] = uint8(checksum >> 0) - w.obuf[len(magicChunk)+5] = uint8(checksum >> 8) - w.obuf[len(magicChunk)+6] = uint8(checksum >> 16) - w.obuf[len(magicChunk)+7] = uint8(checksum >> 24) - - if _, err := w.w.Write(w.obuf[obufStart:obufEnd]); err != nil { - w.err = err - return nRet, err - } - if chunkType == chunkTypeUncompressedData { - if _, err := w.w.Write(uncompressed); err != nil { - w.err = err - return nRet, err - } - } - nRet += len(uncompressed) - } - return nRet, nil -} - -// Flush flushes the Writer to its underlying io.Writer. -func (w *Writer) Flush() error { - if w.err != nil { - return w.err - } - if len(w.ibuf) == 0 { - return nil - } - w.write(w.ibuf) - w.ibuf = w.ibuf[:0] - return w.err -} - -// Close calls Flush and then closes the Writer. -func (w *Writer) Close() error { - w.Flush() - ret := w.err - if w.err == nil { - w.err = errClosed - } - return ret -} diff --git a/vendor/github.com/golang/snappy/encode_amd64.s b/vendor/github.com/golang/snappy/encode_amd64.s deleted file mode 100644 index adfd979fe..000000000 --- a/vendor/github.com/golang/snappy/encode_amd64.s +++ /dev/null @@ -1,730 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !appengine -// +build gc -// +build !noasm - -#include "textflag.h" - -// The XXX lines assemble on Go 1.4, 1.5 and 1.7, but not 1.6, due to a -// Go toolchain regression. See https://github.com/golang/go/issues/15426 and -// https://github.com/golang/snappy/issues/29 -// -// As a workaround, the package was built with a known good assembler, and -// those instructions were disassembled by "objdump -d" to yield the -// 4e 0f b7 7c 5c 78 movzwq 0x78(%rsp,%r11,2),%r15 -// style comments, in AT&T asm syntax. Note that rsp here is a physical -// register, not Go/asm's SP pseudo-register (see https://golang.org/doc/asm). -// The instructions were then encoded as "BYTE $0x.." sequences, which assemble -// fine on Go 1.6. - -// The asm code generally follows the pure Go code in encode_other.go, except -// where marked with a "!!!". - -// ---------------------------------------------------------------------------- - -// func emitLiteral(dst, lit []byte) int -// -// All local variables fit into registers. The register allocation: -// - AX len(lit) -// - BX n -// - DX return value -// - DI &dst[i] -// - R10 &lit[0] -// -// The 24 bytes of stack space is to call runtime·memmove. -// -// The unusual register allocation of local variables, such as R10 for the -// source pointer, matches the allocation used at the call site in encodeBlock, -// which makes it easier to manually inline this function. -TEXT ·emitLiteral(SB), NOSPLIT, $24-56 - MOVQ dst_base+0(FP), DI - MOVQ lit_base+24(FP), R10 - MOVQ lit_len+32(FP), AX - MOVQ AX, DX - MOVL AX, BX - SUBL $1, BX - - CMPL BX, $60 - JLT oneByte - CMPL BX, $256 - JLT twoBytes - -threeBytes: - MOVB $0xf4, 0(DI) - MOVW BX, 1(DI) - ADDQ $3, DI - ADDQ $3, DX - JMP memmove - -twoBytes: - MOVB $0xf0, 0(DI) - MOVB BX, 1(DI) - ADDQ $2, DI - ADDQ $2, DX - JMP memmove - -oneByte: - SHLB $2, BX - MOVB BX, 0(DI) - ADDQ $1, DI - ADDQ $1, DX - -memmove: - MOVQ DX, ret+48(FP) - - // copy(dst[i:], lit) - // - // This means calling runtime·memmove(&dst[i], &lit[0], len(lit)), so we push - // DI, R10 and AX as arguments. - MOVQ DI, 0(SP) - MOVQ R10, 8(SP) - MOVQ AX, 16(SP) - CALL runtime·memmove(SB) - RET - -// ---------------------------------------------------------------------------- - -// func emitCopy(dst []byte, offset, length int) int -// -// All local variables fit into registers. The register allocation: -// - AX length -// - SI &dst[0] -// - DI &dst[i] -// - R11 offset -// -// The unusual register allocation of local variables, such as R11 for the -// offset, matches the allocation used at the call site in encodeBlock, which -// makes it easier to manually inline this function. -TEXT ·emitCopy(SB), NOSPLIT, $0-48 - MOVQ dst_base+0(FP), DI - MOVQ DI, SI - MOVQ offset+24(FP), R11 - MOVQ length+32(FP), AX - -loop0: - // for length >= 68 { etc } - CMPL AX, $68 - JLT step1 - - // Emit a length 64 copy, encoded as 3 bytes. - MOVB $0xfe, 0(DI) - MOVW R11, 1(DI) - ADDQ $3, DI - SUBL $64, AX - JMP loop0 - -step1: - // if length > 64 { etc } - CMPL AX, $64 - JLE step2 - - // Emit a length 60 copy, encoded as 3 bytes. - MOVB $0xee, 0(DI) - MOVW R11, 1(DI) - ADDQ $3, DI - SUBL $60, AX - -step2: - // if length >= 12 || offset >= 2048 { goto step3 } - CMPL AX, $12 - JGE step3 - CMPL R11, $2048 - JGE step3 - - // Emit the remaining copy, encoded as 2 bytes. - MOVB R11, 1(DI) - SHRL $8, R11 - SHLB $5, R11 - SUBB $4, AX - SHLB $2, AX - ORB AX, R11 - ORB $1, R11 - MOVB R11, 0(DI) - ADDQ $2, DI - - // Return the number of bytes written. - SUBQ SI, DI - MOVQ DI, ret+40(FP) - RET - -step3: - // Emit the remaining copy, encoded as 3 bytes. - SUBL $1, AX - SHLB $2, AX - ORB $2, AX - MOVB AX, 0(DI) - MOVW R11, 1(DI) - ADDQ $3, DI - - // Return the number of bytes written. - SUBQ SI, DI - MOVQ DI, ret+40(FP) - RET - -// ---------------------------------------------------------------------------- - -// func extendMatch(src []byte, i, j int) int -// -// All local variables fit into registers. The register allocation: -// - DX &src[0] -// - SI &src[j] -// - R13 &src[len(src) - 8] -// - R14 &src[len(src)] -// - R15 &src[i] -// -// The unusual register allocation of local variables, such as R15 for a source -// pointer, matches the allocation used at the call site in encodeBlock, which -// makes it easier to manually inline this function. -TEXT ·extendMatch(SB), NOSPLIT, $0-48 - MOVQ src_base+0(FP), DX - MOVQ src_len+8(FP), R14 - MOVQ i+24(FP), R15 - MOVQ j+32(FP), SI - ADDQ DX, R14 - ADDQ DX, R15 - ADDQ DX, SI - MOVQ R14, R13 - SUBQ $8, R13 - -cmp8: - // As long as we are 8 or more bytes before the end of src, we can load and - // compare 8 bytes at a time. If those 8 bytes are equal, repeat. - CMPQ SI, R13 - JA cmp1 - MOVQ (R15), AX - MOVQ (SI), BX - CMPQ AX, BX - JNE bsf - ADDQ $8, R15 - ADDQ $8, SI - JMP cmp8 - -bsf: - // If those 8 bytes were not equal, XOR the two 8 byte values, and return - // the index of the first byte that differs. The BSF instruction finds the - // least significant 1 bit, the amd64 architecture is little-endian, and - // the shift by 3 converts a bit index to a byte index. - XORQ AX, BX - BSFQ BX, BX - SHRQ $3, BX - ADDQ BX, SI - - // Convert from &src[ret] to ret. - SUBQ DX, SI - MOVQ SI, ret+40(FP) - RET - -cmp1: - // In src's tail, compare 1 byte at a time. - CMPQ SI, R14 - JAE extendMatchEnd - MOVB (R15), AX - MOVB (SI), BX - CMPB AX, BX - JNE extendMatchEnd - ADDQ $1, R15 - ADDQ $1, SI - JMP cmp1 - -extendMatchEnd: - // Convert from &src[ret] to ret. - SUBQ DX, SI - MOVQ SI, ret+40(FP) - RET - -// ---------------------------------------------------------------------------- - -// func encodeBlock(dst, src []byte) (d int) -// -// All local variables fit into registers, other than "var table". The register -// allocation: -// - AX . . -// - BX . . -// - CX 56 shift (note that amd64 shifts by non-immediates must use CX). -// - DX 64 &src[0], tableSize -// - SI 72 &src[s] -// - DI 80 &dst[d] -// - R9 88 sLimit -// - R10 . &src[nextEmit] -// - R11 96 prevHash, currHash, nextHash, offset -// - R12 104 &src[base], skip -// - R13 . &src[nextS], &src[len(src) - 8] -// - R14 . len(src), bytesBetweenHashLookups, &src[len(src)], x -// - R15 112 candidate -// -// The second column (56, 64, etc) is the stack offset to spill the registers -// when calling other functions. We could pack this slightly tighter, but it's -// simpler to have a dedicated spill map independent of the function called. -// -// "var table [maxTableSize]uint16" takes up 32768 bytes of stack space. An -// extra 56 bytes, to call other functions, and an extra 64 bytes, to spill -// local variables (registers) during calls gives 32768 + 56 + 64 = 32888. -TEXT ·encodeBlock(SB), 0, $32888-56 - MOVQ dst_base+0(FP), DI - MOVQ src_base+24(FP), SI - MOVQ src_len+32(FP), R14 - - // shift, tableSize := uint32(32-8), 1<<8 - MOVQ $24, CX - MOVQ $256, DX - -calcShift: - // for ; tableSize < maxTableSize && tableSize < len(src); tableSize *= 2 { - // shift-- - // } - CMPQ DX, $16384 - JGE varTable - CMPQ DX, R14 - JGE varTable - SUBQ $1, CX - SHLQ $1, DX - JMP calcShift - -varTable: - // var table [maxTableSize]uint16 - // - // In the asm code, unlike the Go code, we can zero-initialize only the - // first tableSize elements. Each uint16 element is 2 bytes and each MOVOU - // writes 16 bytes, so we can do only tableSize/8 writes instead of the - // 2048 writes that would zero-initialize all of table's 32768 bytes. - SHRQ $3, DX - LEAQ table-32768(SP), BX - PXOR X0, X0 - -memclr: - MOVOU X0, 0(BX) - ADDQ $16, BX - SUBQ $1, DX - JNZ memclr - - // !!! DX = &src[0] - MOVQ SI, DX - - // sLimit := len(src) - inputMargin - MOVQ R14, R9 - SUBQ $15, R9 - - // !!! Pre-emptively spill CX, DX and R9 to the stack. Their values don't - // change for the rest of the function. - MOVQ CX, 56(SP) - MOVQ DX, 64(SP) - MOVQ R9, 88(SP) - - // nextEmit := 0 - MOVQ DX, R10 - - // s := 1 - ADDQ $1, SI - - // nextHash := hash(load32(src, s), shift) - MOVL 0(SI), R11 - IMULL $0x1e35a7bd, R11 - SHRL CX, R11 - -outer: - // for { etc } - - // skip := 32 - MOVQ $32, R12 - - // nextS := s - MOVQ SI, R13 - - // candidate := 0 - MOVQ $0, R15 - -inner0: - // for { etc } - - // s := nextS - MOVQ R13, SI - - // bytesBetweenHashLookups := skip >> 5 - MOVQ R12, R14 - SHRQ $5, R14 - - // nextS = s + bytesBetweenHashLookups - ADDQ R14, R13 - - // skip += bytesBetweenHashLookups - ADDQ R14, R12 - - // if nextS > sLimit { goto emitRemainder } - MOVQ R13, AX - SUBQ DX, AX - CMPQ AX, R9 - JA emitRemainder - - // candidate = int(table[nextHash]) - // XXX: MOVWQZX table-32768(SP)(R11*2), R15 - // XXX: 4e 0f b7 7c 5c 78 movzwq 0x78(%rsp,%r11,2),%r15 - BYTE $0x4e - BYTE $0x0f - BYTE $0xb7 - BYTE $0x7c - BYTE $0x5c - BYTE $0x78 - - // table[nextHash] = uint16(s) - MOVQ SI, AX - SUBQ DX, AX - - // XXX: MOVW AX, table-32768(SP)(R11*2) - // XXX: 66 42 89 44 5c 78 mov %ax,0x78(%rsp,%r11,2) - BYTE $0x66 - BYTE $0x42 - BYTE $0x89 - BYTE $0x44 - BYTE $0x5c - BYTE $0x78 - - // nextHash = hash(load32(src, nextS), shift) - MOVL 0(R13), R11 - IMULL $0x1e35a7bd, R11 - SHRL CX, R11 - - // if load32(src, s) != load32(src, candidate) { continue } break - MOVL 0(SI), AX - MOVL (DX)(R15*1), BX - CMPL AX, BX - JNE inner0 - -fourByteMatch: - // As per the encode_other.go code: - // - // A 4-byte match has been found. We'll later see etc. - - // !!! Jump to a fast path for short (<= 16 byte) literals. See the comment - // on inputMargin in encode.go. - MOVQ SI, AX - SUBQ R10, AX - CMPQ AX, $16 - JLE emitLiteralFastPath - - // ---------------------------------------- - // Begin inline of the emitLiteral call. - // - // d += emitLiteral(dst[d:], src[nextEmit:s]) - - MOVL AX, BX - SUBL $1, BX - - CMPL BX, $60 - JLT inlineEmitLiteralOneByte - CMPL BX, $256 - JLT inlineEmitLiteralTwoBytes - -inlineEmitLiteralThreeBytes: - MOVB $0xf4, 0(DI) - MOVW BX, 1(DI) - ADDQ $3, DI - JMP inlineEmitLiteralMemmove - -inlineEmitLiteralTwoBytes: - MOVB $0xf0, 0(DI) - MOVB BX, 1(DI) - ADDQ $2, DI - JMP inlineEmitLiteralMemmove - -inlineEmitLiteralOneByte: - SHLB $2, BX - MOVB BX, 0(DI) - ADDQ $1, DI - -inlineEmitLiteralMemmove: - // Spill local variables (registers) onto the stack; call; unspill. - // - // copy(dst[i:], lit) - // - // This means calling runtime·memmove(&dst[i], &lit[0], len(lit)), so we push - // DI, R10 and AX as arguments. - MOVQ DI, 0(SP) - MOVQ R10, 8(SP) - MOVQ AX, 16(SP) - ADDQ AX, DI // Finish the "d +=" part of "d += emitLiteral(etc)". - MOVQ SI, 72(SP) - MOVQ DI, 80(SP) - MOVQ R15, 112(SP) - CALL runtime·memmove(SB) - MOVQ 56(SP), CX - MOVQ 64(SP), DX - MOVQ 72(SP), SI - MOVQ 80(SP), DI - MOVQ 88(SP), R9 - MOVQ 112(SP), R15 - JMP inner1 - -inlineEmitLiteralEnd: - // End inline of the emitLiteral call. - // ---------------------------------------- - -emitLiteralFastPath: - // !!! Emit the 1-byte encoding "uint8(len(lit)-1)<<2". - MOVB AX, BX - SUBB $1, BX - SHLB $2, BX - MOVB BX, (DI) - ADDQ $1, DI - - // !!! Implement the copy from lit to dst as a 16-byte load and store. - // (Encode's documentation says that dst and src must not overlap.) - // - // This always copies 16 bytes, instead of only len(lit) bytes, but that's - // OK. Subsequent iterations will fix up the overrun. - // - // Note that on amd64, it is legal and cheap to issue unaligned 8-byte or - // 16-byte loads and stores. This technique probably wouldn't be as - // effective on architectures that are fussier about alignment. - MOVOU 0(R10), X0 - MOVOU X0, 0(DI) - ADDQ AX, DI - -inner1: - // for { etc } - - // base := s - MOVQ SI, R12 - - // !!! offset := base - candidate - MOVQ R12, R11 - SUBQ R15, R11 - SUBQ DX, R11 - - // ---------------------------------------- - // Begin inline of the extendMatch call. - // - // s = extendMatch(src, candidate+4, s+4) - - // !!! R14 = &src[len(src)] - MOVQ src_len+32(FP), R14 - ADDQ DX, R14 - - // !!! R13 = &src[len(src) - 8] - MOVQ R14, R13 - SUBQ $8, R13 - - // !!! R15 = &src[candidate + 4] - ADDQ $4, R15 - ADDQ DX, R15 - - // !!! s += 4 - ADDQ $4, SI - -inlineExtendMatchCmp8: - // As long as we are 8 or more bytes before the end of src, we can load and - // compare 8 bytes at a time. If those 8 bytes are equal, repeat. - CMPQ SI, R13 - JA inlineExtendMatchCmp1 - MOVQ (R15), AX - MOVQ (SI), BX - CMPQ AX, BX - JNE inlineExtendMatchBSF - ADDQ $8, R15 - ADDQ $8, SI - JMP inlineExtendMatchCmp8 - -inlineExtendMatchBSF: - // If those 8 bytes were not equal, XOR the two 8 byte values, and return - // the index of the first byte that differs. The BSF instruction finds the - // least significant 1 bit, the amd64 architecture is little-endian, and - // the shift by 3 converts a bit index to a byte index. - XORQ AX, BX - BSFQ BX, BX - SHRQ $3, BX - ADDQ BX, SI - JMP inlineExtendMatchEnd - -inlineExtendMatchCmp1: - // In src's tail, compare 1 byte at a time. - CMPQ SI, R14 - JAE inlineExtendMatchEnd - MOVB (R15), AX - MOVB (SI), BX - CMPB AX, BX - JNE inlineExtendMatchEnd - ADDQ $1, R15 - ADDQ $1, SI - JMP inlineExtendMatchCmp1 - -inlineExtendMatchEnd: - // End inline of the extendMatch call. - // ---------------------------------------- - - // ---------------------------------------- - // Begin inline of the emitCopy call. - // - // d += emitCopy(dst[d:], base-candidate, s-base) - - // !!! length := s - base - MOVQ SI, AX - SUBQ R12, AX - -inlineEmitCopyLoop0: - // for length >= 68 { etc } - CMPL AX, $68 - JLT inlineEmitCopyStep1 - - // Emit a length 64 copy, encoded as 3 bytes. - MOVB $0xfe, 0(DI) - MOVW R11, 1(DI) - ADDQ $3, DI - SUBL $64, AX - JMP inlineEmitCopyLoop0 - -inlineEmitCopyStep1: - // if length > 64 { etc } - CMPL AX, $64 - JLE inlineEmitCopyStep2 - - // Emit a length 60 copy, encoded as 3 bytes. - MOVB $0xee, 0(DI) - MOVW R11, 1(DI) - ADDQ $3, DI - SUBL $60, AX - -inlineEmitCopyStep2: - // if length >= 12 || offset >= 2048 { goto inlineEmitCopyStep3 } - CMPL AX, $12 - JGE inlineEmitCopyStep3 - CMPL R11, $2048 - JGE inlineEmitCopyStep3 - - // Emit the remaining copy, encoded as 2 bytes. - MOVB R11, 1(DI) - SHRL $8, R11 - SHLB $5, R11 - SUBB $4, AX - SHLB $2, AX - ORB AX, R11 - ORB $1, R11 - MOVB R11, 0(DI) - ADDQ $2, DI - JMP inlineEmitCopyEnd - -inlineEmitCopyStep3: - // Emit the remaining copy, encoded as 3 bytes. - SUBL $1, AX - SHLB $2, AX - ORB $2, AX - MOVB AX, 0(DI) - MOVW R11, 1(DI) - ADDQ $3, DI - -inlineEmitCopyEnd: - // End inline of the emitCopy call. - // ---------------------------------------- - - // nextEmit = s - MOVQ SI, R10 - - // if s >= sLimit { goto emitRemainder } - MOVQ SI, AX - SUBQ DX, AX - CMPQ AX, R9 - JAE emitRemainder - - // As per the encode_other.go code: - // - // We could immediately etc. - - // x := load64(src, s-1) - MOVQ -1(SI), R14 - - // prevHash := hash(uint32(x>>0), shift) - MOVL R14, R11 - IMULL $0x1e35a7bd, R11 - SHRL CX, R11 - - // table[prevHash] = uint16(s-1) - MOVQ SI, AX - SUBQ DX, AX - SUBQ $1, AX - - // XXX: MOVW AX, table-32768(SP)(R11*2) - // XXX: 66 42 89 44 5c 78 mov %ax,0x78(%rsp,%r11,2) - BYTE $0x66 - BYTE $0x42 - BYTE $0x89 - BYTE $0x44 - BYTE $0x5c - BYTE $0x78 - - // currHash := hash(uint32(x>>8), shift) - SHRQ $8, R14 - MOVL R14, R11 - IMULL $0x1e35a7bd, R11 - SHRL CX, R11 - - // candidate = int(table[currHash]) - // XXX: MOVWQZX table-32768(SP)(R11*2), R15 - // XXX: 4e 0f b7 7c 5c 78 movzwq 0x78(%rsp,%r11,2),%r15 - BYTE $0x4e - BYTE $0x0f - BYTE $0xb7 - BYTE $0x7c - BYTE $0x5c - BYTE $0x78 - - // table[currHash] = uint16(s) - ADDQ $1, AX - - // XXX: MOVW AX, table-32768(SP)(R11*2) - // XXX: 66 42 89 44 5c 78 mov %ax,0x78(%rsp,%r11,2) - BYTE $0x66 - BYTE $0x42 - BYTE $0x89 - BYTE $0x44 - BYTE $0x5c - BYTE $0x78 - - // if uint32(x>>8) == load32(src, candidate) { continue } - MOVL (DX)(R15*1), BX - CMPL R14, BX - JEQ inner1 - - // nextHash = hash(uint32(x>>16), shift) - SHRQ $8, R14 - MOVL R14, R11 - IMULL $0x1e35a7bd, R11 - SHRL CX, R11 - - // s++ - ADDQ $1, SI - - // break out of the inner1 for loop, i.e. continue the outer loop. - JMP outer - -emitRemainder: - // if nextEmit < len(src) { etc } - MOVQ src_len+32(FP), AX - ADDQ DX, AX - CMPQ R10, AX - JEQ encodeBlockEnd - - // d += emitLiteral(dst[d:], src[nextEmit:]) - // - // Push args. - MOVQ DI, 0(SP) - MOVQ $0, 8(SP) // Unnecessary, as the callee ignores it, but conservative. - MOVQ $0, 16(SP) // Unnecessary, as the callee ignores it, but conservative. - MOVQ R10, 24(SP) - SUBQ R10, AX - MOVQ AX, 32(SP) - MOVQ AX, 40(SP) // Unnecessary, as the callee ignores it, but conservative. - - // Spill local variables (registers) onto the stack; call; unspill. - MOVQ DI, 80(SP) - CALL ·emitLiteral(SB) - MOVQ 80(SP), DI - - // Finish the "d +=" part of "d += emitLiteral(etc)". - ADDQ 48(SP), DI - -encodeBlockEnd: - MOVQ dst_base+0(FP), AX - SUBQ AX, DI - MOVQ DI, d+48(FP) - RET diff --git a/vendor/github.com/golang/snappy/encode_arm64.s b/vendor/github.com/golang/snappy/encode_arm64.s deleted file mode 100644 index f8d54adfc..000000000 --- a/vendor/github.com/golang/snappy/encode_arm64.s +++ /dev/null @@ -1,722 +0,0 @@ -// Copyright 2020 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !appengine -// +build gc -// +build !noasm - -#include "textflag.h" - -// The asm code generally follows the pure Go code in encode_other.go, except -// where marked with a "!!!". - -// ---------------------------------------------------------------------------- - -// func emitLiteral(dst, lit []byte) int -// -// All local variables fit into registers. The register allocation: -// - R3 len(lit) -// - R4 n -// - R6 return value -// - R8 &dst[i] -// - R10 &lit[0] -// -// The 32 bytes of stack space is to call runtime·memmove. -// -// The unusual register allocation of local variables, such as R10 for the -// source pointer, matches the allocation used at the call site in encodeBlock, -// which makes it easier to manually inline this function. -TEXT ·emitLiteral(SB), NOSPLIT, $32-56 - MOVD dst_base+0(FP), R8 - MOVD lit_base+24(FP), R10 - MOVD lit_len+32(FP), R3 - MOVD R3, R6 - MOVW R3, R4 - SUBW $1, R4, R4 - - CMPW $60, R4 - BLT oneByte - CMPW $256, R4 - BLT twoBytes - -threeBytes: - MOVD $0xf4, R2 - MOVB R2, 0(R8) - MOVW R4, 1(R8) - ADD $3, R8, R8 - ADD $3, R6, R6 - B memmove - -twoBytes: - MOVD $0xf0, R2 - MOVB R2, 0(R8) - MOVB R4, 1(R8) - ADD $2, R8, R8 - ADD $2, R6, R6 - B memmove - -oneByte: - LSLW $2, R4, R4 - MOVB R4, 0(R8) - ADD $1, R8, R8 - ADD $1, R6, R6 - -memmove: - MOVD R6, ret+48(FP) - - // copy(dst[i:], lit) - // - // This means calling runtime·memmove(&dst[i], &lit[0], len(lit)), so we push - // R8, R10 and R3 as arguments. - MOVD R8, 8(RSP) - MOVD R10, 16(RSP) - MOVD R3, 24(RSP) - CALL runtime·memmove(SB) - RET - -// ---------------------------------------------------------------------------- - -// func emitCopy(dst []byte, offset, length int) int -// -// All local variables fit into registers. The register allocation: -// - R3 length -// - R7 &dst[0] -// - R8 &dst[i] -// - R11 offset -// -// The unusual register allocation of local variables, such as R11 for the -// offset, matches the allocation used at the call site in encodeBlock, which -// makes it easier to manually inline this function. -TEXT ·emitCopy(SB), NOSPLIT, $0-48 - MOVD dst_base+0(FP), R8 - MOVD R8, R7 - MOVD offset+24(FP), R11 - MOVD length+32(FP), R3 - -loop0: - // for length >= 68 { etc } - CMPW $68, R3 - BLT step1 - - // Emit a length 64 copy, encoded as 3 bytes. - MOVD $0xfe, R2 - MOVB R2, 0(R8) - MOVW R11, 1(R8) - ADD $3, R8, R8 - SUB $64, R3, R3 - B loop0 - -step1: - // if length > 64 { etc } - CMP $64, R3 - BLE step2 - - // Emit a length 60 copy, encoded as 3 bytes. - MOVD $0xee, R2 - MOVB R2, 0(R8) - MOVW R11, 1(R8) - ADD $3, R8, R8 - SUB $60, R3, R3 - -step2: - // if length >= 12 || offset >= 2048 { goto step3 } - CMP $12, R3 - BGE step3 - CMPW $2048, R11 - BGE step3 - - // Emit the remaining copy, encoded as 2 bytes. - MOVB R11, 1(R8) - LSRW $3, R11, R11 - AND $0xe0, R11, R11 - SUB $4, R3, R3 - LSLW $2, R3 - AND $0xff, R3, R3 - ORRW R3, R11, R11 - ORRW $1, R11, R11 - MOVB R11, 0(R8) - ADD $2, R8, R8 - - // Return the number of bytes written. - SUB R7, R8, R8 - MOVD R8, ret+40(FP) - RET - -step3: - // Emit the remaining copy, encoded as 3 bytes. - SUB $1, R3, R3 - AND $0xff, R3, R3 - LSLW $2, R3, R3 - ORRW $2, R3, R3 - MOVB R3, 0(R8) - MOVW R11, 1(R8) - ADD $3, R8, R8 - - // Return the number of bytes written. - SUB R7, R8, R8 - MOVD R8, ret+40(FP) - RET - -// ---------------------------------------------------------------------------- - -// func extendMatch(src []byte, i, j int) int -// -// All local variables fit into registers. The register allocation: -// - R6 &src[0] -// - R7 &src[j] -// - R13 &src[len(src) - 8] -// - R14 &src[len(src)] -// - R15 &src[i] -// -// The unusual register allocation of local variables, such as R15 for a source -// pointer, matches the allocation used at the call site in encodeBlock, which -// makes it easier to manually inline this function. -TEXT ·extendMatch(SB), NOSPLIT, $0-48 - MOVD src_base+0(FP), R6 - MOVD src_len+8(FP), R14 - MOVD i+24(FP), R15 - MOVD j+32(FP), R7 - ADD R6, R14, R14 - ADD R6, R15, R15 - ADD R6, R7, R7 - MOVD R14, R13 - SUB $8, R13, R13 - -cmp8: - // As long as we are 8 or more bytes before the end of src, we can load and - // compare 8 bytes at a time. If those 8 bytes are equal, repeat. - CMP R13, R7 - BHI cmp1 - MOVD (R15), R3 - MOVD (R7), R4 - CMP R4, R3 - BNE bsf - ADD $8, R15, R15 - ADD $8, R7, R7 - B cmp8 - -bsf: - // If those 8 bytes were not equal, XOR the two 8 byte values, and return - // the index of the first byte that differs. - // RBIT reverses the bit order, then CLZ counts the leading zeros, the - // combination of which finds the least significant bit which is set. - // The arm64 architecture is little-endian, and the shift by 3 converts - // a bit index to a byte index. - EOR R3, R4, R4 - RBIT R4, R4 - CLZ R4, R4 - ADD R4>>3, R7, R7 - - // Convert from &src[ret] to ret. - SUB R6, R7, R7 - MOVD R7, ret+40(FP) - RET - -cmp1: - // In src's tail, compare 1 byte at a time. - CMP R7, R14 - BLS extendMatchEnd - MOVB (R15), R3 - MOVB (R7), R4 - CMP R4, R3 - BNE extendMatchEnd - ADD $1, R15, R15 - ADD $1, R7, R7 - B cmp1 - -extendMatchEnd: - // Convert from &src[ret] to ret. - SUB R6, R7, R7 - MOVD R7, ret+40(FP) - RET - -// ---------------------------------------------------------------------------- - -// func encodeBlock(dst, src []byte) (d int) -// -// All local variables fit into registers, other than "var table". The register -// allocation: -// - R3 . . -// - R4 . . -// - R5 64 shift -// - R6 72 &src[0], tableSize -// - R7 80 &src[s] -// - R8 88 &dst[d] -// - R9 96 sLimit -// - R10 . &src[nextEmit] -// - R11 104 prevHash, currHash, nextHash, offset -// - R12 112 &src[base], skip -// - R13 . &src[nextS], &src[len(src) - 8] -// - R14 . len(src), bytesBetweenHashLookups, &src[len(src)], x -// - R15 120 candidate -// - R16 . hash constant, 0x1e35a7bd -// - R17 . &table -// - . 128 table -// -// The second column (64, 72, etc) is the stack offset to spill the registers -// when calling other functions. We could pack this slightly tighter, but it's -// simpler to have a dedicated spill map independent of the function called. -// -// "var table [maxTableSize]uint16" takes up 32768 bytes of stack space. An -// extra 64 bytes, to call other functions, and an extra 64 bytes, to spill -// local variables (registers) during calls gives 32768 + 64 + 64 = 32896. -TEXT ·encodeBlock(SB), 0, $32896-56 - MOVD dst_base+0(FP), R8 - MOVD src_base+24(FP), R7 - MOVD src_len+32(FP), R14 - - // shift, tableSize := uint32(32-8), 1<<8 - MOVD $24, R5 - MOVD $256, R6 - MOVW $0xa7bd, R16 - MOVKW $(0x1e35<<16), R16 - -calcShift: - // for ; tableSize < maxTableSize && tableSize < len(src); tableSize *= 2 { - // shift-- - // } - MOVD $16384, R2 - CMP R2, R6 - BGE varTable - CMP R14, R6 - BGE varTable - SUB $1, R5, R5 - LSL $1, R6, R6 - B calcShift - -varTable: - // var table [maxTableSize]uint16 - // - // In the asm code, unlike the Go code, we can zero-initialize only the - // first tableSize elements. Each uint16 element is 2 bytes and each - // iterations writes 64 bytes, so we can do only tableSize/32 writes - // instead of the 2048 writes that would zero-initialize all of table's - // 32768 bytes. This clear could overrun the first tableSize elements, but - // it won't overrun the allocated stack size. - ADD $128, RSP, R17 - MOVD R17, R4 - - // !!! R6 = &src[tableSize] - ADD R6<<1, R17, R6 - -memclr: - STP.P (ZR, ZR), 64(R4) - STP (ZR, ZR), -48(R4) - STP (ZR, ZR), -32(R4) - STP (ZR, ZR), -16(R4) - CMP R4, R6 - BHI memclr - - // !!! R6 = &src[0] - MOVD R7, R6 - - // sLimit := len(src) - inputMargin - MOVD R14, R9 - SUB $15, R9, R9 - - // !!! Pre-emptively spill R5, R6 and R9 to the stack. Their values don't - // change for the rest of the function. - MOVD R5, 64(RSP) - MOVD R6, 72(RSP) - MOVD R9, 96(RSP) - - // nextEmit := 0 - MOVD R6, R10 - - // s := 1 - ADD $1, R7, R7 - - // nextHash := hash(load32(src, s), shift) - MOVW 0(R7), R11 - MULW R16, R11, R11 - LSRW R5, R11, R11 - -outer: - // for { etc } - - // skip := 32 - MOVD $32, R12 - - // nextS := s - MOVD R7, R13 - - // candidate := 0 - MOVD $0, R15 - -inner0: - // for { etc } - - // s := nextS - MOVD R13, R7 - - // bytesBetweenHashLookups := skip >> 5 - MOVD R12, R14 - LSR $5, R14, R14 - - // nextS = s + bytesBetweenHashLookups - ADD R14, R13, R13 - - // skip += bytesBetweenHashLookups - ADD R14, R12, R12 - - // if nextS > sLimit { goto emitRemainder } - MOVD R13, R3 - SUB R6, R3, R3 - CMP R9, R3 - BHI emitRemainder - - // candidate = int(table[nextHash]) - MOVHU 0(R17)(R11<<1), R15 - - // table[nextHash] = uint16(s) - MOVD R7, R3 - SUB R6, R3, R3 - - MOVH R3, 0(R17)(R11<<1) - - // nextHash = hash(load32(src, nextS), shift) - MOVW 0(R13), R11 - MULW R16, R11 - LSRW R5, R11, R11 - - // if load32(src, s) != load32(src, candidate) { continue } break - MOVW 0(R7), R3 - MOVW (R6)(R15), R4 - CMPW R4, R3 - BNE inner0 - -fourByteMatch: - // As per the encode_other.go code: - // - // A 4-byte match has been found. We'll later see etc. - - // !!! Jump to a fast path for short (<= 16 byte) literals. See the comment - // on inputMargin in encode.go. - MOVD R7, R3 - SUB R10, R3, R3 - CMP $16, R3 - BLE emitLiteralFastPath - - // ---------------------------------------- - // Begin inline of the emitLiteral call. - // - // d += emitLiteral(dst[d:], src[nextEmit:s]) - - MOVW R3, R4 - SUBW $1, R4, R4 - - MOVW $60, R2 - CMPW R2, R4 - BLT inlineEmitLiteralOneByte - MOVW $256, R2 - CMPW R2, R4 - BLT inlineEmitLiteralTwoBytes - -inlineEmitLiteralThreeBytes: - MOVD $0xf4, R1 - MOVB R1, 0(R8) - MOVW R4, 1(R8) - ADD $3, R8, R8 - B inlineEmitLiteralMemmove - -inlineEmitLiteralTwoBytes: - MOVD $0xf0, R1 - MOVB R1, 0(R8) - MOVB R4, 1(R8) - ADD $2, R8, R8 - B inlineEmitLiteralMemmove - -inlineEmitLiteralOneByte: - LSLW $2, R4, R4 - MOVB R4, 0(R8) - ADD $1, R8, R8 - -inlineEmitLiteralMemmove: - // Spill local variables (registers) onto the stack; call; unspill. - // - // copy(dst[i:], lit) - // - // This means calling runtime·memmove(&dst[i], &lit[0], len(lit)), so we push - // R8, R10 and R3 as arguments. - MOVD R8, 8(RSP) - MOVD R10, 16(RSP) - MOVD R3, 24(RSP) - - // Finish the "d +=" part of "d += emitLiteral(etc)". - ADD R3, R8, R8 - MOVD R7, 80(RSP) - MOVD R8, 88(RSP) - MOVD R15, 120(RSP) - CALL runtime·memmove(SB) - MOVD 64(RSP), R5 - MOVD 72(RSP), R6 - MOVD 80(RSP), R7 - MOVD 88(RSP), R8 - MOVD 96(RSP), R9 - MOVD 120(RSP), R15 - ADD $128, RSP, R17 - MOVW $0xa7bd, R16 - MOVKW $(0x1e35<<16), R16 - B inner1 - -inlineEmitLiteralEnd: - // End inline of the emitLiteral call. - // ---------------------------------------- - -emitLiteralFastPath: - // !!! Emit the 1-byte encoding "uint8(len(lit)-1)<<2". - MOVB R3, R4 - SUBW $1, R4, R4 - AND $0xff, R4, R4 - LSLW $2, R4, R4 - MOVB R4, (R8) - ADD $1, R8, R8 - - // !!! Implement the copy from lit to dst as a 16-byte load and store. - // (Encode's documentation says that dst and src must not overlap.) - // - // This always copies 16 bytes, instead of only len(lit) bytes, but that's - // OK. Subsequent iterations will fix up the overrun. - // - // Note that on arm64, it is legal and cheap to issue unaligned 8-byte or - // 16-byte loads and stores. This technique probably wouldn't be as - // effective on architectures that are fussier about alignment. - LDP 0(R10), (R0, R1) - STP (R0, R1), 0(R8) - ADD R3, R8, R8 - -inner1: - // for { etc } - - // base := s - MOVD R7, R12 - - // !!! offset := base - candidate - MOVD R12, R11 - SUB R15, R11, R11 - SUB R6, R11, R11 - - // ---------------------------------------- - // Begin inline of the extendMatch call. - // - // s = extendMatch(src, candidate+4, s+4) - - // !!! R14 = &src[len(src)] - MOVD src_len+32(FP), R14 - ADD R6, R14, R14 - - // !!! R13 = &src[len(src) - 8] - MOVD R14, R13 - SUB $8, R13, R13 - - // !!! R15 = &src[candidate + 4] - ADD $4, R15, R15 - ADD R6, R15, R15 - - // !!! s += 4 - ADD $4, R7, R7 - -inlineExtendMatchCmp8: - // As long as we are 8 or more bytes before the end of src, we can load and - // compare 8 bytes at a time. If those 8 bytes are equal, repeat. - CMP R13, R7 - BHI inlineExtendMatchCmp1 - MOVD (R15), R3 - MOVD (R7), R4 - CMP R4, R3 - BNE inlineExtendMatchBSF - ADD $8, R15, R15 - ADD $8, R7, R7 - B inlineExtendMatchCmp8 - -inlineExtendMatchBSF: - // If those 8 bytes were not equal, XOR the two 8 byte values, and return - // the index of the first byte that differs. - // RBIT reverses the bit order, then CLZ counts the leading zeros, the - // combination of which finds the least significant bit which is set. - // The arm64 architecture is little-endian, and the shift by 3 converts - // a bit index to a byte index. - EOR R3, R4, R4 - RBIT R4, R4 - CLZ R4, R4 - ADD R4>>3, R7, R7 - B inlineExtendMatchEnd - -inlineExtendMatchCmp1: - // In src's tail, compare 1 byte at a time. - CMP R7, R14 - BLS inlineExtendMatchEnd - MOVB (R15), R3 - MOVB (R7), R4 - CMP R4, R3 - BNE inlineExtendMatchEnd - ADD $1, R15, R15 - ADD $1, R7, R7 - B inlineExtendMatchCmp1 - -inlineExtendMatchEnd: - // End inline of the extendMatch call. - // ---------------------------------------- - - // ---------------------------------------- - // Begin inline of the emitCopy call. - // - // d += emitCopy(dst[d:], base-candidate, s-base) - - // !!! length := s - base - MOVD R7, R3 - SUB R12, R3, R3 - -inlineEmitCopyLoop0: - // for length >= 68 { etc } - MOVW $68, R2 - CMPW R2, R3 - BLT inlineEmitCopyStep1 - - // Emit a length 64 copy, encoded as 3 bytes. - MOVD $0xfe, R1 - MOVB R1, 0(R8) - MOVW R11, 1(R8) - ADD $3, R8, R8 - SUBW $64, R3, R3 - B inlineEmitCopyLoop0 - -inlineEmitCopyStep1: - // if length > 64 { etc } - MOVW $64, R2 - CMPW R2, R3 - BLE inlineEmitCopyStep2 - - // Emit a length 60 copy, encoded as 3 bytes. - MOVD $0xee, R1 - MOVB R1, 0(R8) - MOVW R11, 1(R8) - ADD $3, R8, R8 - SUBW $60, R3, R3 - -inlineEmitCopyStep2: - // if length >= 12 || offset >= 2048 { goto inlineEmitCopyStep3 } - MOVW $12, R2 - CMPW R2, R3 - BGE inlineEmitCopyStep3 - MOVW $2048, R2 - CMPW R2, R11 - BGE inlineEmitCopyStep3 - - // Emit the remaining copy, encoded as 2 bytes. - MOVB R11, 1(R8) - LSRW $8, R11, R11 - LSLW $5, R11, R11 - SUBW $4, R3, R3 - AND $0xff, R3, R3 - LSLW $2, R3, R3 - ORRW R3, R11, R11 - ORRW $1, R11, R11 - MOVB R11, 0(R8) - ADD $2, R8, R8 - B inlineEmitCopyEnd - -inlineEmitCopyStep3: - // Emit the remaining copy, encoded as 3 bytes. - SUBW $1, R3, R3 - LSLW $2, R3, R3 - ORRW $2, R3, R3 - MOVB R3, 0(R8) - MOVW R11, 1(R8) - ADD $3, R8, R8 - -inlineEmitCopyEnd: - // End inline of the emitCopy call. - // ---------------------------------------- - - // nextEmit = s - MOVD R7, R10 - - // if s >= sLimit { goto emitRemainder } - MOVD R7, R3 - SUB R6, R3, R3 - CMP R3, R9 - BLS emitRemainder - - // As per the encode_other.go code: - // - // We could immediately etc. - - // x := load64(src, s-1) - MOVD -1(R7), R14 - - // prevHash := hash(uint32(x>>0), shift) - MOVW R14, R11 - MULW R16, R11, R11 - LSRW R5, R11, R11 - - // table[prevHash] = uint16(s-1) - MOVD R7, R3 - SUB R6, R3, R3 - SUB $1, R3, R3 - - MOVHU R3, 0(R17)(R11<<1) - - // currHash := hash(uint32(x>>8), shift) - LSR $8, R14, R14 - MOVW R14, R11 - MULW R16, R11, R11 - LSRW R5, R11, R11 - - // candidate = int(table[currHash]) - MOVHU 0(R17)(R11<<1), R15 - - // table[currHash] = uint16(s) - ADD $1, R3, R3 - MOVHU R3, 0(R17)(R11<<1) - - // if uint32(x>>8) == load32(src, candidate) { continue } - MOVW (R6)(R15), R4 - CMPW R4, R14 - BEQ inner1 - - // nextHash = hash(uint32(x>>16), shift) - LSR $8, R14, R14 - MOVW R14, R11 - MULW R16, R11, R11 - LSRW R5, R11, R11 - - // s++ - ADD $1, R7, R7 - - // break out of the inner1 for loop, i.e. continue the outer loop. - B outer - -emitRemainder: - // if nextEmit < len(src) { etc } - MOVD src_len+32(FP), R3 - ADD R6, R3, R3 - CMP R3, R10 - BEQ encodeBlockEnd - - // d += emitLiteral(dst[d:], src[nextEmit:]) - // - // Push args. - MOVD R8, 8(RSP) - MOVD $0, 16(RSP) // Unnecessary, as the callee ignores it, but conservative. - MOVD $0, 24(RSP) // Unnecessary, as the callee ignores it, but conservative. - MOVD R10, 32(RSP) - SUB R10, R3, R3 - MOVD R3, 40(RSP) - MOVD R3, 48(RSP) // Unnecessary, as the callee ignores it, but conservative. - - // Spill local variables (registers) onto the stack; call; unspill. - MOVD R8, 88(RSP) - CALL ·emitLiteral(SB) - MOVD 88(RSP), R8 - - // Finish the "d +=" part of "d += emitLiteral(etc)". - MOVD 56(RSP), R1 - ADD R1, R8, R8 - -encodeBlockEnd: - MOVD dst_base+0(FP), R3 - SUB R3, R8, R8 - MOVD R8, d+48(FP) - RET diff --git a/vendor/github.com/golang/snappy/encode_asm.go b/vendor/github.com/golang/snappy/encode_asm.go deleted file mode 100644 index 107c1e714..000000000 --- a/vendor/github.com/golang/snappy/encode_asm.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2016 The Snappy-Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !appengine -// +build gc -// +build !noasm -// +build amd64 arm64 - -package snappy - -// emitLiteral has the same semantics as in encode_other.go. -// -//go:noescape -func emitLiteral(dst, lit []byte) int - -// emitCopy has the same semantics as in encode_other.go. -// -//go:noescape -func emitCopy(dst []byte, offset, length int) int - -// extendMatch has the same semantics as in encode_other.go. -// -//go:noescape -func extendMatch(src []byte, i, j int) int - -// encodeBlock has the same semantics as in encode_other.go. -// -//go:noescape -func encodeBlock(dst, src []byte) (d int) diff --git a/vendor/github.com/golang/snappy/encode_other.go b/vendor/github.com/golang/snappy/encode_other.go deleted file mode 100644 index 296d7f0be..000000000 --- a/vendor/github.com/golang/snappy/encode_other.go +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright 2016 The Snappy-Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !amd64,!arm64 appengine !gc noasm - -package snappy - -func load32(b []byte, i int) uint32 { - b = b[i : i+4 : len(b)] // Help the compiler eliminate bounds checks on the next line. - return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 -} - -func load64(b []byte, i int) uint64 { - b = b[i : i+8 : len(b)] // Help the compiler eliminate bounds checks on the next line. - return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | - uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 -} - -// emitLiteral writes a literal chunk and returns the number of bytes written. -// -// It assumes that: -// dst is long enough to hold the encoded bytes -// 1 <= len(lit) && len(lit) <= 65536 -func emitLiteral(dst, lit []byte) int { - i, n := 0, uint(len(lit)-1) - switch { - case n < 60: - dst[0] = uint8(n)<<2 | tagLiteral - i = 1 - case n < 1<<8: - dst[0] = 60<<2 | tagLiteral - dst[1] = uint8(n) - i = 2 - default: - dst[0] = 61<<2 | tagLiteral - dst[1] = uint8(n) - dst[2] = uint8(n >> 8) - i = 3 - } - return i + copy(dst[i:], lit) -} - -// emitCopy writes a copy chunk and returns the number of bytes written. -// -// It assumes that: -// dst is long enough to hold the encoded bytes -// 1 <= offset && offset <= 65535 -// 4 <= length && length <= 65535 -func emitCopy(dst []byte, offset, length int) int { - i := 0 - // The maximum length for a single tagCopy1 or tagCopy2 op is 64 bytes. The - // threshold for this loop is a little higher (at 68 = 64 + 4), and the - // length emitted down below is is a little lower (at 60 = 64 - 4), because - // it's shorter to encode a length 67 copy as a length 60 tagCopy2 followed - // by a length 7 tagCopy1 (which encodes as 3+2 bytes) than to encode it as - // a length 64 tagCopy2 followed by a length 3 tagCopy2 (which encodes as - // 3+3 bytes). The magic 4 in the 64±4 is because the minimum length for a - // tagCopy1 op is 4 bytes, which is why a length 3 copy has to be an - // encodes-as-3-bytes tagCopy2 instead of an encodes-as-2-bytes tagCopy1. - for length >= 68 { - // Emit a length 64 copy, encoded as 3 bytes. - dst[i+0] = 63<<2 | tagCopy2 - dst[i+1] = uint8(offset) - dst[i+2] = uint8(offset >> 8) - i += 3 - length -= 64 - } - if length > 64 { - // Emit a length 60 copy, encoded as 3 bytes. - dst[i+0] = 59<<2 | tagCopy2 - dst[i+1] = uint8(offset) - dst[i+2] = uint8(offset >> 8) - i += 3 - length -= 60 - } - if length >= 12 || offset >= 2048 { - // Emit the remaining copy, encoded as 3 bytes. - dst[i+0] = uint8(length-1)<<2 | tagCopy2 - dst[i+1] = uint8(offset) - dst[i+2] = uint8(offset >> 8) - return i + 3 - } - // Emit the remaining copy, encoded as 2 bytes. - dst[i+0] = uint8(offset>>8)<<5 | uint8(length-4)<<2 | tagCopy1 - dst[i+1] = uint8(offset) - return i + 2 -} - -// extendMatch returns the largest k such that k <= len(src) and that -// src[i:i+k-j] and src[j:k] have the same contents. -// -// It assumes that: -// 0 <= i && i < j && j <= len(src) -func extendMatch(src []byte, i, j int) int { - for ; j < len(src) && src[i] == src[j]; i, j = i+1, j+1 { - } - return j -} - -func hash(u, shift uint32) uint32 { - return (u * 0x1e35a7bd) >> shift -} - -// encodeBlock encodes a non-empty src to a guaranteed-large-enough dst. It -// assumes that the varint-encoded length of the decompressed bytes has already -// been written. -// -// It also assumes that: -// len(dst) >= MaxEncodedLen(len(src)) && -// minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize -func encodeBlock(dst, src []byte) (d int) { - // Initialize the hash table. Its size ranges from 1<<8 to 1<<14 inclusive. - // The table element type is uint16, as s < sLimit and sLimit < len(src) - // and len(src) <= maxBlockSize and maxBlockSize == 65536. - const ( - maxTableSize = 1 << 14 - // tableMask is redundant, but helps the compiler eliminate bounds - // checks. - tableMask = maxTableSize - 1 - ) - shift := uint32(32 - 8) - for tableSize := 1 << 8; tableSize < maxTableSize && tableSize < len(src); tableSize *= 2 { - shift-- - } - // In Go, all array elements are zero-initialized, so there is no advantage - // to a smaller tableSize per se. However, it matches the C++ algorithm, - // and in the asm versions of this code, we can get away with zeroing only - // the first tableSize elements. - var table [maxTableSize]uint16 - - // sLimit is when to stop looking for offset/length copies. The inputMargin - // lets us use a fast path for emitLiteral in the main loop, while we are - // looking for copies. - sLimit := len(src) - inputMargin - - // nextEmit is where in src the next emitLiteral should start from. - nextEmit := 0 - - // The encoded form must start with a literal, as there are no previous - // bytes to copy, so we start looking for hash matches at s == 1. - s := 1 - nextHash := hash(load32(src, s), shift) - - for { - // Copied from the C++ snappy implementation: - // - // Heuristic match skipping: If 32 bytes are scanned with no matches - // found, start looking only at every other byte. If 32 more bytes are - // scanned (or skipped), look at every third byte, etc.. When a match - // is found, immediately go back to looking at every byte. This is a - // small loss (~5% performance, ~0.1% density) for compressible data - // due to more bookkeeping, but for non-compressible data (such as - // JPEG) it's a huge win since the compressor quickly "realizes" the - // data is incompressible and doesn't bother looking for matches - // everywhere. - // - // The "skip" variable keeps track of how many bytes there are since - // the last match; dividing it by 32 (ie. right-shifting by five) gives - // the number of bytes to move ahead for each iteration. - skip := 32 - - nextS := s - candidate := 0 - for { - s = nextS - bytesBetweenHashLookups := skip >> 5 - nextS = s + bytesBetweenHashLookups - skip += bytesBetweenHashLookups - if nextS > sLimit { - goto emitRemainder - } - candidate = int(table[nextHash&tableMask]) - table[nextHash&tableMask] = uint16(s) - nextHash = hash(load32(src, nextS), shift) - if load32(src, s) == load32(src, candidate) { - break - } - } - - // A 4-byte match has been found. We'll later see if more than 4 bytes - // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit - // them as literal bytes. - d += emitLiteral(dst[d:], src[nextEmit:s]) - - // Call emitCopy, and then see if another emitCopy could be our next - // move. Repeat until we find no match for the input immediately after - // what was consumed by the last emitCopy call. - // - // If we exit this loop normally then we need to call emitLiteral next, - // though we don't yet know how big the literal will be. We handle that - // by proceeding to the next iteration of the main loop. We also can - // exit this loop via goto if we get close to exhausting the input. - for { - // Invariant: we have a 4-byte match at s, and no need to emit any - // literal bytes prior to s. - base := s - - // Extend the 4-byte match as long as possible. - // - // This is an inlined version of: - // s = extendMatch(src, candidate+4, s+4) - s += 4 - for i := candidate + 4; s < len(src) && src[i] == src[s]; i, s = i+1, s+1 { - } - - d += emitCopy(dst[d:], base-candidate, s-base) - nextEmit = s - if s >= sLimit { - goto emitRemainder - } - - // We could immediately start working at s now, but to improve - // compression we first update the hash table at s-1 and at s. If - // another emitCopy is not our next move, also calculate nextHash - // at s+1. At least on GOARCH=amd64, these three hash calculations - // are faster as one load64 call (with some shifts) instead of - // three load32 calls. - x := load64(src, s-1) - prevHash := hash(uint32(x>>0), shift) - table[prevHash&tableMask] = uint16(s - 1) - currHash := hash(uint32(x>>8), shift) - candidate = int(table[currHash&tableMask]) - table[currHash&tableMask] = uint16(s) - if uint32(x>>8) != load32(src, candidate) { - nextHash = hash(uint32(x>>16), shift) - s++ - break - } - } - } - -emitRemainder: - if nextEmit < len(src) { - d += emitLiteral(dst[d:], src[nextEmit:]) - } - return d -} diff --git a/vendor/github.com/golang/snappy/snappy.go b/vendor/github.com/golang/snappy/snappy.go deleted file mode 100644 index ece692ea4..000000000 --- a/vendor/github.com/golang/snappy/snappy.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2011 The Snappy-Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package snappy implements the Snappy compression format. It aims for very -// high speeds and reasonable compression. -// -// There are actually two Snappy formats: block and stream. They are related, -// but different: trying to decompress block-compressed data as a Snappy stream -// will fail, and vice versa. The block format is the Decode and Encode -// functions and the stream format is the Reader and Writer types. -// -// The block format, the more common case, is used when the complete size (the -// number of bytes) of the original data is known upfront, at the time -// compression starts. The stream format, also known as the framing format, is -// for when that isn't always true. -// -// The canonical, C++ implementation is at https://github.com/google/snappy and -// it only implements the block format. -package snappy // import "github.com/golang/snappy" - -import ( - "hash/crc32" -) - -/* -Each encoded block begins with the varint-encoded length of the decoded data, -followed by a sequence of chunks. Chunks begin and end on byte boundaries. The -first byte of each chunk is broken into its 2 least and 6 most significant bits -called l and m: l ranges in [0, 4) and m ranges in [0, 64). l is the chunk tag. -Zero means a literal tag. All other values mean a copy tag. - -For literal tags: - - If m < 60, the next 1 + m bytes are literal bytes. - - Otherwise, let n be the little-endian unsigned integer denoted by the next - m - 59 bytes. The next 1 + n bytes after that are literal bytes. - -For copy tags, length bytes are copied from offset bytes ago, in the style of -Lempel-Ziv compression algorithms. In particular: - - For l == 1, the offset ranges in [0, 1<<11) and the length in [4, 12). - The length is 4 + the low 3 bits of m. The high 3 bits of m form bits 8-10 - of the offset. The next byte is bits 0-7 of the offset. - - For l == 2, the offset ranges in [0, 1<<16) and the length in [1, 65). - The length is 1 + m. The offset is the little-endian unsigned integer - denoted by the next 2 bytes. - - For l == 3, this tag is a legacy format that is no longer issued by most - encoders. Nonetheless, the offset ranges in [0, 1<<32) and the length in - [1, 65). The length is 1 + m. The offset is the little-endian unsigned - integer denoted by the next 4 bytes. -*/ -const ( - tagLiteral = 0x00 - tagCopy1 = 0x01 - tagCopy2 = 0x02 - tagCopy4 = 0x03 -) - -const ( - checksumSize = 4 - chunkHeaderSize = 4 - magicChunk = "\xff\x06\x00\x00" + magicBody - magicBody = "sNaPpY" - - // maxBlockSize is the maximum size of the input to encodeBlock. It is not - // part of the wire format per se, but some parts of the encoder assume - // that an offset fits into a uint16. - // - // Also, for the framing format (Writer type instead of Encode function), - // https://github.com/google/snappy/blob/master/framing_format.txt says - // that "the uncompressed data in a chunk must be no longer than 65536 - // bytes". - maxBlockSize = 65536 - - // maxEncodedLenOfMaxBlockSize equals MaxEncodedLen(maxBlockSize), but is - // hard coded to be a const instead of a variable, so that obufLen can also - // be a const. Their equivalence is confirmed by - // TestMaxEncodedLenOfMaxBlockSize. - maxEncodedLenOfMaxBlockSize = 76490 - - obufHeaderLen = len(magicChunk) + checksumSize + chunkHeaderSize - obufLen = obufHeaderLen + maxEncodedLenOfMaxBlockSize -) - -const ( - chunkTypeCompressedData = 0x00 - chunkTypeUncompressedData = 0x01 - chunkTypePadding = 0xfe - chunkTypeStreamIdentifier = 0xff -) - -var crcTable = crc32.MakeTable(crc32.Castagnoli) - -// crc implements the checksum specified in section 3 of -// https://github.com/google/snappy/blob/master/framing_format.txt -func crc(b []byte) uint32 { - c := crc32.Update(0, crcTable, b) - return uint32(c>>15|c<<17) + 0xa282ead8 -} diff --git a/vendor/github.com/sirupsen/logrus/.gitignore b/vendor/github.com/sirupsen/logrus/.gitignore deleted file mode 100644 index 1fb13abeb..000000000 --- a/vendor/github.com/sirupsen/logrus/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -logrus -vendor - -.idea/ diff --git a/vendor/github.com/sirupsen/logrus/.golangci.yml b/vendor/github.com/sirupsen/logrus/.golangci.yml deleted file mode 100644 index c9a840e0d..000000000 --- a/vendor/github.com/sirupsen/logrus/.golangci.yml +++ /dev/null @@ -1,39 +0,0 @@ -version: "2" -linters: - enable: - - asasalint - - asciicheck - - bidichk - - contextcheck - - durationcheck - - errchkjson - - errorlint - - exhaustive - - gocheckcompilerdirectives - - gochecksumtype - - gosec - - gosmopolitan - - loggercheck - - makezero - - musttag - - nilerr - - nilnesserr - - noctx - - reassign - - recvcheck - - testifylint - - unparam - exclusions: - presets: - - legacy - - std-error-handling - rules: - # Exclude some linters from running on tests files. - - path: _test\.go - linters: - - gosec - - musttag - - noctx # TODO: enable once we switch to Go 1.24+. - - linters: # TODO: remove once golangci-lint is updated with https://github.com/golangci/golangci-lint/pull/6584 - - gocheckcompilerdirectives - text: 'compiler directive unrecognized: //go:fix' diff --git a/vendor/github.com/sirupsen/logrus/CHANGELOG.md b/vendor/github.com/sirupsen/logrus/CHANGELOG.md deleted file mode 100644 index 683cec908..000000000 --- a/vendor/github.com/sirupsen/logrus/CHANGELOG.md +++ /dev/null @@ -1,429 +0,0 @@ -# Changelog - -All notable changes to this project will be documented in this file. - -## 1.10.2 - -Changed: - - * Update `github.com/stretchr/testify` to v1.12.1, removing the legacy - `gopkg.in/yaml.v3` dependency. - -## 1.10.1 - -Fixes: - - * Fix a regression introduced in v1.10.0 where `TextFormatter` could panic - when formatting nil or panicking `error` and `fmt.Stringer` values. - * Allow function-backed implementations of `error` as field values. - -## 1.10.0 - -Fixes: - - * Fix reentrant logging deadlocks in formatter paths. - * Fix race conditions in formatter and entry handling. - * Fix generic `Log`, `Logf`, `Logln`, and `LogFn` methods unexpectedly - panicking when called with `PanicLevel`. Use the corresponding `Panic` - methods when panic behavior is desired. - * Improve concurrency safety around formatter and hook access. - -Features: - - * Add `slog` hook for forwarding Logrus entries to `log/slog`. - * Add `slog.Handler` for forwarding `log/slog` records to a Logrus logger, - including levels, fields, groups, context, time, and optional caller - reporting. The hook and handler can also be combined to help migrate - between Logrus and `log/slog`. - * Add minimal, composable logging interfaces for each log level. This enables - consumers to depend on narrower interfaces, making it easier to substitute - or adapt logging implementations. - * Allow `Entry.Caller` to be set explicitly and preserve it across derived - entries, enabling custom caller detection without Logrus overwriting - caller information when `ReportCaller` is enabled. - -Changed: - - * Raise minimum supported Go version to 1.23. - * TextFormatter now renders `[]byte` values as raw/quoted strings instead of slice-of-ints. - * TextFormatter now uses distinct dimmed colors for debug and trace output. - * TextFormatter now automatically enables colors on Windows terminals with ANSI support, - matching the behavior on other platforms. - * `Entry.HasCaller` is now deprecated in favor of checking `Entry.Caller` directly. - * Deprecated `MutexWrap`, which was unintentionally exposed as public API. - It remains available as an alias for compatibility but should not be used - directly. - -Performance: - - * Significantly improve TextFormatter performance and reduce allocations. - * Optimize common Entry and Logger hot paths. - * Reduce allocations in caller reporting. - * ~17% lower geomean runtime and ~27% higher formatter throughput overall. - * Common enabled logging paths are ~30–44% faster. - * TextFormatter paths are up to ~40% faster, with allocation counts reduced - by 25–74% across the measured formatter cases. - - -## 1.9.4 - -Fixes: - - * Remove uses of deprecated `ioutil` package - -Features: - - * Add GNU/Hurd support - * Add WASI wasip1 support - -Code quality: - - * Update minimum supported Go version to 1.17 - * Documentation updates - - -## 1.9.3 - -Fixes: - - * Re-apply fix for potential denial of service in logrus.Writer() when logging >64KB single-line payloads without newlines (#1376) - * Fix panic in Writer - - -## 1.9.2 - -Fixes: - - * Revert Writer DoS fix (#1376) due to regression - - -## 1.9.1 - -Fixes: - - * Fix potential denial of service in logrus.Writer() when logging >64KB single-line payloads without newlines (#1376) - - -## 1.9.0 - -Fixes: - - * Multiple concurrency and race condition fixes - * Improve Windows terminal and ANSI handling - -Code quality: - - * Internal cleanups and modernization - - -## 1.8.3 - -Fixes: - - * Fix potential denial of service in logrus.Writer() when logging >64KB single-line payloads without newlines (#1376) - - -## 1.8.2 - -Features: - - * Add support for the logger private buffer pool (#1253) - -Fixes: - - * Fix race condition for SetFormatter and SetReportCaller - * Fix data race in hooks test package - -## 1.8.1 - -Code quality: - - * move magefile in its own subdir/submodule to remove magefile dependency on logrus consumer - * improve timestamp format documentation - -Fixes: - - * fix race condition on logger hooks - - -## 1.8.0 - -Correct versioning number replacing v1.7.1. - -## 1.7.1 - -Beware this release has introduced a new public API and its semver is therefore incorrect. - -Code quality: - - * use go 1.15 in travis - * use magefile as task runner - -Fixes: - - * small fixes about new go 1.13 error formatting system - * Fix for long time race condiction with mutating data hooks - -Features: - - * build support for zos - -## 1.7.0 - -Fixes: - - * the dependency toward a windows terminal library has been removed - -Features: - - * a new buffer pool management API has been added - * a set of `Fn()` functions have been added - -## 1.6.0 - -Fixes: - - * end of line cleanup - * revert the entry concurrency bug fix which leads to deadlock under some circumstances - * update dependency on go-windows-terminal-sequences to fix a crash with go 1.14 - -Features: - - * add an option to the `TextFormatter` to completely disable fields quoting - -## 1.5.0 - -Code quality: - - * add golangci linter run on travis - -Fixes: - - * add mutex for hooks concurrent access on `Entry` data - * caller function field for go1.14 - * fix build issue for gopherjs target - -Feature: - - * add an hooks/writer sub-package whose goal is to split output on different stream depending on the trace level - * add a `DisableHTMLEscape` option in the `JSONFormatter` - * add `ForceQuote` and `PadLevelText` options in the `TextFormatter` - -## 1.4.2 - - * Fixes build break for plan9, nacl, solaris - -## 1.4.1 - -This new release introduces: - - * Enhance TextFormatter to not print caller information when they are empty (#944) - * Remove dependency on golang.org/x/crypto (#932, #943) - -Fixes: - - * Fix Entry.WithContext method to return a copy of the initial entry (#941) - -## 1.4.0 - -This new release introduces: - - * Add `DeferExitHandler`, similar to `RegisterExitHandler` but prepending the handler to the list of handlers (semantically like `defer`) (#848). - * Add `CallerPrettyfier` to `JSONFormatter` and `TextFormatter` (#909, #911) - * Add `Entry.WithContext()` and `Entry.Context`, to set a context on entries to be used e.g. in hooks (#919). - -Fixes: - - * Fix wrong method calls `Logger.Print` and `Logger.Warningln` (#893). - * Update `Entry.Logf` to not do string formatting unless the log level is enabled (#903) - * Fix infinite recursion on unknown `Level.String()` (#907) - * Fix race condition in `getCaller` (#916). - - -## 1.3.0 - -This new release introduces: - - * Log, Logf, Logln functions for Logger and Entry that take a Level - -Fixes: - - * Building prometheus node_exporter on AIX (#840) - * Race condition in TextFormatter (#468) - * Travis CI import path (#868) - * Remove coloured output on Windows (#862) - * Pointer to func as field in JSONFormatter (#870) - * Properly marshal Levels (#873) - -## 1.2.0 - -This new release introduces: - - * A new method `SetReportCaller` in the `Logger` to enable the file, line and calling function from which the trace has been issued - * A new trace level named `Trace` whose level is below `Debug` - * A configurable exit function to be called upon a Fatal trace - * The `Level` object now implements `encoding.TextUnmarshaler` interface - -## 1.1.1 - -This is a bug fix release. - - * fix the build break on Solaris - * don't drop a whole trace in JSONFormatter when a field param is a function pointer which can not be serialized - -## 1.1.0 - -This new release introduces: - - * several fixes: - * a fix for a race condition on entry formatting - * proper cleanup of previously used entries before putting them back in the pool - * the extra new line at the end of message in text formatter has been removed - * a new global public API to check if a level is activated: IsLevelEnabled - * the following methods have been added to the Logger object - * IsLevelEnabled - * SetFormatter - * SetOutput - * ReplaceHooks - * introduction of go module - * an indent configuration for the json formatter - * output colour support for windows - * the field sort function is now configurable for text formatter - * the CLICOLOR and CLICOLOR\_FORCE environment variable support in text formater - -## 1.0.6 - -This new release introduces: - - * a new api WithTime which allows to easily force the time of the log entry - which is mostly useful for logger wrapper - * a fix reverting the immutability of the entry given as parameter to the hooks - a new configuration field of the json formatter in order to put all the fields - in a nested dictionary - * a new SetOutput method in the Logger - * a new configuration of the textformatter to configure the name of the default keys - * a new configuration of the text formatter to disable the level truncation - -## 1.0.5 - -* Fix hooks race (#707) -* Fix panic deadlock (#695) - -## 1.0.4 - -* Fix race when adding hooks (#612) -* Fix terminal check in AppEngine (#635) - -## 1.0.3 - -* Replace example files with testable examples - -## 1.0.2 - -* bug: quote non-string values in text formatter (#583) -* Make (*Logger) SetLevel a public method - -## 1.0.1 - -* bug: fix escaping in text formatter (#575) - -## 1.0.0 - -* Officially changed name to lower-case -* bug: colors on Windows 10 (#541) -* bug: fix race in accessing level (#512) - -## 0.11.5 - -* feature: add writer and writerlevel to entry (#372) - -## 0.11.4 - -* bug: fix undefined variable on solaris (#493) - -## 0.11.3 - -* formatter: configure quoting of empty values (#484) -* formatter: configure quoting character (default is `"`) (#484) -* bug: fix not importing io correctly in non-linux environments (#481) - -## 0.11.2 - -* bug: fix windows terminal detection (#476) - -## 0.11.1 - -* bug: fix tty detection with custom out (#471) - -## 0.11.0 - -* performance: Use bufferpool to allocate (#370) -* terminal: terminal detection for app-engine (#343) -* feature: exit handler (#375) - -## 0.10.0 - -* feature: Add a test hook (#180) -* feature: `ParseLevel` is now case-insensitive (#326) -* feature: `FieldLogger` interface that generalizes `Logger` and `Entry` (#308) -* performance: avoid re-allocations on `WithFields` (#335) - -## 0.9.0 - -* logrus/text_formatter: don't emit empty msg -* logrus/hooks/airbrake: move out of main repository -* logrus/hooks/sentry: move out of main repository -* logrus/hooks/papertrail: move out of main repository -* logrus/hooks/bugsnag: move out of main repository -* logrus/core: run tests with `-race` -* logrus/core: detect TTY based on `stderr` -* logrus/core: support `WithError` on logger -* logrus/core: Solaris support - -## 0.8.7 - -* logrus/core: fix possible race (#216) -* logrus/doc: small typo fixes and doc improvements - - -## 0.8.6 - -* hooks/raven: allow passing an initialized client - -## 0.8.5 - -* logrus/core: revert #208 - -## 0.8.4 - -* formatter/text: fix data race (#218) - -## 0.8.3 - -* logrus/core: fix entry log level (#208) -* logrus/core: improve performance of text formatter by 40% -* logrus/core: expose `LevelHooks` type -* logrus/core: add support for DragonflyBSD and NetBSD -* formatter/text: print structs more verbosely - -## 0.8.2 - -* logrus: fix more Fatal family functions - -## 0.8.1 - -* logrus: fix not exiting on `Fatalf` and `Fatalln` - -## 0.8.0 - -* logrus: defaults to stderr instead of stdout -* hooks/sentry: add special field for `*http.Request` -* formatter/text: ignore Windows for colors - -## 0.7.3 - -* formatter/\*: allow configuration of timestamp layout - -## 0.7.2 - -* formatter/text: Add configuration option for time format (#158) diff --git a/vendor/github.com/sirupsen/logrus/LICENSE b/vendor/github.com/sirupsen/logrus/LICENSE deleted file mode 100644 index f090cb42f..000000000 --- a/vendor/github.com/sirupsen/logrus/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Simon Eskildsen - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/vendor/github.com/sirupsen/logrus/README.md b/vendor/github.com/sirupsen/logrus/README.md deleted file mode 100644 index b2ff7affc..000000000 --- a/vendor/github.com/sirupsen/logrus/README.md +++ /dev/null @@ -1,505 +0,0 @@ -# Logrus :walrus: [![Build Status](https://github.com/sirupsen/logrus/workflows/CI/badge.svg)](https://github.com/sirupsen/logrus/actions?query=workflow%3ACI) [![Go Reference](https://pkg.go.dev/badge/github.com/sirupsen/logrus.svg)](https://pkg.go.dev/github.com/sirupsen/logrus) - -Logrus is a structured logger for Go (golang), completely API compatible with -the standard library logger. - -**Logrus is in maintenance mode.** The project focuses on security, bug fixes, -and performance improvements. New features are not planned, aside from changes -required to provide interoperability with other logging ecosystems (e.g., Go's -[log/slog](https://pkg.go.dev/log/slog)). - -I believe Logrus' biggest contribution is to have played a part in today's -widespread use of structured logging in Golang. There doesn't seem to be a -reason to do a major, breaking iteration into Logrus V2, since the fantastic Go -community has built those independently. Many fantastic alternatives have sprung -up. Logrus would look like those, had it been re-designed with what we know -about structured logging in Go today. Check out, for example, -[Zerolog][zerolog], [Zap][zap], and [Apex][apex]. - -[zerolog]: https://github.com/rs/zerolog -[zap]: https://github.com/uber-go/zap -[apex]: https://github.com/apex/log - -Nicely color-coded in development (when a TTY is attached, otherwise just -plain text): - -![Colored](http://i.imgur.com/PY7qMwd.png) - -With `logrus.SetFormatter(&logrus.JSONFormatter{})`, for easy parsing by logstash -or Splunk: - -```json lines -{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"} -{"level":"warning","msg":"The group's number increased tremendously!","number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"} -{"animal":"walrus","level":"info","msg":"A giant walrus appears!","size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"} -{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.","size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"} -{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,"time":"2014-03-10 19:57:38.562543128 -0400 EDT"} -``` - -With the default `logrus.SetFormatter(&logrus.TextFormatter{})` when a TTY is not -attached, the output is compatible with the -[logfmt](https://pkg.go.dev/github.com/kr/logfmt) format: - -```bash -time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8 -time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10 -time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true -time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4 -time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009 -time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" animal=orca err="It's over 9000!" number=100 omg=true size=9009 -``` - -To ensure this behaviour even if a TTY is attached, set your formatter as follows: - -```go -logrus.SetFormatter(&logrus.TextFormatter{ - DisableColors: true, - FullTimestamp: true, -}) -``` - -#### Logging Method Name - -If you wish to add the calling method as a field, instruct the logger via: - -```go -logrus.SetReportCaller(true) -``` - -This adds the caller as 'method' like so: - -```json -{"animal":"penguin","level":"fatal","method":"github.com/sirupsen/arcticcreatures.migrate","msg":"a penguin swims by","time":"2014-03-10 19:57:38.562543129 -0400 EDT"} -``` - -```bash -time="2015-03-26T01:27:38-04:00" level=fatal method=github.com/sirupsen/arcticcreatures.migrate msg="a penguin swims by" animal=penguin -``` - -Note that this does add measurable overhead - the cost will depend on the version of Go, but is -between 20 and 40% in recent tests with 1.6 and 1.7. You can validate this in your -environment via benchmarks: - -```bash -go test -bench=ReportCaller -``` - -#### Case-sensitivity - -The organization's name was [changed to lower-case][1]. If you are getting import -conflicts due to case sensitivity, please use the lower-case import: -`github.com/sirupsen/logrus`. - -[1]: https://github.com/sirupsen/logrus/issues/570#issuecomment-313933276 - -#### Example - -The simplest way to use Logrus is simply the package-level exported logger: - -```go -package main - -import "github.com/sirupsen/logrus" - -func main() { - logrus.WithFields(logrus.Fields{ - "animal": "walrus", - }).Info("A walrus appears") -} -``` - -Note that it's completely api-compatible with the stdlib logger, so you can -replace your `log` imports everywhere with `log "github.com/sirupsen/logrus"` -and you'll now have the flexibility of Logrus. You can customize it all you -want: - -```go -package main - -import ( - "os" - - log "github.com/sirupsen/logrus" -) - -func init() { - // Log as JSON instead of the default ASCII formatter. - log.SetFormatter(&log.JSONFormatter{}) - - // Output to stdout instead of the default stderr - // Can be any io.Writer, see below for File example - log.SetOutput(os.Stdout) - - // Only log the warning severity or above. - log.SetLevel(log.WarnLevel) -} - -func main() { - log.WithFields(log.Fields{ - "animal": "walrus", - "size": 10, - }).Info("A group of walrus emerges from the ocean") - - log.WithFields(log.Fields{ - "omg": true, - "number": 122, - }).Warn("The group's number increased tremendously!") - - log.WithFields(log.Fields{ - "omg": true, - "number": 100, - }).Fatal("The ice breaks!") - - // A common pattern is to re-use fields between logging statements by re-using - // the logrus.Entry returned from WithFields() - contextLogger := log.WithFields(log.Fields{ - "common": "this is a common field", - "other": "I also should be logged always", - }) - - contextLogger.Info("I'll be logged with common and other field") - contextLogger.Info("Me too") -} -``` - -For more advanced usage such as logging to multiple locations from the same -application, you can also create an instance of the `logrus` Logger: - -```go -package main - -import ( - "os" - - "github.com/sirupsen/logrus" -) - -// Create a new instance of the logger. You can have any number of instances. -var logger = logrus.New() - -func main() { - // The API for setting attributes is a little different than the package level - // exported logger. See Godoc. - logger.Out = os.Stdout - - // You could set this to any `io.Writer` such as a file - // file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666) - // if err == nil { - // logger.Out = file - // } else { - // logger.Info("Failed to log to file, using default stderr") - // } - - logger.WithFields(logrus.Fields{ - "animal": "walrus", - "size": 10, - }).Info("A group of walrus emerges from the ocean") -} -``` - -#### Fields - -Logrus encourages careful, structured logging through logging fields instead of -long, unparseable error messages. For example, instead of: `logrus.Fatalf("Failed -to send event %s to topic %s with key %d")`, you should log the much more -discoverable: - -```go -logrus.WithFields(logrus.Fields{ - "event": event, - "topic": topic, - "key": key, -}).Fatal("Failed to send event") -``` - -We've found this API forces you to think about logging in a way that produces -much more useful logging messages. We've been in countless situations where just -a single added field to a log statement that was already there would've saved us -hours. The `WithFields` call is optional. - -In general, with Logrus using any of the `printf`-family functions should be -seen as a hint you should add a field, however, you can still use the -`printf`-family functions with Logrus. - -#### Default Fields - -Often it's helpful to have fields _always_ attached to log statements in an -application or parts of one. For example, you may want to always log the -`request_id` and `user_ip` in the context of a request. Instead of writing -`logger.WithFields(logrus.Fields{"request_id": request_id, "user_ip": user_ip})` on -every line, you can create a `logrus.Entry` to pass around instead: - -```go -requestLogger := logger.WithFields(logrus.Fields{"request_id": request_id, "user_ip": user_ip}) -requestLogger.Info("something happened on that request") // will log request_id and user_ip -requestLogger.Warn("something not great happened") -``` - -#### Hooks - -You can add hooks for logging levels. For example to send errors to an exception -tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to -multiple places simultaneously, e.g. syslog. - -Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in -`init`: - -```go -package main - -import ( - "log/syslog" - - "github.com/sirupsen/logrus" - airbrake "gopkg.in/gemnasium/logrus-airbrake-hook.v2" - logrus_syslog "github.com/sirupsen/logrus/hooks/syslog" -) - -func init() { - - // Use the Airbrake hook to report errors that have Error severity or above to - // an exception tracker. You can create custom hooks, see the Hooks section. - logrus.AddHook(airbrake.NewHook(123, "xyz", "production")) - - hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "") - if err != nil { - logrus.Error("Unable to connect to local syslog daemon") - } else { - logrus.AddHook(hook) - } -} -``` - -Note: Syslog hooks also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md). - -A list of currently known service hooks can be found in this wiki [page](https://github.com/sirupsen/logrus/wiki/Hooks) - - -#### Level logging - -Logrus has seven logging levels: Trace, Debug, Info, Warning, Error, Fatal and Panic. - -```go -logrus.Trace("Something very low level.") -logrus.Debug("Useful debugging information.") -logrus.Info("Something noteworthy happened!") -logrus.Warn("You should probably take a look at this.") -logrus.Error("Something failed but I'm not quitting.") -// Calls os.Exit(1) after logging -logrus.Fatal("Bye.") -// Calls panic() after logging -logrus.Panic("I'm bailing.") -``` - -You can set the logging level on a `Logger`, then it will only log entries with -that severity or anything above it: - -```go -// Will log anything that is info or above (warn, error, fatal, panic). Default. -logrus.SetLevel(logrus.InfoLevel) -``` - -It may be useful to set `logrus.Level = logrus.DebugLevel` in a debug or verbose -environment if your application has that. - -Note: If you want different log levels for global (`logrus.SetLevel(...)`) and syslog logging, please check the [syslog hook README](hooks/syslog/README.md#different-log-levels-for-local-and-remote-logging). - -#### Entries - -Besides the fields added with `WithField` or `WithFields` some fields are -automatically added to all logging events: - -1. `time`. The timestamp when the entry was created. -2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after - the `AddFields` call. E.g. `Failed to send event.` -3. `level`. The logging level. E.g. `info`. - -#### Environments - -Logrus has no notion of environment. - -If you wish for hooks and formatters to only be used in specific environments, -you should handle that yourself. For example, if your application has a global -variable `Environment`, which is a string representation of the environment you -could do: - -```go -import ( - "github.com/sirupsen/logrus" -) - -func init() { - // do something here to set environment depending on an environment variable - // or command-line flag - if Environment == "production" { - logrus.SetFormatter(&logrus.JSONFormatter{}) - } else { - // The TextFormatter is default, you don't actually have to do this. - logrus.SetFormatter(&logrus.TextFormatter{}) - } -} -``` - -This configuration is how `logrus` was intended to be used, but JSON in -production is mostly only useful if you do log aggregation with tools like -Splunk or Logstash. - -#### Formatters - -The built-in logging formatters are: - -* [`logrus.TextFormatter`](https://pkg.go.dev/github.com/sirupsen/logrus#TextFormatter) - logs the event in colors if the logger output is a TTY, otherwise without colors. - * To force colored output when there is no TTY, set the `ForceColors` - field to `true`. To force no colored output even if there is a TTY set the - `DisableColors` field to `true`. - * On modern Windows terminals with ANSI (Virtual Terminal) support, TextFormatter - automatically enables colored output. - * If your environment does not support ANSI escape sequences, wrap the logger output - using [github.com/mattn/go-colorable](https://github.com/mattn/go-colorable) - and set `ForceColors` (or `CLICOLOR_FORCE=1`) to enable colors through the wrapper. - * When colors are enabled, levels are truncated to 4 characters by default. To disable - truncation set the `DisableLevelTruncation` field to `true`. - * When outputting to a TTY, it's often helpful to visually scan down a column where all the levels are the same width. Setting the `PadLevelText` field to `true` enables this behavior, by adding padding to the level text. -* [`logrus.JSONFormatter`](https://pkg.go.dev/github.com/sirupsen/logrus#JSONFormatter) - logs fields as JSON. - -Third-party logging formatters: - -* [`FluentdFormatter`](https://github.com/joonix/log). Formats entries that can be parsed by Kubernetes and Google Container Engine. -* [`GELF`](https://github.com/fabienm/go-logrus-formatters). Formats entries so they comply to Graylog's [GELF 1.1 specification](http://docs.graylog.org/en/2.4/pages/gelf.html). -* [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events. -* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout. -* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the Power of Zalgo. -* [`nested-logrus-formatter`](https://github.com/antonfisher/nested-logrus-formatter). Converts logrus fields to a nested structure. -* [`powerful-logrus-formatter`](https://github.com/zput/zxcTool). get fileName, log's line number and the latest function's name when print log; Save log to files. -* [`caption-json-formatter`](https://github.com/nolleh/caption_json_formatter). logrus's message json formatter with human-readable caption added. -* [`easy-logrus-formatter`](https://github.com/WeiZhixiong/easy-logrus-formatter). Provide a user-friendly formatter for logrus. -* [`redactrus`](https://github.com/ibreakthecloud/redactrus). Redacts sensitive information like password, apikeys, email, etc. from logs. - -You can define your formatter by implementing the `Formatter` interface, -requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a -`Fields` type (`map[string]any`) with all your fields as well as the -default ones (see Entries section above): - -```go -type MyJSONFormatter struct{} - -logrus.SetFormatter(new(MyJSONFormatter)) - -func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) { - // Note this doesn't include Time, Level and Message which are available on - // the Entry. Consult `godoc` on information about those fields or read the - // source of the official loggers. - serialized, err := json.Marshal(entry.Data) - if err != nil { - return nil, fmt.Errorf("Failed to marshal fields to JSON, %w", err) - } - return append(serialized, '\n'), nil -} -``` - -#### Logger as an `io.Writer` - -Logrus can be transformed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it. - -```go -w := logger.Writer() -defer w.Close() - -srv := http.Server{ - // create a stdlib log.Logger that writes to - // logrus.Logger. - ErrorLog: log.New(w, "", 0), -} -``` - -Each line written to that writer will be printed the usual way, using formatters -and hooks. The level for those entries is `info`. - -This means that we can override the standard library logger easily: - -```go -logger := logrus.New() -logger.Formatter = &logrus.JSONFormatter{} - -// Use logrus for standard log output -// Note that `log` here references stdlib's log -// Not logrus imported under the name `log`. -log.SetOutput(logger.Writer()) -``` - -#### Rotation - -Log rotation is not provided with Logrus. Log rotation should be done by an -external program (like `logrotate(8)`) that can compress and delete old log -entries. It should not be a feature of the application-level logger. - -#### Tools - -| Tool | Description | -| ---- | ----------- | -|[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will be generated with different configs in different environments.| -|[Logrus Viper Helper](https://github.com/heirko/go-contrib/tree/master/logrusHelper)|An Helper around Logrus to wrap with spf13/Viper to load configuration with fangs! And to simplify Logrus configuration use some behavior of [Logrus Mate](https://github.com/gogap/logrus_mate). [sample](https://github.com/heirko/iris-contrib/blob/master/middleware/logrus-logger/example) | - -#### Testing - -Logrus has a built-in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides: - -* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just adds the `test` hook -* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any): - -```go -import( - "testing" - - "github.com/sirupsen/logrus" - "github.com/sirupsen/logrus/hooks/test" - "github.com/stretchr/testify/assert" -) - -func TestSomething(t*testing.T){ - logger, hook := test.NewNullLogger() - logger.Error("Helloerror") - - assert.Equal(t, 1, len(hook.Entries)) - assert.Equal(t, logrus.ErrorLevel, hook.LastEntry().Level) - assert.Equal(t, "Helloerror", hook.LastEntry().Message) - - hook.Reset() - assert.Nil(t, hook.LastEntry()) -} -``` - -#### Fatal handlers - -Logrus can register one or more functions that will be called when any `fatal` -level message is logged. The registered handlers will be executed before -logrus performs an `os.Exit(1)`. This behavior may be helpful if callers need -to gracefully shut down. Unlike a `panic("Something went wrong...")` call which can be intercepted with a deferred `recover` a call to `os.Exit(1)` can not be intercepted. - -```go -// ... -handler := func() { - // gracefully shut down something... -} -logrus.RegisterExitHandler(handler) -// ... -``` - -#### Thread safety - -By default, Logger is protected by a mutex for concurrent writes. The mutex is held when calling hooks and writing logs. -If you are sure such locking is not needed, you can call logger.SetNoLock() to disable the locking. - -Situations when locking is not needed include: - -* You have no hooks registered, or hooks calling is already thread-safe. - -* Writing to logger.Out is already thread-safe, for example: - - 1) logger.Out is protected by locks. - - 2) logger.Out is an os.File handler opened with `O_APPEND` flag, and every write is smaller than 4k. (This allows multi-thread/multi-process writing) - - (Refer to ) diff --git a/vendor/github.com/sirupsen/logrus/alt_exit.go b/vendor/github.com/sirupsen/logrus/alt_exit.go deleted file mode 100644 index 1c35cf81c..000000000 --- a/vendor/github.com/sirupsen/logrus/alt_exit.go +++ /dev/null @@ -1,76 +0,0 @@ -package logrus - -// The following code was sourced and modified from the -// https://github.com/tebeka/atexit package governed by the following license: -// -// Copyright (c) 2012 Miki Tebeka . -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -// the Software, and to permit persons to whom the Software is furnished to do so, -// subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -import ( - "fmt" - "os" -) - -var handlers = []func(){} - -func runHandler(handler func()) { - defer func() { - if err := recover(); err != nil { - fmt.Fprintln(os.Stderr, "Error: Logrus exit handler error:", err) - } - }() - - handler() -} - -func runHandlers() { - for _, handler := range handlers { - runHandler(handler) - } -} - -// Exit runs all the Logrus atexit handlers and then terminates the program using os.Exit(code) -func Exit(code int) { - runHandlers() - os.Exit(code) -} - -// RegisterExitHandler appends a Logrus Exit handler to the list of handlers, -// call logrus.Exit to invoke all handlers. The handlers will also be invoked when -// any Fatal log entry is made. -// -// This method is useful when a caller wishes to use logrus to log a fatal -// message but also needs to gracefully shutdown. An example usecase could be -// closing database connections, or sending an alert that the application is -// closing. -func RegisterExitHandler(handler func()) { - handlers = append(handlers, handler) -} - -// DeferExitHandler prepends a Logrus Exit handler to the list of handlers, -// call logrus.Exit to invoke all handlers. The handlers will also be invoked when -// any Fatal log entry is made. -// -// This method is useful when a caller wishes to use logrus to log a fatal -// message but also needs to gracefully shutdown. An example usecase could be -// closing database connections, or sending an alert that the application is -// closing. -func DeferExitHandler(handler func()) { - handlers = append([]func(){handler}, handlers...) -} diff --git a/vendor/github.com/sirupsen/logrus/appveyor.yml b/vendor/github.com/sirupsen/logrus/appveyor.yml deleted file mode 100644 index e90f09ea6..000000000 --- a/vendor/github.com/sirupsen/logrus/appveyor.yml +++ /dev/null @@ -1,12 +0,0 @@ -# Minimal stub to satisfy AppVeyor CI -version: 1.0.{build} -platform: x64 -shallow_clone: true - -branches: - only: - - master - - main - -build_script: - - echo "No-op build to satisfy AppVeyor CI" diff --git a/vendor/github.com/sirupsen/logrus/buffer_pool.go b/vendor/github.com/sirupsen/logrus/buffer_pool.go deleted file mode 100644 index 6b562d870..000000000 --- a/vendor/github.com/sirupsen/logrus/buffer_pool.go +++ /dev/null @@ -1,37 +0,0 @@ -package logrus - -import ( - "bytes" - "sync" -) - -var bufferPool BufferPool = &defaultPool{ - pool: &sync.Pool{ - New: func() any { - return new(bytes.Buffer) - }, - }, -} - -type BufferPool interface { - Put(*bytes.Buffer) - Get() *bytes.Buffer -} - -type defaultPool struct { - pool *sync.Pool -} - -func (p *defaultPool) Put(buf *bytes.Buffer) { - p.pool.Put(buf) -} - -func (p *defaultPool) Get() *bytes.Buffer { - return p.pool.Get().(*bytes.Buffer) -} - -// SetBufferPool allows to replace the default logrus buffer pool -// to better meet the specific needs of an application. -func SetBufferPool(bp BufferPool) { - bufferPool = bp -} diff --git a/vendor/github.com/sirupsen/logrus/doc.go b/vendor/github.com/sirupsen/logrus/doc.go deleted file mode 100644 index 75186dc2d..000000000 --- a/vendor/github.com/sirupsen/logrus/doc.go +++ /dev/null @@ -1,26 +0,0 @@ -/* -Package logrus is a structured logger for Go, completely API compatible with the standard library logger. - -The simplest way to use Logrus is simply the package-level exported logger: - - package main - - import ( - log "github.com/sirupsen/logrus" - ) - - func main() { - log.WithFields(log.Fields{ - "animal": "walrus", - "number": 1, - "size": 10, - }).Info("A walrus appears") - } - -Output: - - time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10 - -For a full guide visit https://github.com/sirupsen/logrus -*/ -package logrus diff --git a/vendor/github.com/sirupsen/logrus/entry.go b/vendor/github.com/sirupsen/logrus/entry.go deleted file mode 100644 index 82de41f9f..000000000 --- a/vendor/github.com/sirupsen/logrus/entry.go +++ /dev/null @@ -1,567 +0,0 @@ -package logrus - -import ( - "bytes" - "context" - "fmt" - "maps" - "os" - "reflect" - "runtime" - "strconv" - "strings" - "sync" - "time" -) - -var ( - - // qualified package name, cached at first use - logrusPackage string - - // Positions in the call stack when tracing to report the calling method. - // - // Start at the bottom of the stack before the package-name cache is primed. - minimumCallerDepth = 1 - - // Used for caller information initialisation - callerInitOnce sync.Once -) - -const ( - maximumCallerDepth int = 25 - knownLogrusFrames int = 4 -) - -// ErrorKey defines the key when adding errors using [WithError], [Logger.WithError]. -var ErrorKey = "error" - -// Entry represents a single log event. It may be either an intermediate -// entry (created via WithField(s), WithContext, etc.) or a final entry -// that is emitted when one of the level methods (Trace, Debug, Info, -// Warn, Error, Fatal, Panic) is called. -// -// An Entry always belongs to a Logger. A nil Logger is invalid and will -// cause a panic when the entry is logged. Use [NewEntry] or Logger methods -// to construct entries. -// -// Entries are safe to reuse for adding fields and may be passed around -// to avoid field duplication. Each log operation operates on a copy -// of the Entry’s data to avoid mutation during formatting. -// -//nolint:recvcheck // Entry methods intentionally use both pointer and value receivers. -type Entry struct { - // Logger is the Logger that owns this entry and is responsible for - // formatting, hooks, and output. It must not be nil. An Entry without - // a Logger is invalid and will panic when logged. - Logger *Logger - - // Data contains all user-defined fields attached to this entry. - Data Fields - - // Time is the timestamp for the log event. If zero when the entry is - // logged, it defaults to the current time. - Time time.Time - - // Level is the severity of the log entry. It is set when the entry - // is fired and reflects the level used for that log call. - Level Level - - // Caller contains the calling method information. - // - // When [Logger.ReportCaller] is enabled, Caller is populated automatically at - // log time if it is nil. Hooks and formatters may inspect Caller. - // - // Applications generally should not modify Caller unless they intentionally - // want to provide custom caller information. - Caller *runtime.Frame - - // Message is the log message supplied to one of the logging methods - // (Trace, Debug, Info, Warn, Error, Fatal, or Panic). It is set when - // the entry is logged. - Message string - - // Buffer is a reusable buffer provided to the formatter. It is set - // before formatting in the normal log path; when nil, formatters - // allocate their own. - Buffer *bytes.Buffer - - // Context carries user-provided context for hooks and formatters. - Context context.Context - - // err contains internal field-formatting errors. - err string -} - -// NewEntry creates a new [Entry] associated with the provided Logger. -// The logger must not be nil. Passing a nil logger results in a -// panic when a logging method (e.g., [Entry.Info], [Entry.Error], etc.) -// is called. -func NewEntry(logger *Logger) *Entry { - return &Entry{ - Logger: logger, - // Reserve default predefined fields and a little extra room. - Data: make(Fields, defaultFields+3), - } -} - -// Dup creates a copy of the entry for further modification. -// -// Data is cloned to avoid mutating the original entry. Other fields -// (Logger, Time, Context, etc.) are copied by value. -func (entry *Entry) Dup() *Entry { - dup := entry.dup() - dup.Data = maps.Clone(entry.Data) - return dup -} - -// dup copies the entry fields shared by derived entries except Data, which -// callers must copy or initialize as appropriate for their use. -func (entry *Entry) dup() *Entry { - return &Entry{ - Logger: entry.Logger, - Time: entry.Time, - Caller: entry.Caller, - Context: entry.Context, - err: entry.err, - } -} - -// Bytes returns the bytes representation of this entry from the formatter. -func (entry *Entry) Bytes() ([]byte, error) { - // Snapshot the formatter under the lock to protect against concurrent - // SetFormatter calls, then release the lock before formatting. - // This avoids a data race and prevents a deadlock if Format() triggers - // reentrant logging (e.g., a field's MarshalJSON calls logrus). - // - // See: - // - // - https://github.com/sirupsen/logrus/issues/1440 - // - https://github.com/sirupsen/logrus/issues/1448 - entry.Logger.mu.Lock() - formatter := entry.Logger.Formatter - entry.Logger.mu.Unlock() - - return formatter.Format(entry) -} - -// String returns the string representation from the reader and ultimately the -// formatter. -func (entry *Entry) String() (string, error) { - serialized, err := entry.Bytes() - if err != nil { - return "", err - } - str := string(serialized) - return str, nil -} - -// WithError adds an error as single field (using the key defined in [ErrorKey]) -// to the Entry. -func (entry *Entry) WithError(err error) *Entry { - return entry.WithField(ErrorKey, err) -} - -// WithContext adds a context to the Entry. -func (entry *Entry) WithContext(ctx context.Context) *Entry { - dup := entry.dup() - dup.Data = maps.Clone(entry.Data) - dup.Context = ctx - return dup -} - -// WithField adds a single field to the Entry. -func (entry *Entry) WithField(key string, value any) *Entry { - dup := entry.dup() - dup.Data = maps.Clone(entry.Data) - dup.addField(key, value) - return dup -} - -// WithFields adds a map of fields to the Entry. -func (entry *Entry) WithFields(fields Fields) *Entry { - dup := entry.dup() - dup.Data = make(Fields, len(entry.Data)+len(fields)) - maps.Copy(dup.Data, entry.Data) - - for key, value := range fields { - dup.addField(key, value) - } - return dup -} - -// WithTime overrides the time of the Entry. -func (entry *Entry) WithTime(t time.Time) *Entry { - dup := entry.dup() - dup.Data = maps.Clone(entry.Data) - dup.Time = t - return dup -} - -func (entry *Entry) addField(key string, value any) { - if _, ok := value.(error); !ok { - t := reflect.TypeOf(value) - if t != nil && (t.Kind() == reflect.Func || t.Kind() == reflect.Pointer && t.Elem().Kind() == reflect.Func) { - if entry.err != "" { - entry.err += ", skipping unsupported field " + strconv.Quote(key) - } else { - entry.err = "skipping unsupported field " + strconv.Quote(key) - } - return - } - } - - if entry.Data == nil { - entry.Data = make(Fields, 1) - } - entry.Data[key] = value -} - -// getPackageName reduces a fully qualified function name to the package name -// There really ought to be a better way... -func getPackageName(f string) string { - for { - lastPeriod := strings.LastIndex(f, ".") - lastSlash := strings.LastIndex(f, "/") - if lastPeriod > lastSlash { - f = f[:lastPeriod] - } else { - break - } - } - - return f -} - -// getCaller retrieves the name of the first non-logrus calling function -func getCaller() *runtime.Frame { - // cache this package's fully-qualified name - callerInitOnce.Do(func() { - pcs := make([]uintptr, maximumCallerDepth) - _ = runtime.Callers(0, pcs) - - // dynamic get the package name and the minimum caller depth - for i := range maximumCallerDepth { - funcName := runtime.FuncForPC(pcs[i]).Name() - if strings.Contains(funcName, "getCaller") { - logrusPackage = getPackageName(funcName) - break - } - } - - minimumCallerDepth = knownLogrusFrames - }) - - // Restrict the lookback frames to avoid runaway lookups - pcs := make([]uintptr, maximumCallerDepth) - depth := runtime.Callers(minimumCallerDepth, pcs) - frames := runtime.CallersFrames(pcs[:depth]) - - for f, again := frames.Next(); again; f, again = frames.Next() { - pkg := getPackageName(f.Function) - - // If the caller isn't part of this package, we're done - if pkg != logrusPackage { - return &f - } - } - - // if we got here, we failed to find the caller's context - return nil -} - -// HasCaller reports whether this Entry contains caller information. -// -// Caller may be set explicitly, or populated at log time when -// [Logger.ReportCaller] is enabled. -// -// Deprecated: use [Entry.Caller] != nil instead. -// -//go:fix inline -func (entry Entry) HasCaller() bool { - return entry.Caller != nil -} - -func (entry *Entry) logArgs(level Level, panicAfter bool, args ...any) { - entry.log(level, panicAfter, sprint(args...)) -} - -func (entry *Entry) logf(level Level, panicAfter bool, format string, args ...any) { - entry.log(level, panicAfter, fmt.Sprintf(format, args...)) -} - -// logln uses Sprintln for multiple arguments to preserve Println-style -// spacing between args, then trims the trailing newline. -func (entry *Entry) logln(level Level, panicAfter bool, args ...any) { - if len(args) <= 1 { - entry.log(level, panicAfter, sprint(args...)) - return - } - msg := fmt.Sprintln(args...) - msg = msg[:len(msg)-1] // Trim the newline added by Sprintln; logging adds its own. - entry.log(level, panicAfter, msg) -} - -// log writes msg at level. If panicAfter is true, it panics with the fully -// populated entry after hooks and output have completed. -// -// The explicit flag keeps panic behavior limited to Panic, Panicf, and -// Panicln while avoiding a return value used only as the panic value. -// See #1283 and commits f96066e and 5f8c666. -func (entry *Entry) log(level Level, panicAfter bool, msg string) { - newEntry := entry.dup() - newEntry.Data = maps.Clone(entry.Data) - - if newEntry.Time.IsZero() { - newEntry.Time = time.Now() - } - - newEntry.Level = level - newEntry.Message = msg - - logger := newEntry.Logger - logger.mu.Lock() - reportCaller := logger.ReportCaller - bufPool := newEntry.getBufferPool() - logger.mu.Unlock() - - // Preserve explicitly set caller information. - if reportCaller && newEntry.Caller == nil { - newEntry.Caller = getCaller() - } - - // Select hooks based on the level for this log call. Hooks receive the - // Entry and may mutate it, but that does not affect which hooks are - // fired for this event. - hooks := logger.hooksForLevel(level) - newEntry.fireHooks(hooks) - - buffer := bufPool.Get() - defer func() { - newEntry.Buffer = nil - buffer.Reset() - bufPool.Put(buffer) - }() - buffer.Reset() - newEntry.Buffer = buffer - newEntry.write() - newEntry.Buffer = nil - - // Panic here so the panic value contains the fully populated entry without - // requiring log to return it to the caller. - if panicAfter { - panic(newEntry) - } -} - -func (entry *Entry) getBufferPool() (pool BufferPool) { - if entry.Logger.BufferPool != nil { - return entry.Logger.BufferPool - } - return bufferPool -} - -func (entry *Entry) fireHooks(hooks []Hook) { - for _, hook := range hooks { - if err := hook.Fire(entry); err != nil { - _, _ = fmt.Fprintln(os.Stderr, "Failed to fire hook:", err) - return - } - } -} - -func (entry *Entry) write() { - // Snapshot the formatter under the lock to protect against concurrent - // SetFormatter calls, then release the lock before formatting. - // This avoids a deadlock when Format() triggers reentrant logging (e.g., - // a field's MarshalJSON calls logrus). See #1448, #1440. - entry.Logger.mu.Lock() - formatter := entry.Logger.Formatter - entry.Logger.mu.Unlock() - - serialized, err := formatter.Format(entry) - if err != nil { - _, _ = fmt.Fprintln(os.Stderr, "Failed to format entry:", err) - return - } - - // Re-acquire the lock to serialize writes to the underlying io.Writer. - entry.Logger.mu.Lock() - defer entry.Logger.mu.Unlock() - if _, err := entry.Logger.Out.Write(serialized); err != nil { - _, _ = fmt.Fprintln(os.Stderr, "Failed to write to log:", err) - } -} - -// Log logs a message at the specified level. -// -// Using Log with [PanicLevel] or [FatalLevel] intentionally does not -// trigger a panic or exit. Log treats the level as logging severity only; -// use [Entry.Panic] or [Entry.Fatal] when those side effects are desired. -func (entry *Entry) Log(level Level, args ...any) { - const panicAfter = false - if entry.Logger.IsLevelEnabled(level) { - entry.logArgs(level, panicAfter, args...) - } -} - -func (entry *Entry) Trace(args ...any) { - entry.Log(TraceLevel, args...) -} - -func (entry *Entry) Debug(args ...any) { - entry.Log(DebugLevel, args...) -} - -func (entry *Entry) Print(args ...any) { - entry.Info(args...) -} - -func (entry *Entry) Info(args ...any) { - entry.Log(InfoLevel, args...) -} - -func (entry *Entry) Warn(args ...any) { - entry.Log(WarnLevel, args...) -} - -func (entry *Entry) Warning(args ...any) { - entry.Warn(args...) -} - -func (entry *Entry) Error(args ...any) { - entry.Log(ErrorLevel, args...) -} - -func (entry *Entry) Fatal(args ...any) { - entry.Log(FatalLevel, args...) - entry.Logger.Exit(1) -} - -func (entry *Entry) Panic(args ...any) { - const panicAfter = true - if entry.Logger.IsLevelEnabled(PanicLevel) { - entry.logArgs(PanicLevel, panicAfter, args...) - } -} - -// Entry Printf family functions - -// Logf logs a formatted message at the specified level. -// -// Using Logf with [PanicLevel] or [FatalLevel] intentionally does not -// trigger a panic or exit. Logf treats the level as logging severity only; -// use [Entry.Panicf] or [Entry.Fatalf] when those side effects are desired. -func (entry *Entry) Logf(level Level, format string, args ...any) { - const panicAfter = false - if entry.Logger.IsLevelEnabled(level) { - entry.logf(level, panicAfter, format, args...) - } -} - -func (entry *Entry) Tracef(format string, args ...any) { - entry.Logf(TraceLevel, format, args...) -} - -func (entry *Entry) Debugf(format string, args ...any) { - entry.Logf(DebugLevel, format, args...) -} - -func (entry *Entry) Infof(format string, args ...any) { - entry.Logf(InfoLevel, format, args...) -} - -func (entry *Entry) Printf(format string, args ...any) { - entry.Infof(format, args...) -} - -func (entry *Entry) Warnf(format string, args ...any) { - entry.Logf(WarnLevel, format, args...) -} - -func (entry *Entry) Warningf(format string, args ...any) { - entry.Warnf(format, args...) -} - -func (entry *Entry) Errorf(format string, args ...any) { - entry.Logf(ErrorLevel, format, args...) -} - -func (entry *Entry) Fatalf(format string, args ...any) { - entry.Logf(FatalLevel, format, args...) - entry.Logger.Exit(1) -} - -func (entry *Entry) Panicf(format string, args ...any) { - const panicAfter = true - if entry.Logger.IsLevelEnabled(PanicLevel) { - entry.logf(PanicLevel, panicAfter, format, args...) - } -} - -// Entry Println family functions - -// Logln logs a message at the specified level with Println-style spacing. -// -// Using Logln with [PanicLevel] or [FatalLevel] intentionally does not -// trigger a panic or exit. Logln treats the level as logging severity only; -// use [Entry.Panicln] or [Entry.Fatalln] when those side effects are desired. -func (entry *Entry) Logln(level Level, args ...any) { - const panicAfter = false - if entry.Logger.IsLevelEnabled(level) { - entry.logln(level, panicAfter, args...) - } -} - -func (entry *Entry) Traceln(args ...any) { - entry.Logln(TraceLevel, args...) -} - -func (entry *Entry) Debugln(args ...any) { - entry.Logln(DebugLevel, args...) -} - -func (entry *Entry) Infoln(args ...any) { - entry.Logln(InfoLevel, args...) -} - -func (entry *Entry) Println(args ...any) { - entry.Infoln(args...) -} - -func (entry *Entry) Warnln(args ...any) { - entry.Logln(WarnLevel, args...) -} - -func (entry *Entry) Warningln(args ...any) { - entry.Warnln(args...) -} - -func (entry *Entry) Errorln(args ...any) { - entry.Logln(ErrorLevel, args...) -} - -func (entry *Entry) Fatalln(args ...any) { - entry.Logln(FatalLevel, args...) - entry.Logger.Exit(1) -} - -func (entry *Entry) Panicln(args ...any) { - const panicAfter = true - if entry.Logger.IsLevelEnabled(PanicLevel) { - entry.logln(PanicLevel, panicAfter, args...) - } -} - -// sprint is fmt.Sprint with fast paths for zero or one string argument. -func sprint(args ...any) string { - switch len(args) { - case 0: - return "" - case 1: - if msg, ok := args[0].(string); ok { - return msg - } - } - return fmt.Sprint(args...) -} diff --git a/vendor/github.com/sirupsen/logrus/exported.go b/vendor/github.com/sirupsen/logrus/exported.go deleted file mode 100644 index 8b261c124..000000000 --- a/vendor/github.com/sirupsen/logrus/exported.go +++ /dev/null @@ -1,265 +0,0 @@ -package logrus - -import ( - "context" - "io" - "time" -) - -// std is the package-level standard logger, similar to the default logger -// in the stdlib [log] package. -var std = New() - -// StandardLogger returns the package-level standard logger used by -// the top-level logging functions. -func StandardLogger() *Logger { - return std -} - -// SetOutput sets the standard logger output. -func SetOutput(out io.Writer) { - std.SetOutput(out) -} - -// SetFormatter sets the standard logger formatter. -func SetFormatter(formatter Formatter) { - std.SetFormatter(formatter) -} - -// SetReportCaller sets whether the standard logger will include the calling -// method as a field. -func SetReportCaller(include bool) { - std.SetReportCaller(include) -} - -// SetLevel sets the standard logger level. -func SetLevel(level Level) { - std.SetLevel(level) -} - -// GetLevel returns the standard logger level. -func GetLevel() Level { - return std.GetLevel() -} - -// IsLevelEnabled checks if logging for the given level is enabled for the standard logger. -func IsLevelEnabled(level Level) bool { - return std.IsLevelEnabled(level) -} - -// AddHook adds a hook to the standard logger hooks. -func AddHook(hook Hook) { - std.AddHook(hook) -} - -// WithError creates an entry from the standard logger and adds an error to it, -// using the value defined in [ErrorKey] as key. -func WithError(err error) *Entry { - return std.WithError(err) -} - -// WithContext creates an entry from the standard logger and adds a context to it. -func WithContext(ctx context.Context) *Entry { - return std.WithContext(ctx) -} - -// WithField creates an entry from the standard logger and adds a single field. -// For multiple fields, prefer [WithFields] over chaining WithField calls. -func WithField(key string, value any) *Entry { - return std.WithField(key, value) -} - -// WithFields creates an entry from the standard logger and adds the fields to it. -func WithFields(fields Fields) *Entry { - return std.WithFields(fields) -} - -// WithTime creates an entry from the standard logger and overrides the time -// used for logs generated with it. -func WithTime(t time.Time) *Entry { - return std.WithTime(t) -} - -// Trace logs a message at level [TraceLevel] on the standard logger. -func Trace(args ...any) { - std.Trace(args...) -} - -// Debug logs a message at level [DebugLevel] on the standard logger. -func Debug(args ...any) { - std.Debug(args...) -} - -// Print logs a message at level [InfoLevel] on the standard logger. -func Print(args ...any) { - std.Print(args...) -} - -// Info logs a message at level [InfoLevel] on the standard logger. -func Info(args ...any) { - std.Info(args...) -} - -// Warn logs a message at level [WarnLevel] on the standard logger. -func Warn(args ...any) { - std.Warn(args...) -} - -// Warning logs a message at level [WarnLevel] on the standard logger. -func Warning(args ...any) { - std.Warning(args...) -} - -// Error logs a message at level [ErrorLevel] on the standard logger. -func Error(args ...any) { - std.Error(args...) -} - -// Panic logs a message at level [PanicLevel] on the standard logger. -func Panic(args ...any) { - std.Panic(args...) -} - -// Fatal logs a message at level [FatalLevel] on the standard logger, -// then exits the process with status 1. -func Fatal(args ...any) { - std.Fatal(args...) -} - -// TraceFn logs a message from a func at level [TraceLevel] on the standard logger. -func TraceFn(fn LogFunction) { - std.TraceFn(fn) -} - -// DebugFn logs a message from a func at level [DebugLevel] on the standard logger. -func DebugFn(fn LogFunction) { - std.DebugFn(fn) -} - -// PrintFn logs a message from a func at level [InfoLevel] on the standard logger. -func PrintFn(fn LogFunction) { - std.PrintFn(fn) -} - -// InfoFn logs a message from a func at level [InfoLevel] on the standard logger. -func InfoFn(fn LogFunction) { - std.InfoFn(fn) -} - -// WarnFn logs a message from a func at level [WarnLevel] on the standard logger. -func WarnFn(fn LogFunction) { - std.WarnFn(fn) -} - -// WarningFn logs a message from a func at level [WarnLevel] on the standard logger. -func WarningFn(fn LogFunction) { - std.WarningFn(fn) -} - -// ErrorFn logs a message from a func at level [ErrorLevel] on the standard logger. -func ErrorFn(fn LogFunction) { - std.ErrorFn(fn) -} - -// PanicFn logs a message from a func at level [PanicLevel] on the standard logger. -func PanicFn(fn LogFunction) { - std.PanicFn(fn) -} - -// FatalFn logs a message from a func at level [FatalLevel] on the standard logger, -// then exits the process with status 1. -func FatalFn(fn LogFunction) { - std.FatalFn(fn) -} - -// Tracef logs a message at level [TraceLevel] on the standard logger. -func Tracef(format string, args ...any) { - std.Tracef(format, args...) -} - -// Debugf logs a message at level [DebugLevel] on the standard logger. -func Debugf(format string, args ...any) { - std.Debugf(format, args...) -} - -// Printf logs a message at level [InfoLevel] on the standard logger. -func Printf(format string, args ...any) { - std.Printf(format, args...) -} - -// Infof logs a message at level [InfoLevel] on the standard logger. -func Infof(format string, args ...any) { - std.Infof(format, args...) -} - -// Warnf logs a message at level [WarnLevel] on the standard logger. -func Warnf(format string, args ...any) { - std.Warnf(format, args...) -} - -// Warningf logs a message at level [WarnLevel] on the standard logger. -func Warningf(format string, args ...any) { - std.Warningf(format, args...) -} - -// Errorf logs a message at level [ErrorLevel] on the standard logger. -func Errorf(format string, args ...any) { - std.Errorf(format, args...) -} - -// Panicf logs a message at level [PanicLevel] on the standard logger. -func Panicf(format string, args ...any) { - std.Panicf(format, args...) -} - -// Fatalf logs a message at level [FatalLevel] on the standard logger, -// then exits the process with status 1. -func Fatalf(format string, args ...any) { - std.Fatalf(format, args...) -} - -// Traceln logs a message at level [TraceLevel] on the standard logger. -func Traceln(args ...any) { - std.Traceln(args...) -} - -// Debugln logs a message at level [DebugLevel] on the standard logger. -func Debugln(args ...any) { - std.Debugln(args...) -} - -// Println logs a message at level [InfoLevel] on the standard logger. -func Println(args ...any) { - std.Println(args...) -} - -// Infoln logs a message at level [InfoLevel] on the standard logger. -func Infoln(args ...any) { - std.Infoln(args...) -} - -// Warnln logs a message at level [WarnLevel] on the standard logger. -func Warnln(args ...any) { - std.Warnln(args...) -} - -// Warningln logs a message at level [WarnLevel] on the standard logger. -func Warningln(args ...any) { - std.Warningln(args...) -} - -// Errorln logs a message at level [ErrorLevel] on the standard logger. -func Errorln(args ...any) { - std.Errorln(args...) -} - -// Panicln logs a message at level [PanicLevel] on the standard logger. -func Panicln(args ...any) { - std.Panicln(args...) -} - -// Fatalln logs a message at level [FatalLevel] on the standard logger, -// then exits the process with status 1. -func Fatalln(args ...any) { - std.Fatalln(args...) -} diff --git a/vendor/github.com/sirupsen/logrus/formatter.go b/vendor/github.com/sirupsen/logrus/formatter.go deleted file mode 100644 index 16f2e0e0f..000000000 --- a/vendor/github.com/sirupsen/logrus/formatter.go +++ /dev/null @@ -1,91 +0,0 @@ -package logrus - -import "time" - -const ( - // defaultTimestampFormat is the layout used to format entry timestamps - // when a formatter has not specified a custom TimestampFormat. - // It follows time.RFC3339 and is applied unless timestamps are disabled. - defaultTimestampFormat = time.RFC3339 - - // defaultFields is the number of commonly included predefined log entry fields - // (msg, level, time). It is used as a capacity hint when constructing - // intermediate collections during formatting (for example, the fixed key list). - // - // It does not include the optional "logrus_error", "func", or "file" fields. - defaultFields = 3 -) - -// Default key names for the default fields -const ( - FieldKeyMsg = "msg" - FieldKeyLevel = "level" - FieldKeyTime = "time" - FieldKeyLogrusError = "logrus_error" - FieldKeyFunc = "func" - FieldKeyFile = "file" -) - -// Formatter is implemented by types that format log entries. It receives an -// [*Entry], which contains: -// -// - entry.Message: the message passed to logging methods such as [Info], [Warn], [Error] -// - entry.Time: the timestamp -// - entry.Level: the log level -// -// Additional fields added with [WithField] or [WithFields] are available in -// [Entry.Data]. Format should return the formatted log entry as a byte slice, -// which is written to [Logger.Out]. -type Formatter interface { - Format(*Entry) ([]byte, error) -} - -// This is to not silently overwrite `time`, `msg`, `func` and `level` fields when -// dumping it. If this code wasn't there doing: -// -// logrus.WithField("level", 1).Info("hello") -// -// Would just silently drop the user provided level. Instead with this code -// it'll logged as: -// -// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."} -// -// It's not exported because it's still using Data in an opinionated way. It's to -// avoid code duplication between the two default formatters. -func prefixFieldClashes(data Fields, fieldMap FieldMap, reportCaller bool) { - timeKey := fieldMap.resolve(FieldKeyTime) - if t, ok := data[timeKey]; ok { - data["fields."+timeKey] = t - delete(data, timeKey) - } - - msgKey := fieldMap.resolve(FieldKeyMsg) - if m, ok := data[msgKey]; ok { - data["fields."+msgKey] = m - delete(data, msgKey) - } - - levelKey := fieldMap.resolve(FieldKeyLevel) - if l, ok := data[levelKey]; ok { - data["fields."+levelKey] = l - delete(data, levelKey) - } - - logrusErrKey := fieldMap.resolve(FieldKeyLogrusError) - if l, ok := data[logrusErrKey]; ok { - data["fields."+logrusErrKey] = l - delete(data, logrusErrKey) - } - - // If reportCaller is not set, 'func' will not conflict. - if reportCaller { - funcKey := fieldMap.resolve(FieldKeyFunc) - if l, ok := data[funcKey]; ok { - data["fields."+funcKey] = l - } - fileKey := fieldMap.resolve(FieldKeyFile) - if l, ok := data[fileKey]; ok { - data["fields."+fileKey] = l - } - } -} diff --git a/vendor/github.com/sirupsen/logrus/hooks.go b/vendor/github.com/sirupsen/logrus/hooks.go deleted file mode 100644 index 9ab978a45..000000000 --- a/vendor/github.com/sirupsen/logrus/hooks.go +++ /dev/null @@ -1,34 +0,0 @@ -package logrus - -// Hook describes hooks to be fired when logging on the logging levels returned from -// [Hook.Levels] on your implementation of the interface. Note that this is not -// fired in a goroutine or a channel with workers, you should handle such -// functionality yourself if your call is non-blocking, and you don't wish for -// the logging calls for levels returned from `Levels()` to block. -type Hook interface { - Levels() []Level - Fire(*Entry) error -} - -// LevelHooks is an internal type for storing the hooks on a logger instance. -type LevelHooks map[Level][]Hook - -// Add a hook to an instance of logger. This is called with -// `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface. -func (hooks LevelHooks) Add(hook Hook) { - for _, level := range hook.Levels() { - hooks[level] = append(hooks[level], hook) - } -} - -// Fire all the hooks for the passed level. Used by `entry.log` to fire -// appropriate hooks for a log entry. -func (hooks LevelHooks) Fire(level Level, entry *Entry) error { - for _, hook := range hooks[level] { - if err := hook.Fire(entry); err != nil { - return err - } - } - - return nil -} diff --git a/vendor/github.com/sirupsen/logrus/json_formatter.go b/vendor/github.com/sirupsen/logrus/json_formatter.go deleted file mode 100644 index fac7695e9..000000000 --- a/vendor/github.com/sirupsen/logrus/json_formatter.go +++ /dev/null @@ -1,137 +0,0 @@ -package logrus - -import ( - "bytes" - "encoding/json" - "fmt" - "runtime" - "strconv" -) - -type fieldKey string - -// FieldMap allows customization of the key names for default fields. -type FieldMap map[fieldKey]string - -func (f FieldMap) resolve(key fieldKey) string { - if k, ok := f[key]; ok { - return k - } - - return string(key) -} - -// JSONFormatter formats logs into parsable JSON. -// -// Fields from [Entry.Data] are included in the JSON object together with the -// standard fields derived from the entry. If a field conflicts with a standard -// field, it is prefixed with "fields.". Standard field names can be customized -// through FieldMap. When DataKey is set, fields from [Entry.Data] are nested -// under that key instead. -type JSONFormatter struct { - // TimestampFormat sets the format used for marshaling timestamps. - // The format to use is the same than for time.Format or time.Parse from the standard - // library. - // The standard Library already provides a set of predefined format. - TimestampFormat string - - // DisableTimestamp allows disabling automatic timestamps in output - DisableTimestamp bool - - // DisableHTMLEscape allows disabling html escaping in output - DisableHTMLEscape bool - - // DataKey allows users to put all the log entry parameters into a nested dictionary at a given key. - DataKey string - - // FieldMap allows users to customize the names of keys for default fields. - // As an example: - // formatter := &JSONFormatter{ - // FieldMap: FieldMap{ - // FieldKeyTime: "@timestamp", - // FieldKeyLevel: "@level", - // FieldKeyMsg: "@message", - // FieldKeyFunc: "@caller", - // }, - // } - FieldMap FieldMap - - // CallerPrettyfier can be set by the user to modify the content - // of the function and file keys in the json data when ReportCaller is - // activated. If any of the returned value is the empty string the - // corresponding key will be removed from json fields. - CallerPrettyfier func(*runtime.Frame) (function string, file string) - - // PrettyPrint will indent all json logs - PrettyPrint bool -} - -// Format renders a single log entry -func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) { - caller := entry.Caller - data := make(Fields, len(entry.Data)+defaultFields) - for k, v := range entry.Data { - switch v := v.(type) { - case error: - // Otherwise errors are ignored by `encoding/json` - // https://github.com/sirupsen/logrus/issues/137 - data[k] = v.Error() - default: - data[k] = v - } - } - - if f.DataKey != "" && len(entry.Data) > 0 { - newData := make(Fields, defaultFields+1) - newData[f.DataKey] = data - data = newData - } - - hasCaller := caller != nil - prefixFieldClashes(data, f.FieldMap, hasCaller) - - timestampFormat := f.TimestampFormat - if timestampFormat == "" { - timestampFormat = defaultTimestampFormat - } - - if entry.err != "" { - data[f.FieldMap.resolve(FieldKeyLogrusError)] = entry.err - } - if !f.DisableTimestamp { - data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat) - } - data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message - data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String() - if caller != nil { - var funcVal, fileVal string - if f.CallerPrettyfier != nil { - funcVal, fileVal = f.CallerPrettyfier(caller) - } else { - funcVal = caller.Function - fileVal = caller.File + ":" + strconv.FormatInt(int64(caller.Line), 10) - } - if funcVal != "" { - data[f.FieldMap.resolve(FieldKeyFunc)] = funcVal - } - if fileVal != "" { - data[f.FieldMap.resolve(FieldKeyFile)] = fileVal - } - } - - b := entry.Buffer - if b == nil { - b = new(bytes.Buffer) - } - - encoder := json.NewEncoder(b) - encoder.SetEscapeHTML(!f.DisableHTMLEscape) - if f.PrettyPrint { - encoder.SetIndent("", " ") - } - if err := encoder.Encode(data); err != nil { - return nil, fmt.Errorf("failed to marshal fields to JSON, %w", err) - } - - return b.Bytes(), nil -} diff --git a/vendor/github.com/sirupsen/logrus/level.go b/vendor/github.com/sirupsen/logrus/level.go deleted file mode 100644 index 7bd4255d3..000000000 --- a/vendor/github.com/sirupsen/logrus/level.go +++ /dev/null @@ -1,101 +0,0 @@ -package logrus - -import ( - "strings" - "sync" -) - -const ( - ansiReset = "\x1b[0m" // reset attributes - ansiRed = "\x1b[31m" // red - ansiYellow = "\x1b[33m" // yellow - ansiCyan = "\x1b[36m" // cyan - ansiDimCyan = "\x1b[2;36m" // dim cyan - ansiDimWhite = "\x1b[2;37m" // dim white (light gray) -) - -type lvlPrefix struct { - full string - truncated string - padded string -} - -func colorize(level Level, s string) string { - color := ansiCyan - switch level { - case TraceLevel: - color = ansiDimWhite - case DebugLevel: - color = ansiDimCyan - case WarnLevel: - color = ansiYellow - case ErrorLevel, FatalLevel, PanicLevel: - color = ansiRed - case InfoLevel: - color = ansiCyan - } - return color + s + ansiReset -} - -func formatLevel(level Level, disableTrunc, pad bool, maxLen int) string { - upper := strings.ToUpper(level.String()) - - if pad && maxLen > len(upper) { - upper += strings.Repeat(" ", maxLen-len(upper)) - } - - if !pad && !disableTrunc && len(upper) > 4 { - upper = upper[:4] - } - - return colorize(level, upper) -} - -var levelPrefixOnce = sync.OnceValues(func() (map[Level]lvlPrefix, lvlPrefix) { - var maxLevel Level - maxLen := 0 - for _, lvl := range AllLevels { - if lvl > maxLevel { - maxLevel = lvl - } - if l := len(lvl.String()); l > maxLen { - maxLen = l - } - } - - prefix := make(map[Level]lvlPrefix, len(AllLevels)) - for _, lvl := range AllLevels { - prefix[lvl] = lvlPrefix{ - full: formatLevel(lvl, true, false, maxLen), - truncated: formatLevel(lvl, false, false, maxLen), - padded: formatLevel(lvl, true, true, maxLen), - } - } - - unknownLevel := maxLevel + 1 - unknown := lvlPrefix{ - full: formatLevel(unknownLevel, true, false, maxLen), - truncated: formatLevel(unknownLevel, false, false, maxLen), - padded: formatLevel(unknownLevel, true, true, maxLen), - } - - return prefix, unknown -}) - -func levelPrefix(level Level, disableTrunc, pad bool) string { - prefix, unknown := levelPrefixOnce() - - p, ok := prefix[level] - if !ok { - p = unknown - } - - switch { - case pad: - return p.padded - case !disableTrunc: - return p.truncated - default: - return p.full - } -} diff --git a/vendor/github.com/sirupsen/logrus/logger.go b/vendor/github.com/sirupsen/logrus/logger.go deleted file mode 100644 index 17a46e6a6..000000000 --- a/vendor/github.com/sirupsen/logrus/logger.go +++ /dev/null @@ -1,475 +0,0 @@ -package logrus - -import ( - "context" - "io" - "os" - "sync" - "sync/atomic" - "time" -) - -// LogFunction For big messages, it can be more efficient to pass a function -// and only call it if the log level is actually enables rather than -// generating the log message and then checking if the level is enabled -type LogFunction func() []any - -type Logger struct { - // The logs are `io.Copy`'d to this in a mutex. It's common to set this to a - // file, or leave it default which is `os.Stderr`. You can also set this to - // something more adventurous, such as logging to Kafka. - Out io.Writer - - // Hooks for the logger instance. These allow firing events based on logging - // levels and log entries. For example, to send errors to an error tracking - // service, log to StatsD or dump the core on fatal errors. - Hooks LevelHooks - - // All log entries pass through the formatter before logged to Out. The - // included formatters are `TextFormatter` and `JSONFormatter` for which - // TextFormatter is the default. In development (when a TTY is attached) it - // logs with colors, but to a file it wouldn't. You can easily implement your - // own that implements the `Formatter` interface, see the `README` or included - // formatters for examples. - Formatter Formatter - - // Flag for whether to log caller info (off by default) - ReportCaller bool - - // The logging level the logger should log at. This is typically (and defaults - // to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be - // logged. - Level Level - - // Used to sync writing to the log. Locking is enabled by Default - mu mutexWrap - - // Reusable empty entry - entryPool sync.Pool - - // Function to exit the application, defaults to `os.Exit()` - ExitFunc func(int) - - // The buffer pool used to format the log. If it is nil, the default global - // buffer pool will be used. - BufferPool BufferPool -} - -// MutexWrap is the mutex implementation used by [Logger]. -// -// Deprecated: MutexWrap is an implementation detail of Logger and should not be used directly. -type MutexWrap = mutexWrap - -type mutexWrap struct { - lock sync.Mutex - disabled bool -} - -func (mw *mutexWrap) Lock() { - if !mw.disabled { - mw.lock.Lock() - } -} - -func (mw *mutexWrap) Unlock() { - if !mw.disabled { - mw.lock.Unlock() - } -} - -func (mw *mutexWrap) Disable() { - mw.disabled = true -} - -// New Creates a new logger. Configuration should be set by changing [Formatter], -// Out and Hooks directly on the default Logger instance. You can also just -// instantiate your own: -// -// var log = &logrus.Logger{ -// Out: os.Stderr, -// Formatter: new(logrus.TextFormatter), -// Hooks: make(logrus.LevelHooks), -// Level: logrus.DebugLevel, -// } -// -// It's recommended to make this a global instance called `log`. -func New() *Logger { - return &Logger{ - Out: os.Stderr, - Formatter: new(TextFormatter), - Hooks: make(LevelHooks), - Level: InfoLevel, - ExitFunc: os.Exit, - ReportCaller: false, - } -} - -func (logger *Logger) newEntry() *Entry { - entry, ok := logger.entryPool.Get().(*Entry) - if ok { - return entry - } - return NewEntry(logger) -} - -func (logger *Logger) releaseEntry(entry *Entry) { - entry.Data = map[string]any{} - logger.entryPool.Put(entry) -} - -// WithField allocates a new entry and adds a field to it. -// Debug, Print, Info, Warn, Error, Fatal or Panic must be then applied to -// this new returned entry. -// If you want multiple fields, use `WithFields`. -func (logger *Logger) WithField(key string, value any) *Entry { - entry := logger.newEntry() - defer logger.releaseEntry(entry) - return entry.WithField(key, value) -} - -// WithFields adds a struct of fields to the log entry. It calls [Entry.WithField] -// for each Field. -func (logger *Logger) WithFields(fields Fields) *Entry { - entry := logger.newEntry() - defer logger.releaseEntry(entry) - return entry.WithFields(fields) -} - -// WithError adds an error as single field to the log entry. It calls -// [Entry.WithError] for the given error. -func (logger *Logger) WithError(err error) *Entry { - entry := logger.newEntry() - defer logger.releaseEntry(entry) - return entry.WithError(err) -} - -// WithContext add a context to the log entry. -func (logger *Logger) WithContext(ctx context.Context) *Entry { - entry := logger.newEntry() - defer logger.releaseEntry(entry) - return entry.WithContext(ctx) -} - -// WithTime overrides the time of the log entry. -func (logger *Logger) WithTime(t time.Time) *Entry { - entry := logger.newEntry() - defer logger.releaseEntry(entry) - return entry.WithTime(t) -} - -// Logf logs a formatted message at the specified level. -// -// Using Logf with [PanicLevel] or [FatalLevel] intentionally does not -// trigger a panic or exit. Logf treats the level as logging severity only; -// use [Logger.Panicf] or [Logger.Fatalf] when those side effects are desired. -func (logger *Logger) Logf(level Level, format string, args ...any) { - if logger.IsLevelEnabled(level) { - entry := logger.newEntry() - entry.Logf(level, format, args...) - logger.releaseEntry(entry) - } -} - -func (logger *Logger) Tracef(format string, args ...any) { - logger.Logf(TraceLevel, format, args...) -} - -func (logger *Logger) Debugf(format string, args ...any) { - logger.Logf(DebugLevel, format, args...) -} - -func (logger *Logger) Infof(format string, args ...any) { - logger.Logf(InfoLevel, format, args...) -} - -func (logger *Logger) Printf(format string, args ...any) { - entry := logger.newEntry() - entry.Printf(format, args...) - logger.releaseEntry(entry) -} - -func (logger *Logger) Warnf(format string, args ...any) { - logger.Logf(WarnLevel, format, args...) -} - -func (logger *Logger) Warningf(format string, args ...any) { - logger.Warnf(format, args...) -} - -func (logger *Logger) Errorf(format string, args ...any) { - logger.Logf(ErrorLevel, format, args...) -} - -func (logger *Logger) Fatalf(format string, args ...any) { - logger.Logf(FatalLevel, format, args...) - logger.Exit(1) -} - -func (logger *Logger) Panicf(format string, args ...any) { - if logger.IsLevelEnabled(PanicLevel) { - entry := logger.newEntry() - defer logger.releaseEntry(entry) - entry.Panicf(format, args...) - } -} - -// Log logs a message at the specified level. -// -// Using Log with [PanicLevel] or [FatalLevel] intentionally does not -// trigger a panic or exit. Log treats the level as logging severity only; -// use [Logger.Panic] or [Logger.Fatal] when those side effects are desired. -func (logger *Logger) Log(level Level, args ...any) { - if logger.IsLevelEnabled(level) { - entry := logger.newEntry() - entry.Log(level, args...) - logger.releaseEntry(entry) - } -} - -// LogFn logs a message returned by fn at the specified level. -// -// Using LogFn with [PanicLevel] or [FatalLevel] intentionally does not -// trigger a panic or exit. LogFn treats the level as logging severity only; -// use [Logger.PanicFn] or [Logger.FatalFn] when those side effects are desired. -func (logger *Logger) LogFn(level Level, fn LogFunction) { - if logger.IsLevelEnabled(level) { - entry := logger.newEntry() - entry.Log(level, fn()...) - logger.releaseEntry(entry) - } -} - -func (logger *Logger) Trace(args ...any) { - logger.Log(TraceLevel, args...) -} - -func (logger *Logger) Debug(args ...any) { - logger.Log(DebugLevel, args...) -} - -func (logger *Logger) Info(args ...any) { - logger.Log(InfoLevel, args...) -} - -func (logger *Logger) Print(args ...any) { - entry := logger.newEntry() - entry.Print(args...) - logger.releaseEntry(entry) -} - -func (logger *Logger) Warn(args ...any) { - logger.Log(WarnLevel, args...) -} - -func (logger *Logger) Warning(args ...any) { - logger.Warn(args...) -} - -func (logger *Logger) Error(args ...any) { - logger.Log(ErrorLevel, args...) -} - -func (logger *Logger) Fatal(args ...any) { - logger.Log(FatalLevel, args...) - logger.Exit(1) -} - -func (logger *Logger) Panic(args ...any) { - if logger.IsLevelEnabled(PanicLevel) { - entry := logger.newEntry() - defer logger.releaseEntry(entry) - entry.Panic(args...) - } -} - -func (logger *Logger) TraceFn(fn LogFunction) { - logger.LogFn(TraceLevel, fn) -} - -func (logger *Logger) DebugFn(fn LogFunction) { - logger.LogFn(DebugLevel, fn) -} - -func (logger *Logger) InfoFn(fn LogFunction) { - logger.LogFn(InfoLevel, fn) -} - -func (logger *Logger) PrintFn(fn LogFunction) { - entry := logger.newEntry() - entry.Print(fn()...) - logger.releaseEntry(entry) -} - -func (logger *Logger) WarnFn(fn LogFunction) { - logger.LogFn(WarnLevel, fn) -} - -func (logger *Logger) WarningFn(fn LogFunction) { - logger.WarnFn(fn) -} - -func (logger *Logger) ErrorFn(fn LogFunction) { - logger.LogFn(ErrorLevel, fn) -} - -func (logger *Logger) FatalFn(fn LogFunction) { - logger.LogFn(FatalLevel, fn) - logger.Exit(1) -} - -func (logger *Logger) PanicFn(fn LogFunction) { - if logger.IsLevelEnabled(PanicLevel) { - entry := logger.newEntry() - defer logger.releaseEntry(entry) - entry.Panic(fn()...) - } -} - -// Logln logs a message at the specified level with Println-style spacing. -// -// Using Logln with [PanicLevel] or [FatalLevel] intentionally does not -// trigger a panic or exit. Logln treats the level as logging severity only; -// use [Logger.Panicln] or [Logger.Fatalln] when those side effects are desired. -func (logger *Logger) Logln(level Level, args ...any) { - if logger.IsLevelEnabled(level) { - entry := logger.newEntry() - entry.Logln(level, args...) - logger.releaseEntry(entry) - } -} - -func (logger *Logger) Traceln(args ...any) { - logger.Logln(TraceLevel, args...) -} - -func (logger *Logger) Debugln(args ...any) { - logger.Logln(DebugLevel, args...) -} - -func (logger *Logger) Infoln(args ...any) { - logger.Logln(InfoLevel, args...) -} - -func (logger *Logger) Println(args ...any) { - entry := logger.newEntry() - entry.Println(args...) - logger.releaseEntry(entry) -} - -func (logger *Logger) Warnln(args ...any) { - logger.Logln(WarnLevel, args...) -} - -func (logger *Logger) Warningln(args ...any) { - logger.Warnln(args...) -} - -func (logger *Logger) Errorln(args ...any) { - logger.Logln(ErrorLevel, args...) -} - -func (logger *Logger) Fatalln(args ...any) { - logger.Logln(FatalLevel, args...) - logger.Exit(1) -} - -func (logger *Logger) Panicln(args ...any) { - if logger.IsLevelEnabled(PanicLevel) { - entry := logger.newEntry() - defer logger.releaseEntry(entry) - entry.Panicln(args...) - } -} - -func (logger *Logger) Exit(code int) { - runHandlers() - if logger.ExitFunc == nil { - logger.ExitFunc = os.Exit - } - logger.ExitFunc(code) -} - -// SetNoLock disables the lock for situations where a file is opened with -// appending mode, and safe for concurrent writes to the file (within 4k -// message on Linux). In these cases user can choose to disable the lock. -func (logger *Logger) SetNoLock() { - logger.mu.Disable() -} - -func (logger *Logger) level() Level { - return Level(atomic.LoadUint32((*uint32)(&logger.Level))) -} - -// SetLevel sets the logger level. -func (logger *Logger) SetLevel(level Level) { - atomic.StoreUint32((*uint32)(&logger.Level), uint32(level)) -} - -// GetLevel returns the logger level. -func (logger *Logger) GetLevel() Level { - return logger.level() -} - -// AddHook adds a hook to the logger hooks. -func (logger *Logger) AddHook(hook Hook) { - logger.mu.Lock() - defer logger.mu.Unlock() - logger.Hooks.Add(hook) -} - -// hooksForLevel returns a snapshot of the hooks registered for the given level. -// The returned slice is a shallow copy and may be used without holding logger.mu. -func (logger *Logger) hooksForLevel(level Level) []Hook { - logger.mu.Lock() - hooks := logger.Hooks[level] - if len(hooks) == 0 { - logger.mu.Unlock() - return nil - } - out := make([]Hook, len(hooks)) - copy(out, hooks) - logger.mu.Unlock() - return out -} - -// IsLevelEnabled checks if logging for the given level is enabled. -func (logger *Logger) IsLevelEnabled(level Level) bool { - return logger.level() >= level -} - -// SetFormatter sets the logger formatter. -func (logger *Logger) SetFormatter(formatter Formatter) { - logger.mu.Lock() - defer logger.mu.Unlock() - logger.Formatter = formatter -} - -// SetOutput sets the logger output. -func (logger *Logger) SetOutput(output io.Writer) { - logger.mu.Lock() - defer logger.mu.Unlock() - logger.Out = output -} - -// SetReportCaller sets whether the caller stack frame must be logged. -func (logger *Logger) SetReportCaller(reportCaller bool) { - logger.mu.Lock() - defer logger.mu.Unlock() - logger.ReportCaller = reportCaller -} - -// ReplaceHooks replaces the logger hooks and returns the old ones -func (logger *Logger) ReplaceHooks(hooks LevelHooks) LevelHooks { - logger.mu.Lock() - defer logger.mu.Unlock() - oldHooks := logger.Hooks - logger.Hooks = hooks - return oldHooks -} - -// SetBufferPool sets the logger buffer pool. -func (logger *Logger) SetBufferPool(pool BufferPool) { - logger.mu.Lock() - defer logger.mu.Unlock() - logger.BufferPool = pool -} diff --git a/vendor/github.com/sirupsen/logrus/logrus.go b/vendor/github.com/sirupsen/logrus/logrus.go deleted file mode 100644 index d52b4ba73..000000000 --- a/vendor/github.com/sirupsen/logrus/logrus.go +++ /dev/null @@ -1,227 +0,0 @@ -package logrus - -import ( - "bytes" - "fmt" - "log" -) - -// Fields type, used to pass to [WithFields]. -type Fields map[string]any - -// Level type -// -//nolint:recvcheck // the methods of "Entry" use pointer receiver and non-pointer receiver. -type Level uint32 - -// Convert the Level to a string. E.g. [PanicLevel] becomes "panic". -func (level Level) String() string { - switch level { - case TraceLevel: - return "trace" - case DebugLevel: - return "debug" - case InfoLevel: - return "info" - case WarnLevel: - return "warning" - case ErrorLevel: - return "error" - case FatalLevel: - return "fatal" - case PanicLevel: - return "panic" - default: - return "unknown" - } -} - -// ParseLevel takes a string level and returns the Logrus log level constant. -func ParseLevel(lvl string) (Level, error) { - return parseLevel([]byte(lvl)) -} - -func parseLevel(b []byte) (Level, error) { - switch { - case bytes.EqualFold(b, []byte("panic")): - return PanicLevel, nil - case bytes.EqualFold(b, []byte("fatal")): - return FatalLevel, nil - case bytes.EqualFold(b, []byte("error")): - return ErrorLevel, nil - case bytes.EqualFold(b, []byte("warn")), - bytes.EqualFold(b, []byte("warning")): - return WarnLevel, nil - case bytes.EqualFold(b, []byte("info")): - return InfoLevel, nil - case bytes.EqualFold(b, []byte("debug")): - return DebugLevel, nil - case bytes.EqualFold(b, []byte("trace")): - return TraceLevel, nil - default: - return 0, fmt.Errorf("not a valid logrus Level: %q", b) - } -} - -// UnmarshalText implements encoding.TextUnmarshaler. -func (level *Level) UnmarshalText(text []byte) error { - l, err := parseLevel(text) - if err != nil { - return err - } - - *level = l - - return nil -} - -func (level Level) MarshalText() ([]byte, error) { - switch level { - case TraceLevel, DebugLevel, InfoLevel, WarnLevel, ErrorLevel, FatalLevel, PanicLevel: - return []byte(level.String()), nil - default: - return nil, fmt.Errorf("not a valid logrus level %d", level) - } -} - -// AllLevels exposing all logging levels. -var AllLevels = []Level{ - PanicLevel, - FatalLevel, - ErrorLevel, - WarnLevel, - InfoLevel, - DebugLevel, - TraceLevel, -} - -// These are the different logging levels. You can set the logging level to log -// on your instance of logger, obtained with [logrus.New]. -const ( - // PanicLevel level, highest level of severity. Logs and then calls panic with the - // message passed to Debug, Info, ... - PanicLevel Level = iota - // FatalLevel level. Logs and then calls `logger.Exit(1)`. It will exit even if the - // logging level is set to Panic. - FatalLevel - // ErrorLevel level. Logs. Used for errors that should definitely be noted. - // Commonly used for hooks to send errors to an error tracking service. - ErrorLevel - // WarnLevel level. Non-critical entries that deserve eyes. - WarnLevel - // InfoLevel level. General operational entries about what's going on inside the - // application. - InfoLevel - // DebugLevel level. Usually only enabled when debugging. Very verbose logging. - DebugLevel - // TraceLevel level. Designates finer-grained informational events than the Debug. - TraceLevel -) - -// Compile-time interface assertions. -var ( - _ StdLogger = (*log.Logger)(nil) - _ StdLogger = (*Entry)(nil) - _ StdLogger = (*Logger)(nil) - - _ FieldLogger = (*Logger)(nil) - _ FieldLogger = (*Entry)(nil) - _ FieldLogger = Ext1FieldLogger(nil) - - _ DebugLogger = (*Logger)(nil) - _ InfoLogger = (*Logger)(nil) - _ WarnLogger = (*Logger)(nil) - _ ErrorLogger = (*Logger)(nil) - _ TraceLogger = (*Logger)(nil) - - _ DebugLogger = (*Entry)(nil) - _ InfoLogger = (*Entry)(nil) - _ WarnLogger = (*Entry)(nil) - _ ErrorLogger = (*Entry)(nil) - _ TraceLogger = (*Entry)(nil) - - _ Ext1FieldLogger = (*Logger)(nil) - _ Ext1FieldLogger = (*Entry)(nil) -) - -// StdLogger is what your logrus-enabled library should take, that way -// it'll accept a stdlib logger ([log.Logger]) and a logrus logger. -// There's no standard interface, so this is the closest we get, unfortunately. -type StdLogger interface { - Print(args ...any) - Printf(format string, args ...any) - Println(args ...any) - - Fatal(args ...any) - Fatalf(format string, args ...any) - Fatalln(args ...any) - - Panic(args ...any) - Panicf(format string, args ...any) - Panicln(args ...any) -} - -// FieldLogger extends the [StdLogger] interface, generalizing -// the [Entry] and [Logger] types. -type FieldLogger interface { - WithField(key string, value any) *Entry - WithFields(fields Fields) *Entry - WithError(err error) *Entry - - StdLogger - DebugLogger - InfoLogger - WarnLogger - ErrorLogger - - // Legacy warning aliases. These are kept on FieldLogger for backwards - // compatibility, but are intentionally omitted from [WarnLogger]. - - Warning(args ...any) - Warningf(format string, args ...any) - Warningln(args ...any) -} - -// DebugLogger provides convenience functions to log messages at level [DebugLevel]. -type DebugLogger interface { - Debug(args ...any) - Debugf(format string, args ...any) - Debugln(args ...any) -} - -// InfoLogger provides convenience functions to log messages at level [InfoLevel]. -type InfoLogger interface { - Info(args ...any) - Infof(format string, args ...any) - Infoln(args ...any) -} - -// WarnLogger provides convenience functions to log messages at level [WarnLevel]. -type WarnLogger interface { - Warn(args ...any) - Warnf(format string, args ...any) - Warnln(args ...any) -} - -// ErrorLogger provides convenience functions to log messages at level [ErrorLevel]. -type ErrorLogger interface { - Error(args ...any) - Errorf(format string, args ...any) - Errorln(args ...any) -} - -// TraceLogger provides convenience functions to log messages at level [TraceLevel]. -type TraceLogger interface { - Trace(args ...any) - Tracef(format string, args ...any) - Traceln(args ...any) -} - -// Ext1FieldLogger is FieldLogger extended with Trace-level methods. -// -// New code should prefer the smallest applicable interface, such as -// [FieldLogger] or [TraceLogger], or use [Logger] or [Entry] directly. -type Ext1FieldLogger interface { - FieldLogger - TraceLogger -} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_appengine.go b/vendor/github.com/sirupsen/logrus/terminal_check_appengine.go deleted file mode 100644 index 1c6202b8c..000000000 --- a/vendor/github.com/sirupsen/logrus/terminal_check_appengine.go +++ /dev/null @@ -1,7 +0,0 @@ -//go:build appengine - -package logrus - -func checkIfTerminal(_ any) bool { - return true -} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_bsd.go b/vendor/github.com/sirupsen/logrus/terminal_check_bsd.go deleted file mode 100644 index ff9531ac4..000000000 --- a/vendor/github.com/sirupsen/logrus/terminal_check_bsd.go +++ /dev/null @@ -1,12 +0,0 @@ -//go:build (darwin || dragonfly || freebsd || netbsd || openbsd || hurd) && !tinygo - -package logrus - -import "golang.org/x/sys/unix" - -const ioctlReadTermios = unix.TIOCGETA - -func isTerminal(fd int) bool { - _, err := unix.IoctlGetTermios(fd, ioctlReadTermios) - return err == nil -} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go b/vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go deleted file mode 100644 index 17ae9f04f..000000000 --- a/vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go +++ /dev/null @@ -1,7 +0,0 @@ -//go:build js || nacl || plan9 || wasi || wasip1 || tinygo - -package logrus - -func checkIfTerminal(_ any) bool { - return false -} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go b/vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go deleted file mode 100644 index 780a42a57..000000000 --- a/vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go +++ /dev/null @@ -1,21 +0,0 @@ -//go:build !appengine && !js && !windows && !nacl && !plan9 && !wasi && !wasip1 && !tinygo - -package logrus - -import ( - "io" - "os" -) - -func checkIfTerminal(w io.Writer) bool { - switch v := w.(type) { - case *os.File: - fd := v.Fd() - if fd > uintptr(^uint(0)>>1) { - return false - } - return isTerminal(int(fd)) - default: - return false - } -} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_solaris.go b/vendor/github.com/sirupsen/logrus/terminal_check_solaris.go deleted file mode 100644 index 8d9b26fca..000000000 --- a/vendor/github.com/sirupsen/logrus/terminal_check_solaris.go +++ /dev/null @@ -1,13 +0,0 @@ -//go:build solaris && !tinygo - -package logrus - -import ( - "golang.org/x/sys/unix" -) - -// IsTerminal returns true if the given file descriptor is a terminal. -func isTerminal(fd int) bool { - _, err := unix.IoctlGetTermio(fd, unix.TCGETA) - return err == nil -} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_unix.go b/vendor/github.com/sirupsen/logrus/terminal_check_unix.go deleted file mode 100644 index b161506d3..000000000 --- a/vendor/github.com/sirupsen/logrus/terminal_check_unix.go +++ /dev/null @@ -1,12 +0,0 @@ -//go:build (linux || aix || zos) && !tinygo - -package logrus - -import "golang.org/x/sys/unix" - -const ioctlReadTermios = unix.TCGETS - -func isTerminal(fd int) bool { - _, err := unix.IoctlGetTermios(fd, ioctlReadTermios) - return err == nil -} diff --git a/vendor/github.com/sirupsen/logrus/terminal_check_windows.go b/vendor/github.com/sirupsen/logrus/terminal_check_windows.go deleted file mode 100644 index 5fd3c4313..000000000 --- a/vendor/github.com/sirupsen/logrus/terminal_check_windows.go +++ /dev/null @@ -1,27 +0,0 @@ -//go:build windows && !appengine - -package logrus - -import ( - "io" - "os" - - "golang.org/x/sys/windows" -) - -func checkIfTerminal(w io.Writer) bool { - switch v := w.(type) { - case *os.File: - handle := windows.Handle(v.Fd()) - var mode uint32 - if err := windows.GetConsoleMode(handle, &mode); err != nil { - return false - } - mode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING - if err := windows.SetConsoleMode(handle, mode); err != nil { - return false - } - return true - } - return false -} diff --git a/vendor/github.com/sirupsen/logrus/text_formatter.go b/vendor/github.com/sirupsen/logrus/text_formatter.go deleted file mode 100644 index 82c1f3da2..000000000 --- a/vendor/github.com/sirupsen/logrus/text_formatter.go +++ /dev/null @@ -1,483 +0,0 @@ -package logrus - -import ( - "bytes" - "fmt" - "maps" - "os" - "reflect" - "runtime" - "slices" - "strconv" - "strings" - "sync" - "time" -) - -var baseTimestamp = time.Now() - -// TextFormatter formats logs into text. -// -// Output is logfmt-like: key=value pairs separated by spaces. Fields from -// [Entry.Data] are included together with the standard fields derived from the -// entry. If a field conflicts with a standard field, it is prefixed with -// "fields.". Standard field names can be customized through FieldMap. -// -// Field keys are written as-is (unquoted and unescaped) in the plain -// (non-colored) format; only field values may be quoted depending on -// DisableQuote, ForceQuote, QuoteEmptyFields, and the value content. -// -// When colors are enabled, ANSI escape sequences may be added for presentation. -// For fully escaped structured output (including safe keys), use JSONFormatter. -type TextFormatter struct { - // Set to true to bypass checking for a TTY before outputting colors. - ForceColors bool - - // Force disabling colors. - DisableColors bool - - // Force quoting of all values - ForceQuote bool - - // DisableQuote disables quoting for all values. - // DisableQuote will have a lower priority than ForceQuote. - // If both of them are set to true, quote will be forced on all values. - DisableQuote bool - - // Override coloring based on CLICOLOR and CLICOLOR_FORCE. - https://bixense.com/clicolors/ - EnvironmentOverrideColors bool - - // Disable timestamp logging. useful when output is redirected to logging - // system that already adds timestamps. - DisableTimestamp bool - - // Enable logging the full timestamp when a TTY is attached instead of just - // the time passed since beginning of execution. - FullTimestamp bool - - // TimestampFormat to use for display when a full timestamp is printed. - // The format to use is the same than for time.Format or time.Parse from the standard - // library. - // The standard Library already provides a set of predefined format. - TimestampFormat string - - // The fields are sorted by default for a consistent output. For applications - // that log extremely frequently and don't use the JSON formatter this may not - // be desired. - DisableSorting bool - - // The keys sorting function, when uninitialized it uses slices.Sort. - SortingFunc func([]string) - - // Disables the truncation of the level text to 4 characters. - DisableLevelTruncation bool - - // PadLevelText Adds padding the level text so that all the levels output at the same length - // PadLevelText is a superset of the DisableLevelTruncation option - PadLevelText bool - - // QuoteEmptyFields will wrap empty fields in quotes if true - QuoteEmptyFields bool - - // Whether the logger's out is to a terminal. Don't use this field - // directly; use TextFormatter.isTerminal instead. - terminal bool - - // FieldMap allows users to customize the names of keys for default fields. - // Mapped keys are written as-is, so they should be safe for plain-text output. - // - // As an example: - // - // formatter := &TextFormatter{ - // FieldMap: FieldMap{ - // FieldKeyTime: "@timestamp", - // FieldKeyLevel: "@level", - // FieldKeyMsg: "@message", - // }, - // } - FieldMap FieldMap - - // CallerPrettyfier can be set by the user to modify the content - // of the function and file keys in the data when ReportCaller is - // activated. If any of the returned value is the empty string the - // corresponding key will be removed from fields. - CallerPrettyfier func(*runtime.Frame) (function string, file string) - - terminalInitOnce sync.Once -} - -func (f *TextFormatter) isTerminal(entry *Entry) bool { - if entry == nil || entry.Logger == nil { - // Don't run the terminalInitOnce without a logger, otherwise we'd - // cache the default (false) forever even if a logger is attached - // later. - return false - } - - f.terminalInitOnce.Do(func() { - entry.Logger.mu.Lock() - out := entry.Logger.Out - entry.Logger.mu.Unlock() - - f.terminal = checkIfTerminal(out) - }) - - return f.terminal -} - -func (f *TextFormatter) isColored(isTerminal bool) bool { - if f.DisableColors { - return false - } - - colored := f.ForceColors || isTerminal - if !f.EnvironmentOverrideColors { - return colored - } - if force, ok := os.LookupEnv("CLICOLOR_FORCE"); ok { - return force != "0" - } - if os.Getenv("CLICOLOR") == "0" { - return false - } - return colored -} - -// Format renders a single log entry -func (f *TextFormatter) Format(entry *Entry) ([]byte, error) { - data := make(Fields, len(entry.Data)) - maps.Copy(data, entry.Data) - isColored := f.isColored(f.isTerminal(entry)) - - caller := entry.Caller - hasCaller := caller != nil - prefixFieldClashes(data, f.FieldMap, hasCaller) - keys := make([]string, 0, len(data)) - for k := range data { - keys = append(keys, k) - } - - b := entry.Buffer - if b == nil { - b = new(bytes.Buffer) - } - - if isColored { - f.printColored(b, entry, keys, data) - } else { - f.printPlain(b, entry, keys, data) - } - - return b.Bytes(), nil -} - -func (f *TextFormatter) printPlain(b *bytes.Buffer, entry *Entry, keys []string, data Fields) { - caller := entry.Caller - hasCaller := caller != nil - - fixedKeys := make([]string, 0, len(keys)+defaultFields) - if !f.DisableTimestamp { - fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyTime)) - } - fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLevel)) - if entry.Message != "" { - fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyMsg)) - } - if entry.err != "" { - fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLogrusError)) - } - - var funcVal, fileVal string - if caller != nil { - if f.CallerPrettyfier != nil { - funcVal, fileVal = f.CallerPrettyfier(caller) - } else { - funcVal = caller.Function - fileVal = caller.File + ":" + strconv.FormatInt(int64(caller.Line), 10) - } - - if funcVal != "" { - fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFunc)) - } - if fileVal != "" { - fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFile)) - } - } - - if !f.DisableSorting { - if f.SortingFunc == nil { - // Default sorting does not sort the "fixed keys"; - // see https://github.com/sirupsen/logrus/commit/73bc94e60c753099e8bae902f81fbd6e7dd95f26 - slices.Sort(keys) - fixedKeys = append(fixedKeys, keys...) - } else { - fixedKeys = append(fixedKeys, keys...) - f.SortingFunc(fixedKeys) - } - } else { - fixedKeys = append(fixedKeys, keys...) - } - - for _, key := range fixedKeys { - var value any - switch { - case key == f.FieldMap.resolve(FieldKeyTime): - if f.TimestampFormat == "" { - value = entry.Time.Format(defaultTimestampFormat) - } else { - value = entry.Time.Format(f.TimestampFormat) - } - case key == f.FieldMap.resolve(FieldKeyLevel): - value = entry.Level.String() - case key == f.FieldMap.resolve(FieldKeyMsg): - value = entry.Message - case key == f.FieldMap.resolve(FieldKeyLogrusError): - value = entry.err - case key == f.FieldMap.resolve(FieldKeyFunc) && hasCaller: - value = funcVal - case key == f.FieldMap.resolve(FieldKeyFile) && hasCaller: - value = fileVal - default: - value = data[key] - } - f.appendKeyValue(b, key, value) - } - - b.WriteByte('\n') -} - -func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, data Fields) { - // Remove a single newline if it already exists in the message to keep - // the behavior of logrus text_formatter the same as the stdlib log package - entry.Message = strings.TrimSuffix(entry.Message, "\n") - - var callerText string - if caller := entry.Caller; caller != nil { - var funcVal, fileVal string - if f.CallerPrettyfier != nil { - funcVal, fileVal = f.CallerPrettyfier(caller) - } else { - if caller.Function != "" { - funcVal = caller.Function + "()" - } - fileVal = caller.File + ":" + strconv.FormatInt(int64(caller.Line), 10) - } - - if fileVal == "" { - callerText = funcVal - } else if funcVal == "" { - callerText = fileVal - } else { - callerText = fileVal + " " + funcVal - } - } - - levelText := levelPrefix(entry.Level, f.DisableLevelTruncation, f.PadLevelText) - switch { - case f.DisableTimestamp: - _, _ = fmt.Fprintf(b, "%s%s %-44s ", levelText, callerText, entry.Message) - case !f.FullTimestamp: - _, _ = fmt.Fprintf(b, "%s[%04d]%s %-44s ", levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), callerText, entry.Message) - default: - timestampFormat := f.TimestampFormat - if timestampFormat == "" { - timestampFormat = defaultTimestampFormat - } - _, _ = fmt.Fprintf(b, "%s[%s]%s %-44s ", levelText, entry.Time.Format(timestampFormat), callerText, entry.Message) - } - - if !f.DisableSorting { - if f.SortingFunc == nil { - slices.Sort(keys) - } else { - f.SortingFunc(keys) - } - } - - // Keys use the same color as the level-prefix. - for _, k := range keys { - b.WriteByte(' ') - b.WriteString(colorize(entry.Level, k)) - b.WriteByte('=') - f.appendValue(b, data[k]) - } - - b.WriteByte('\n') -} - -// appendKeyValue writes key=value. Keys are written verbatim (unquoted/unescaped); -// values are subject to quoting/escaping. -func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value any) { - if b.Len() > 0 { - b.WriteByte(' ') - } - b.WriteString(key) - b.WriteByte('=') - f.appendValue(b, value) -} - -func (f *TextFormatter) appendValue(b *bytes.Buffer, value any) { - // Fast paths. - switch v := value.(type) { - case string: - f.appendString(b, v) - return - case []byte: - f.appendBytes(b, v) - return - case bool: - var raw [8]byte - f.appendBytes(b, strconv.AppendBool(raw[:0], v)) - return - case error: - f.appendError(b, v) - return - case fmt.Stringer: - f.appendStringer(b, v) - return - } - - // Handle common primitives. - var raw [64]byte - var num []byte - - switch v := value.(type) { - case int: - num = strconv.AppendInt(raw[:0], int64(v), 10) - case int8: - num = strconv.AppendInt(raw[:0], int64(v), 10) - case int16: - num = strconv.AppendInt(raw[:0], int64(v), 10) - case int32: - num = strconv.AppendInt(raw[:0], int64(v), 10) - case int64: - num = strconv.AppendInt(raw[:0], v, 10) - - case uint: - num = strconv.AppendUint(raw[:0], uint64(v), 10) - case uint8: - num = strconv.AppendUint(raw[:0], uint64(v), 10) - case uint16: - num = strconv.AppendUint(raw[:0], uint64(v), 10) - case uint32: - num = strconv.AppendUint(raw[:0], uint64(v), 10) - case uint64: - num = strconv.AppendUint(raw[:0], v, 10) - case uintptr: - num = strconv.AppendUint(raw[:0], uint64(v), 10) - - case float32: - num = strconv.AppendFloat(raw[:0], float64(v), 'g', -1, 32) - case float64: - num = strconv.AppendFloat(raw[:0], v, 'g', -1, 64) - - default: - f.appendString(b, fmt.Sprint(value)) - return - } - - f.appendNumeric(b, num) -} - -func (f *TextFormatter) appendString(b *bytes.Buffer, s string) { - quote := f.ForceQuote || (f.QuoteEmptyFields && len(s) == 0) || (!f.DisableQuote && needsQuoting(s)) - if !quote { - b.WriteString(s) - return - } - if len(s) == 0 { - b.WriteString(`""`) - return - } - - var tmp [128]byte - b.Write(strconv.AppendQuote(tmp[:0], s)) -} - -func (f *TextFormatter) appendBytes(b *bytes.Buffer, bs []byte) { - quote := f.ForceQuote || (f.QuoteEmptyFields && len(bs) == 0) || (!f.DisableQuote && needsQuotingBytes(bs)) - if !quote { - b.Write(bs) - return - } - if len(bs) == 0 { - b.WriteString(`""`) - return - } - - var tmp [128]byte - b.Write(strconv.AppendQuote(tmp[:0], string(bs))) -} - -func (f *TextFormatter) appendNumeric(b *bytes.Buffer, out []byte) { - if f.ForceQuote { - var tmp [128]byte - b.Write(strconv.AppendQuote(tmp[:0], string(out))) - return - } - b.Write(out) -} - -func (f *TextFormatter) appendError(b *bytes.Buffer, v error) { - defer f.recoverValue(b, v, "Error") - - f.appendString(b, v.Error()) -} - -func (f *TextFormatter) appendStringer(b *bytes.Buffer, v fmt.Stringer) { - defer f.recoverValue(b, v, "String") - - f.appendString(b, v.String()) -} - -func (f *TextFormatter) recoverValue(b *bytes.Buffer, v any, method string) { - if r := recover(); r != nil { - rv := reflect.ValueOf(v) - if rv.Kind() == reflect.Pointer && rv.IsNil() { - f.appendString(b, "") - } else { - f.appendString(b, fmt.Sprintf("%%!v(PANIC=%s method: %v)", method, r)) - } - } -} - -// needsQuoting returns true if the string contains any byte that -// requires quoting. It returns false when every byte is "safe" according -// to isSafeByte. -func needsQuoting(s string) bool { - // use an index loop (avoid rune decoding). - for i := range len(s) { - c := s[i] - if !isSafeByte(c) { - return true - } - } - return false -} - -// needsQuotingBytes returns true if the byte slice contains any byte that -// requires quoting. It returns false when every byte is "safe" according -// to isSafeByte. -func needsQuotingBytes(bs []byte) bool { - for _, c := range bs { - if !isSafeByte(c) { - return true - } - } - return false -} - -// isSafeByte returns true if the byte is allowed unquoted (ASCII and in the allowlist). -// It purposely uses byte arithmetic (no runes) for performance. -func isSafeByte(ch byte) bool { - ok := ch < 0x80 && ((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9')) - if ok { - return true - } - switch ch { - case '-', '.', '_', '/', '@', '^', '+': - return true - default: - return false - } -} diff --git a/vendor/github.com/sirupsen/logrus/writer.go b/vendor/github.com/sirupsen/logrus/writer.go deleted file mode 100644 index 30e34cda7..000000000 --- a/vendor/github.com/sirupsen/logrus/writer.go +++ /dev/null @@ -1,100 +0,0 @@ -package logrus - -import ( - "bufio" - "io" - "runtime" - "strings" -) - -// Writer at INFO level. See WriterLevel for details. -func (logger *Logger) Writer() *io.PipeWriter { - return logger.WriterLevel(InfoLevel) -} - -// WriterLevel returns an io.Writer that can be used to write arbitrary text to -// the logger at the given log level. Each line written to the writer will be -// printed in the usual way using formatters and hooks. The writer is part of an -// io.Pipe and it is the callers responsibility to close the writer when done. -// This can be used to override the standard library logger easily. -func (logger *Logger) WriterLevel(level Level) *io.PipeWriter { - return NewEntry(logger).WriterLevel(level) -} - -// Writer returns an io.Writer that writes to the logger at the info log level -func (entry *Entry) Writer() *io.PipeWriter { - return entry.WriterLevel(InfoLevel) -} - -// WriterLevel returns an io.Writer that writes to the logger at the given log level -func (entry *Entry) WriterLevel(level Level) *io.PipeWriter { - reader, writer := io.Pipe() - - printFunc := entry.Print - - // Determine which log function to use based on the specified log level - switch level { - case TraceLevel: - printFunc = entry.Trace - case DebugLevel: - printFunc = entry.Debug - case InfoLevel: - printFunc = entry.Info - case WarnLevel: - printFunc = entry.Warn - case ErrorLevel: - printFunc = entry.Error - case FatalLevel: - printFunc = entry.Fatal - case PanicLevel: - printFunc = entry.Panic - } - - // Start a new goroutine to scan the input and write it to the logger using the specified print function. - // It splits the input into chunks of up to 64KB to avoid buffer overflows. - go entry.writerScanner(reader, printFunc) - - // Set a finalizer function to close the writer when it is garbage collected - runtime.SetFinalizer(writer, writerFinalizer) - - return writer -} - -// writerScanner scans the input from the reader and writes it to the logger -func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...any)) { - scanner := bufio.NewScanner(reader) - - // Set the buffer size to the maximum token size to avoid buffer overflows - scanner.Buffer(make([]byte, bufio.MaxScanTokenSize), bufio.MaxScanTokenSize) - - // Define a split function to split the input into chunks of up to 64KB - chunkSize := bufio.MaxScanTokenSize // 64KB - splitFunc := func(data []byte, atEOF bool) (int, []byte, error) { - if len(data) >= chunkSize { - return chunkSize, data[:chunkSize], nil - } - - return bufio.ScanLines(data, atEOF) - } - - // Use the custom split function to split the input - scanner.Split(splitFunc) - - // Scan the input and write it to the logger using the specified print function - for scanner.Scan() { - printFunc(strings.TrimRight(scanner.Text(), "\r\n")) - } - - // If there was an error while scanning the input, log an error - if err := scanner.Err(); err != nil { - entry.Errorf("Error while reading from Writer: %s", err) - } - - // Close the reader when we are done - reader.Close() -} - -// WriterFinalizer is a finalizer function that closes then given writer when it is garbage collected -func writerFinalizer(writer *io.PipeWriter) { - writer.Close() -} diff --git a/vendor/github.com/stretchr/testify/LICENSE b/vendor/github.com/stretchr/testify/LICENSE deleted file mode 100644 index 4b0421cf9..000000000 --- a/vendor/github.com/stretchr/testify/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/stretchr/testify/assert/assertion_compare.go b/vendor/github.com/stretchr/testify/assert/assertion_compare.go deleted file mode 100644 index ffb24e8e3..000000000 --- a/vendor/github.com/stretchr/testify/assert/assertion_compare.go +++ /dev/null @@ -1,495 +0,0 @@ -package assert - -import ( - "bytes" - "fmt" - "reflect" - "time" -) - -// Deprecated: CompareType has only ever been for internal use and has accidentally been published since v1.6.0. Do not use it. -type CompareType = compareResult - -type compareResult int - -const ( - compareLess compareResult = iota - 1 - compareEqual - compareGreater -) - -var ( - intType = reflect.TypeOf(int(1)) - int8Type = reflect.TypeOf(int8(1)) - int16Type = reflect.TypeOf(int16(1)) - int32Type = reflect.TypeOf(int32(1)) - int64Type = reflect.TypeOf(int64(1)) - - uintType = reflect.TypeOf(uint(1)) - uint8Type = reflect.TypeOf(uint8(1)) - uint16Type = reflect.TypeOf(uint16(1)) - uint32Type = reflect.TypeOf(uint32(1)) - uint64Type = reflect.TypeOf(uint64(1)) - - uintptrType = reflect.TypeOf(uintptr(1)) - - float32Type = reflect.TypeOf(float32(1)) - float64Type = reflect.TypeOf(float64(1)) - - stringType = reflect.TypeOf("") - - timeType = reflect.TypeOf(time.Time{}) - bytesType = reflect.TypeOf([]byte{}) -) - -func compare(obj1, obj2 interface{}, kind reflect.Kind) (compareResult, bool) { - obj1Value := reflect.ValueOf(obj1) - obj2Value := reflect.ValueOf(obj2) - - // throughout this switch we try and avoid calling .Convert() if possible, - // as this has a pretty big performance impact - switch kind { - case reflect.Int: - { - intobj1, ok := obj1.(int) - if !ok { - intobj1 = obj1Value.Convert(intType).Interface().(int) - } - intobj2, ok := obj2.(int) - if !ok { - intobj2 = obj2Value.Convert(intType).Interface().(int) - } - if intobj1 > intobj2 { - return compareGreater, true - } - if intobj1 == intobj2 { - return compareEqual, true - } - if intobj1 < intobj2 { - return compareLess, true - } - } - case reflect.Int8: - { - int8obj1, ok := obj1.(int8) - if !ok { - int8obj1 = obj1Value.Convert(int8Type).Interface().(int8) - } - int8obj2, ok := obj2.(int8) - if !ok { - int8obj2 = obj2Value.Convert(int8Type).Interface().(int8) - } - if int8obj1 > int8obj2 { - return compareGreater, true - } - if int8obj1 == int8obj2 { - return compareEqual, true - } - if int8obj1 < int8obj2 { - return compareLess, true - } - } - case reflect.Int16: - { - int16obj1, ok := obj1.(int16) - if !ok { - int16obj1 = obj1Value.Convert(int16Type).Interface().(int16) - } - int16obj2, ok := obj2.(int16) - if !ok { - int16obj2 = obj2Value.Convert(int16Type).Interface().(int16) - } - if int16obj1 > int16obj2 { - return compareGreater, true - } - if int16obj1 == int16obj2 { - return compareEqual, true - } - if int16obj1 < int16obj2 { - return compareLess, true - } - } - case reflect.Int32: - { - int32obj1, ok := obj1.(int32) - if !ok { - int32obj1 = obj1Value.Convert(int32Type).Interface().(int32) - } - int32obj2, ok := obj2.(int32) - if !ok { - int32obj2 = obj2Value.Convert(int32Type).Interface().(int32) - } - if int32obj1 > int32obj2 { - return compareGreater, true - } - if int32obj1 == int32obj2 { - return compareEqual, true - } - if int32obj1 < int32obj2 { - return compareLess, true - } - } - case reflect.Int64: - { - int64obj1, ok := obj1.(int64) - if !ok { - int64obj1 = obj1Value.Convert(int64Type).Interface().(int64) - } - int64obj2, ok := obj2.(int64) - if !ok { - int64obj2 = obj2Value.Convert(int64Type).Interface().(int64) - } - if int64obj1 > int64obj2 { - return compareGreater, true - } - if int64obj1 == int64obj2 { - return compareEqual, true - } - if int64obj1 < int64obj2 { - return compareLess, true - } - } - case reflect.Uint: - { - uintobj1, ok := obj1.(uint) - if !ok { - uintobj1 = obj1Value.Convert(uintType).Interface().(uint) - } - uintobj2, ok := obj2.(uint) - if !ok { - uintobj2 = obj2Value.Convert(uintType).Interface().(uint) - } - if uintobj1 > uintobj2 { - return compareGreater, true - } - if uintobj1 == uintobj2 { - return compareEqual, true - } - if uintobj1 < uintobj2 { - return compareLess, true - } - } - case reflect.Uint8: - { - uint8obj1, ok := obj1.(uint8) - if !ok { - uint8obj1 = obj1Value.Convert(uint8Type).Interface().(uint8) - } - uint8obj2, ok := obj2.(uint8) - if !ok { - uint8obj2 = obj2Value.Convert(uint8Type).Interface().(uint8) - } - if uint8obj1 > uint8obj2 { - return compareGreater, true - } - if uint8obj1 == uint8obj2 { - return compareEqual, true - } - if uint8obj1 < uint8obj2 { - return compareLess, true - } - } - case reflect.Uint16: - { - uint16obj1, ok := obj1.(uint16) - if !ok { - uint16obj1 = obj1Value.Convert(uint16Type).Interface().(uint16) - } - uint16obj2, ok := obj2.(uint16) - if !ok { - uint16obj2 = obj2Value.Convert(uint16Type).Interface().(uint16) - } - if uint16obj1 > uint16obj2 { - return compareGreater, true - } - if uint16obj1 == uint16obj2 { - return compareEqual, true - } - if uint16obj1 < uint16obj2 { - return compareLess, true - } - } - case reflect.Uint32: - { - uint32obj1, ok := obj1.(uint32) - if !ok { - uint32obj1 = obj1Value.Convert(uint32Type).Interface().(uint32) - } - uint32obj2, ok := obj2.(uint32) - if !ok { - uint32obj2 = obj2Value.Convert(uint32Type).Interface().(uint32) - } - if uint32obj1 > uint32obj2 { - return compareGreater, true - } - if uint32obj1 == uint32obj2 { - return compareEqual, true - } - if uint32obj1 < uint32obj2 { - return compareLess, true - } - } - case reflect.Uint64: - { - uint64obj1, ok := obj1.(uint64) - if !ok { - uint64obj1 = obj1Value.Convert(uint64Type).Interface().(uint64) - } - uint64obj2, ok := obj2.(uint64) - if !ok { - uint64obj2 = obj2Value.Convert(uint64Type).Interface().(uint64) - } - if uint64obj1 > uint64obj2 { - return compareGreater, true - } - if uint64obj1 == uint64obj2 { - return compareEqual, true - } - if uint64obj1 < uint64obj2 { - return compareLess, true - } - } - case reflect.Float32: - { - float32obj1, ok := obj1.(float32) - if !ok { - float32obj1 = obj1Value.Convert(float32Type).Interface().(float32) - } - float32obj2, ok := obj2.(float32) - if !ok { - float32obj2 = obj2Value.Convert(float32Type).Interface().(float32) - } - if float32obj1 > float32obj2 { - return compareGreater, true - } - if float32obj1 == float32obj2 { - return compareEqual, true - } - if float32obj1 < float32obj2 { - return compareLess, true - } - } - case reflect.Float64: - { - float64obj1, ok := obj1.(float64) - if !ok { - float64obj1 = obj1Value.Convert(float64Type).Interface().(float64) - } - float64obj2, ok := obj2.(float64) - if !ok { - float64obj2 = obj2Value.Convert(float64Type).Interface().(float64) - } - if float64obj1 > float64obj2 { - return compareGreater, true - } - if float64obj1 == float64obj2 { - return compareEqual, true - } - if float64obj1 < float64obj2 { - return compareLess, true - } - } - case reflect.String: - { - stringobj1, ok := obj1.(string) - if !ok { - stringobj1 = obj1Value.Convert(stringType).Interface().(string) - } - stringobj2, ok := obj2.(string) - if !ok { - stringobj2 = obj2Value.Convert(stringType).Interface().(string) - } - if stringobj1 > stringobj2 { - return compareGreater, true - } - if stringobj1 == stringobj2 { - return compareEqual, true - } - if stringobj1 < stringobj2 { - return compareLess, true - } - } - // Check for known struct types we can check for compare results. - case reflect.Struct: - { - // All structs enter here. We're not interested in most types. - if !obj1Value.CanConvert(timeType) { - break - } - - // time.Time can be compared! - timeObj1, ok := obj1.(time.Time) - if !ok { - timeObj1 = obj1Value.Convert(timeType).Interface().(time.Time) - } - - timeObj2, ok := obj2.(time.Time) - if !ok { - timeObj2 = obj2Value.Convert(timeType).Interface().(time.Time) - } - - if timeObj1.Before(timeObj2) { - return compareLess, true - } - if timeObj1.Equal(timeObj2) { - return compareEqual, true - } - return compareGreater, true - } - case reflect.Slice: - { - // We only care about the []byte type. - if !obj1Value.CanConvert(bytesType) { - break - } - - // []byte can be compared! - bytesObj1, ok := obj1.([]byte) - if !ok { - bytesObj1 = obj1Value.Convert(bytesType).Interface().([]byte) - - } - bytesObj2, ok := obj2.([]byte) - if !ok { - bytesObj2 = obj2Value.Convert(bytesType).Interface().([]byte) - } - - return compareResult(bytes.Compare(bytesObj1, bytesObj2)), true - } - case reflect.Uintptr: - { - uintptrObj1, ok := obj1.(uintptr) - if !ok { - uintptrObj1 = obj1Value.Convert(uintptrType).Interface().(uintptr) - } - uintptrObj2, ok := obj2.(uintptr) - if !ok { - uintptrObj2 = obj2Value.Convert(uintptrType).Interface().(uintptr) - } - if uintptrObj1 > uintptrObj2 { - return compareGreater, true - } - if uintptrObj1 == uintptrObj2 { - return compareEqual, true - } - if uintptrObj1 < uintptrObj2 { - return compareLess, true - } - } - } - - return compareEqual, false -} - -// Greater asserts that the first element is greater than the second -// -// assert.Greater(t, 2, 1) -// assert.Greater(t, float64(2), float64(1)) -// assert.Greater(t, "b", "a") -func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - failMessage := fmt.Sprintf("\"%v\" is not greater than \"%v\"", e1, e2) - return compareTwoValues(t, e1, e2, []compareResult{compareGreater}, failMessage, msgAndArgs...) -} - -// GreaterOrEqual asserts that the first element is greater than or equal to the second -// -// assert.GreaterOrEqual(t, 2, 1) -// assert.GreaterOrEqual(t, 2, 2) -// assert.GreaterOrEqual(t, "b", "a") -// assert.GreaterOrEqual(t, "b", "b") -func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - failMessage := fmt.Sprintf("\"%v\" is not greater than or equal to \"%v\"", e1, e2) - return compareTwoValues(t, e1, e2, []compareResult{compareGreater, compareEqual}, failMessage, msgAndArgs...) -} - -// Less asserts that the first element is less than the second -// -// assert.Less(t, 1, 2) -// assert.Less(t, float64(1), float64(2)) -// assert.Less(t, "a", "b") -func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - failMessage := fmt.Sprintf("\"%v\" is not less than \"%v\"", e1, e2) - return compareTwoValues(t, e1, e2, []compareResult{compareLess}, failMessage, msgAndArgs...) -} - -// LessOrEqual asserts that the first element is less than or equal to the second -// -// assert.LessOrEqual(t, 1, 2) -// assert.LessOrEqual(t, 2, 2) -// assert.LessOrEqual(t, "a", "b") -// assert.LessOrEqual(t, "b", "b") -func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - failMessage := fmt.Sprintf("\"%v\" is not less than or equal to \"%v\"", e1, e2) - return compareTwoValues(t, e1, e2, []compareResult{compareLess, compareEqual}, failMessage, msgAndArgs...) -} - -// Positive asserts that the specified element is positive -// -// assert.Positive(t, 1) -// assert.Positive(t, 1.23) -func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - zero := reflect.Zero(reflect.TypeOf(e)) - failMessage := fmt.Sprintf("\"%v\" is not positive", e) - return compareTwoValues(t, e, zero.Interface(), []compareResult{compareGreater}, failMessage, msgAndArgs...) -} - -// Negative asserts that the specified element is negative -// -// assert.Negative(t, -1) -// assert.Negative(t, -1.23) -func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - zero := reflect.Zero(reflect.TypeOf(e)) - failMessage := fmt.Sprintf("\"%v\" is not negative", e) - return compareTwoValues(t, e, zero.Interface(), []compareResult{compareLess}, failMessage, msgAndArgs...) -} - -func compareTwoValues(t TestingT, e1 interface{}, e2 interface{}, allowedComparesResults []compareResult, failMessage string, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - - e1Kind := reflect.ValueOf(e1).Kind() - e2Kind := reflect.ValueOf(e2).Kind() - if e1Kind != e2Kind { - return Fail(t, "Elements should be the same type", msgAndArgs...) - } - - compareResult, isComparable := compare(e1, e2, e1Kind) - if !isComparable { - return Fail(t, fmt.Sprintf(`Can not compare type "%T"`, e1), msgAndArgs...) - } - - if !containsValue(allowedComparesResults, compareResult) { - return Fail(t, failMessage, msgAndArgs...) - } - - return true -} - -func containsValue(values []compareResult, value compareResult) bool { - for _, v := range values { - if v == value { - return true - } - } - - return false -} diff --git a/vendor/github.com/stretchr/testify/assert/assertion_format.go b/vendor/github.com/stretchr/testify/assert/assertion_format.go deleted file mode 100644 index a19a89279..000000000 --- a/vendor/github.com/stretchr/testify/assert/assertion_format.go +++ /dev/null @@ -1,878 +0,0 @@ -// Code generated with github.com/stretchr/testify/_codegen; DO NOT EDIT. - -package assert - -import ( - http "net/http" - url "net/url" - time "time" -) - -// Conditionf uses a Comparison to assert a complex condition. -func Conditionf(t TestingT, comp Comparison, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return Condition(t, comp, append([]interface{}{msg}, args...)...) -} - -// Containsf asserts that the specified string, list(array, slice...) or map contains the -// specified substring or element. -// -// assert.Containsf(t, "Hello World", "World", "error message %s", "formatted") -// assert.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") -// assert.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") -func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return Contains(t, s, contains, append([]interface{}{msg}, args...)...) -} - -// DirExistsf checks whether a directory exists in the given path. It also fails -// if the path is a file rather a directory or there is an error checking whether it exists. -func DirExistsf(t TestingT, path string, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return DirExists(t, path, append([]interface{}{msg}, args...)...) -} - -// ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified -// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, -// the number of appearances of each of them in both lists should match. -// -// assert.ElementsMatchf(t, [1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted") -func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return ElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...) -} - -// Emptyf asserts that the given value is "empty". -// -// [Zero values] are "empty". -// -// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). -// -// Slices, maps and channels with zero length are "empty". -// -// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". -// -// assert.Emptyf(t, obj, "error message %s", "formatted") -// -// [Zero values]: https://go.dev/ref/spec#The_zero_value -func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return Empty(t, object, append([]interface{}{msg}, args...)...) -} - -// Equalf asserts that two objects are equal. -// -// assert.Equalf(t, 123, 123, "error message %s", "formatted") -// -// Pointer variable equality is determined based on the equality of the -// referenced values (as opposed to the memory addresses). Function equality -// cannot be determined and will always fail. -func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return Equal(t, expected, actual, append([]interface{}{msg}, args...)...) -} - -// EqualErrorf asserts that a function returned a non-nil error (i.e. an error) -// and that it is equal to the provided error. -// -// actualObj, err := SomeFunction() -// assert.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") -func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return EqualError(t, theError, errString, append([]interface{}{msg}, args...)...) -} - -// EqualExportedValuesf asserts that the types of two objects are equal and their public -// fields are also equal. This is useful for comparing structs that have private fields -// that could potentially differ. -// -// type S struct { -// Exported int -// notExported int -// } -// assert.EqualExportedValuesf(t, S{1, 2}, S{1, 3}, "error message %s", "formatted") => true -// assert.EqualExportedValuesf(t, S{1, 2}, S{2, 3}, "error message %s", "formatted") => false -func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return EqualExportedValues(t, expected, actual, append([]interface{}{msg}, args...)...) -} - -// EqualValuesf asserts that two objects are equal or convertible to the larger -// type and equal. -// -// assert.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") -func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return EqualValues(t, expected, actual, append([]interface{}{msg}, args...)...) -} - -// Errorf asserts that a function returned a non-nil error (ie. an error). -// -// actualObj, err := SomeFunction() -// assert.Errorf(t, err, "error message %s", "formatted") -func Errorf(t TestingT, err error, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return Error(t, err, append([]interface{}{msg}, args...)...) -} - -// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. -// This is a wrapper for errors.As. -func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return ErrorAs(t, err, target, append([]interface{}{msg}, args...)...) -} - -// ErrorContainsf asserts that a function returned a non-nil error (i.e. an -// error) and that the error contains the specified substring. -// -// actualObj, err := SomeFunction() -// assert.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") -func ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return ErrorContains(t, theError, contains, append([]interface{}{msg}, args...)...) -} - -// ErrorIsf asserts that at least one of the errors in err's chain matches target. -// This is a wrapper for errors.Is. -func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return ErrorIs(t, err, target, append([]interface{}{msg}, args...)...) -} - -// Eventuallyf asserts that given condition will be met in waitFor time, -// periodically checking target function each tick. -// -// assert.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") -func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return Eventually(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...) -} - -// EventuallyWithTf asserts that given condition will be met in waitFor time, -// periodically checking target function each tick. In contrast to Eventually, -// it supplies a CollectT to the condition function, so that the condition -// function can use the CollectT to call other assertions. -// The condition is considered "met" if no errors are raised in a tick. -// The supplied CollectT collects all errors from one tick (if there are any). -// If the condition is not met before waitFor, the collected errors of -// the last tick are copied to t. -// -// externalValue := false -// go func() { -// time.Sleep(8*time.Second) -// externalValue = true -// }() -// assert.EventuallyWithTf(t, func(c *assert.CollectT) { -// // add assertions as needed; any assertion failure will fail the current tick -// assert.True(c, externalValue, "expected 'externalValue' to be true") -// }, 10*time.Second, 1*time.Second, "error message %s", "formatted") -func EventuallyWithTf(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return EventuallyWithT(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...) -} - -// Exactlyf asserts that two objects are equal in value and type. -// -// assert.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") -func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return Exactly(t, expected, actual, append([]interface{}{msg}, args...)...) -} - -// Failf reports a failure through -func Failf(t TestingT, failureMessage string, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return Fail(t, failureMessage, append([]interface{}{msg}, args...)...) -} - -// FailNowf fails test -func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return FailNow(t, failureMessage, append([]interface{}{msg}, args...)...) -} - -// Falsef asserts that the specified value is false. -// -// assert.Falsef(t, myBool, "error message %s", "formatted") -func Falsef(t TestingT, value bool, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return False(t, value, append([]interface{}{msg}, args...)...) -} - -// FileExistsf checks whether a file exists in the given path. It also fails if -// the path points to a directory or there is an error when trying to check the file. -func FileExistsf(t TestingT, path string, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return FileExists(t, path, append([]interface{}{msg}, args...)...) -} - -// Greaterf asserts that the first element is greater than the second -// -// assert.Greaterf(t, 2, 1, "error message %s", "formatted") -// assert.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") -// assert.Greaterf(t, "b", "a", "error message %s", "formatted") -func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return Greater(t, e1, e2, append([]interface{}{msg}, args...)...) -} - -// GreaterOrEqualf asserts that the first element is greater than or equal to the second -// -// assert.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") -// assert.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") -// assert.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") -// assert.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") -func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return GreaterOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...) -} - -// HTTPBodyContainsf asserts that a specified handler returns a -// body that contains a string. -// -// assert.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") -// -// Returns whether the assertion was successful (true) or not (false). -func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return HTTPBodyContains(t, handler, method, url, values, str, append([]interface{}{msg}, args...)...) -} - -// HTTPBodyNotContainsf asserts that a specified handler returns a -// body that does not contain a string. -// -// assert.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") -// -// Returns whether the assertion was successful (true) or not (false). -func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return HTTPBodyNotContains(t, handler, method, url, values, str, append([]interface{}{msg}, args...)...) -} - -// HTTPErrorf asserts that a specified handler returns an error status code. -// -// assert.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} -// -// Returns whether the assertion was successful (true) or not (false). -func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return HTTPError(t, handler, method, url, values, append([]interface{}{msg}, args...)...) -} - -// HTTPRedirectf asserts that a specified handler returns a redirect status code. -// -// assert.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} -// -// Returns whether the assertion was successful (true) or not (false). -func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return HTTPRedirect(t, handler, method, url, values, append([]interface{}{msg}, args...)...) -} - -// HTTPStatusCodef asserts that a specified handler returns a specified status code. -// -// assert.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") -// -// Returns whether the assertion was successful (true) or not (false). -func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return HTTPStatusCode(t, handler, method, url, values, statuscode, append([]interface{}{msg}, args...)...) -} - -// HTTPSuccessf asserts that a specified handler returns a success status code. -// -// assert.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") -// -// Returns whether the assertion was successful (true) or not (false). -func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return HTTPSuccess(t, handler, method, url, values, append([]interface{}{msg}, args...)...) -} - -// Implementsf asserts that an object is implemented by the specified interface. -// -// assert.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") -func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return Implements(t, interfaceObject, object, append([]interface{}{msg}, args...)...) -} - -// InDeltaf asserts that the two numerals are within delta of each other. -// -// assert.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") -func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return InDelta(t, expected, actual, delta, append([]interface{}{msg}, args...)...) -} - -// InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. -func InDeltaMapValuesf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return InDeltaMapValues(t, expected, actual, delta, append([]interface{}{msg}, args...)...) -} - -// InDeltaSlicef is the same as InDelta, except it compares two slices. -func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return InDeltaSlice(t, expected, actual, delta, append([]interface{}{msg}, args...)...) -} - -// InEpsilonf asserts that expected and actual have a relative error less than epsilon -func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return InEpsilon(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...) -} - -// InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices. -func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return InEpsilonSlice(t, expected, actual, epsilon, append([]interface{}{msg}, args...)...) -} - -// IsDecreasingf asserts that the collection is decreasing -// -// assert.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") -// assert.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") -// assert.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") -func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return IsDecreasing(t, object, append([]interface{}{msg}, args...)...) -} - -// IsIncreasingf asserts that the collection is increasing -// -// assert.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") -// assert.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") -// assert.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") -func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return IsIncreasing(t, object, append([]interface{}{msg}, args...)...) -} - -// IsNonDecreasingf asserts that the collection is not decreasing -// -// assert.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") -// assert.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") -// assert.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") -func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return IsNonDecreasing(t, object, append([]interface{}{msg}, args...)...) -} - -// IsNonIncreasingf asserts that the collection is not increasing -// -// assert.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") -// assert.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") -// assert.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") -func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return IsNonIncreasing(t, object, append([]interface{}{msg}, args...)...) -} - -// IsNotTypef asserts that the specified objects are not of the same type. -// -// assert.IsNotTypef(t, &NotMyStruct{}, &MyStruct{}, "error message %s", "formatted") -func IsNotTypef(t TestingT, theType interface{}, object interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return IsNotType(t, theType, object, append([]interface{}{msg}, args...)...) -} - -// IsTypef asserts that the specified objects are of the same type. -// -// assert.IsTypef(t, &MyStruct{}, &MyStruct{}, "error message %s", "formatted") -func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return IsType(t, expectedType, object, append([]interface{}{msg}, args...)...) -} - -// JSONEqf asserts that two JSON strings are equivalent. -// -// assert.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") -func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return JSONEq(t, expected, actual, append([]interface{}{msg}, args...)...) -} - -// Lenf asserts that the specified object has specific length. -// Lenf also fails if the object has a type that len() not accept. -// -// assert.Lenf(t, mySlice, 3, "error message %s", "formatted") -func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return Len(t, object, length, append([]interface{}{msg}, args...)...) -} - -// Lessf asserts that the first element is less than the second -// -// assert.Lessf(t, 1, 2, "error message %s", "formatted") -// assert.Lessf(t, float64(1), float64(2), "error message %s", "formatted") -// assert.Lessf(t, "a", "b", "error message %s", "formatted") -func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return Less(t, e1, e2, append([]interface{}{msg}, args...)...) -} - -// LessOrEqualf asserts that the first element is less than or equal to the second -// -// assert.LessOrEqualf(t, 1, 2, "error message %s", "formatted") -// assert.LessOrEqualf(t, 2, 2, "error message %s", "formatted") -// assert.LessOrEqualf(t, "a", "b", "error message %s", "formatted") -// assert.LessOrEqualf(t, "b", "b", "error message %s", "formatted") -func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return LessOrEqual(t, e1, e2, append([]interface{}{msg}, args...)...) -} - -// Negativef asserts that the specified element is negative -// -// assert.Negativef(t, -1, "error message %s", "formatted") -// assert.Negativef(t, -1.23, "error message %s", "formatted") -func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return Negative(t, e, append([]interface{}{msg}, args...)...) -} - -// Neverf asserts that the given condition doesn't satisfy in waitFor time, -// periodically checking the target function each tick. -// -// assert.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") -func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return Never(t, condition, waitFor, tick, append([]interface{}{msg}, args...)...) -} - -// Nilf asserts that the specified object is nil. -// -// assert.Nilf(t, err, "error message %s", "formatted") -func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return Nil(t, object, append([]interface{}{msg}, args...)...) -} - -// NoDirExistsf checks whether a directory does not exist in the given path. -// It fails if the path points to an existing _directory_ only. -func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return NoDirExists(t, path, append([]interface{}{msg}, args...)...) -} - -// NoErrorf asserts that a function returned a nil error (ie. no error). -// -// actualObj, err := SomeFunction() -// if assert.NoErrorf(t, err, "error message %s", "formatted") { -// assert.Equal(t, expectedObj, actualObj) -// } -func NoErrorf(t TestingT, err error, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return NoError(t, err, append([]interface{}{msg}, args...)...) -} - -// NoFileExistsf checks whether a file does not exist in a given path. It fails -// if the path points to an existing _file_ only. -func NoFileExistsf(t TestingT, path string, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return NoFileExists(t, path, append([]interface{}{msg}, args...)...) -} - -// NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the -// specified substring or element. -// -// assert.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") -// assert.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") -// assert.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") -func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return NotContains(t, s, contains, append([]interface{}{msg}, args...)...) -} - -// NotElementsMatchf asserts that the specified listA(array, slice...) is NOT equal to specified -// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, -// the number of appearances of each of them in both lists should not match. -// This is an inverse of ElementsMatch. -// -// assert.NotElementsMatchf(t, [1, 1, 2, 3], [1, 1, 2, 3], "error message %s", "formatted") -> false -// -// assert.NotElementsMatchf(t, [1, 1, 2, 3], [1, 2, 3], "error message %s", "formatted") -> true -// -// assert.NotElementsMatchf(t, [1, 2, 3], [1, 2, 4], "error message %s", "formatted") -> true -func NotElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return NotElementsMatch(t, listA, listB, append([]interface{}{msg}, args...)...) -} - -// NotEmptyf asserts that the specified object is NOT [Empty]. -// -// if assert.NotEmptyf(t, obj, "error message %s", "formatted") { -// assert.Equal(t, "two", obj[1]) -// } -func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return NotEmpty(t, object, append([]interface{}{msg}, args...)...) -} - -// NotEqualf asserts that the specified values are NOT equal. -// -// assert.NotEqualf(t, obj1, obj2, "error message %s", "formatted") -// -// Pointer variable equality is determined based on the equality of the -// referenced values (as opposed to the memory addresses). -func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return NotEqual(t, expected, actual, append([]interface{}{msg}, args...)...) -} - -// NotEqualValuesf asserts that two objects are not equal even when converted to the same type -// -// assert.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") -func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return NotEqualValues(t, expected, actual, append([]interface{}{msg}, args...)...) -} - -// NotErrorAsf asserts that none of the errors in err's chain matches target, -// but if so, sets target to that error value. -func NotErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return NotErrorAs(t, err, target, append([]interface{}{msg}, args...)...) -} - -// NotErrorIsf asserts that none of the errors in err's chain matches target. -// This is a wrapper for errors.Is. -func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return NotErrorIs(t, err, target, append([]interface{}{msg}, args...)...) -} - -// NotImplementsf asserts that an object does not implement the specified interface. -// -// assert.NotImplementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") -func NotImplementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return NotImplements(t, interfaceObject, object, append([]interface{}{msg}, args...)...) -} - -// NotNilf asserts that the specified object is not nil. -// -// assert.NotNilf(t, err, "error message %s", "formatted") -func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return NotNil(t, object, append([]interface{}{msg}, args...)...) -} - -// NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. -// -// assert.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") -func NotPanicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return NotPanics(t, f, append([]interface{}{msg}, args...)...) -} - -// NotRegexpf asserts that a specified regexp does not match a string. -// -// assert.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") -// assert.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") -func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return NotRegexp(t, rx, str, append([]interface{}{msg}, args...)...) -} - -// NotSamef asserts that two pointers do not reference the same object. -// -// assert.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") -// -// Both arguments must be pointer variables. Pointer variable sameness is -// determined based on the equality of both type and value. -func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return NotSame(t, expected, actual, append([]interface{}{msg}, args...)...) -} - -// NotSubsetf asserts that the list (array, slice, or map) does NOT contain all -// elements given in the subset (array, slice, or map). -// Map elements are key-value pairs unless compared with an array or slice where -// only the map key is evaluated. -// -// assert.NotSubsetf(t, [1, 3, 4], [1, 2], "error message %s", "formatted") -// assert.NotSubsetf(t, {"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") -// assert.NotSubsetf(t, [1, 3, 4], {1: "one", 2: "two"}, "error message %s", "formatted") -// assert.NotSubsetf(t, {"x": 1, "y": 2}, ["z"], "error message %s", "formatted") -func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return NotSubset(t, list, subset, append([]interface{}{msg}, args...)...) -} - -// NotZerof asserts that i is not the zero value for its type. -func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return NotZero(t, i, append([]interface{}{msg}, args...)...) -} - -// Panicsf asserts that the code inside the specified PanicTestFunc panics. -// -// assert.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") -func Panicsf(t TestingT, f PanicTestFunc, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return Panics(t, f, append([]interface{}{msg}, args...)...) -} - -// PanicsWithErrorf asserts that the code inside the specified PanicTestFunc -// panics, and that the recovered panic value is an error that satisfies the -// EqualError comparison. -// -// assert.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") -func PanicsWithErrorf(t TestingT, errString string, f PanicTestFunc, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return PanicsWithError(t, errString, f, append([]interface{}{msg}, args...)...) -} - -// PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that -// the recovered panic value equals the expected panic value. -// -// assert.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") -func PanicsWithValuef(t TestingT, expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return PanicsWithValue(t, expected, f, append([]interface{}{msg}, args...)...) -} - -// Positivef asserts that the specified element is positive -// -// assert.Positivef(t, 1, "error message %s", "formatted") -// assert.Positivef(t, 1.23, "error message %s", "formatted") -func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return Positive(t, e, append([]interface{}{msg}, args...)...) -} - -// Regexpf asserts that a specified regexp matches a string. -// -// assert.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") -// assert.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") -func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return Regexp(t, rx, str, append([]interface{}{msg}, args...)...) -} - -// Samef asserts that two pointers reference the same object. -// -// assert.Samef(t, ptr1, ptr2, "error message %s", "formatted") -// -// Both arguments must be pointer variables. Pointer variable sameness is -// determined based on the equality of both type and value. -func Samef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return Same(t, expected, actual, append([]interface{}{msg}, args...)...) -} - -// Subsetf asserts that the list (array, slice, or map) contains all elements -// given in the subset (array, slice, or map). -// Map elements are key-value pairs unless compared with an array or slice where -// only the map key is evaluated. -// -// assert.Subsetf(t, [1, 2, 3], [1, 2], "error message %s", "formatted") -// assert.Subsetf(t, {"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") -// assert.Subsetf(t, [1, 2, 3], {1: "one", 2: "two"}, "error message %s", "formatted") -// assert.Subsetf(t, {"x": 1, "y": 2}, ["x"], "error message %s", "formatted") -func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return Subset(t, list, subset, append([]interface{}{msg}, args...)...) -} - -// Truef asserts that the specified value is true. -// -// assert.Truef(t, myBool, "error message %s", "formatted") -func Truef(t TestingT, value bool, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return True(t, value, append([]interface{}{msg}, args...)...) -} - -// WithinDurationf asserts that the two times are within duration delta of each other. -// -// assert.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") -func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return WithinDuration(t, expected, actual, delta, append([]interface{}{msg}, args...)...) -} - -// WithinRangef asserts that a time is within a time range (inclusive). -// -// assert.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") -func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return WithinRange(t, actual, start, end, append([]interface{}{msg}, args...)...) -} - -// YAMLEqf asserts that the first documents in the two YAML strings are equivalent. -// -// expected := `--- -// key: value -// --- -// key: this is a second document, it is not evaluated -// ` -// actual := `--- -// key: value -// --- -// key: this is a subsequent document, it is not evaluated -// ` -// assert.YAMLEqf(t, expected, actual, "error message %s", "formatted") -func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return YAMLEq(t, expected, actual, append([]interface{}{msg}, args...)...) -} - -// Zerof asserts that i is the zero value for its type. -func Zerof(t TestingT, i interface{}, msg string, args ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return Zero(t, i, append([]interface{}{msg}, args...)...) -} diff --git a/vendor/github.com/stretchr/testify/assert/assertion_format.go.tmpl b/vendor/github.com/stretchr/testify/assert/assertion_format.go.tmpl deleted file mode 100644 index d2bb0b817..000000000 --- a/vendor/github.com/stretchr/testify/assert/assertion_format.go.tmpl +++ /dev/null @@ -1,5 +0,0 @@ -{{.CommentFormat}} -func {{.DocInfo.Name}}f(t TestingT, {{.ParamsFormat}}) bool { - if h, ok := t.(tHelper); ok { h.Helper() } - return {{.DocInfo.Name}}(t, {{.ForwardedParamsFormat}}) -} diff --git a/vendor/github.com/stretchr/testify/assert/assertion_forward.go b/vendor/github.com/stretchr/testify/assert/assertion_forward.go deleted file mode 100644 index cd2a86061..000000000 --- a/vendor/github.com/stretchr/testify/assert/assertion_forward.go +++ /dev/null @@ -1,1747 +0,0 @@ -// Code generated with github.com/stretchr/testify/_codegen; DO NOT EDIT. - -package assert - -import ( - http "net/http" - url "net/url" - time "time" -) - -// Condition uses a Comparison to assert a complex condition. -func (a *Assertions) Condition(comp Comparison, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Condition(a.t, comp, msgAndArgs...) -} - -// Conditionf uses a Comparison to assert a complex condition. -func (a *Assertions) Conditionf(comp Comparison, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Conditionf(a.t, comp, msg, args...) -} - -// Contains asserts that the specified string, list(array, slice...) or map contains the -// specified substring or element. -// -// a.Contains("Hello World", "World") -// a.Contains(["Hello", "World"], "World") -// a.Contains({"Hello": "World"}, "Hello") -func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Contains(a.t, s, contains, msgAndArgs...) -} - -// Containsf asserts that the specified string, list(array, slice...) or map contains the -// specified substring or element. -// -// a.Containsf("Hello World", "World", "error message %s", "formatted") -// a.Containsf(["Hello", "World"], "World", "error message %s", "formatted") -// a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted") -func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Containsf(a.t, s, contains, msg, args...) -} - -// DirExists checks whether a directory exists in the given path. It also fails -// if the path is a file rather a directory or there is an error checking whether it exists. -func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return DirExists(a.t, path, msgAndArgs...) -} - -// DirExistsf checks whether a directory exists in the given path. It also fails -// if the path is a file rather a directory or there is an error checking whether it exists. -func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return DirExistsf(a.t, path, msg, args...) -} - -// ElementsMatch asserts that the specified listA(array, slice...) is equal to specified -// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, -// the number of appearances of each of them in both lists should match. -// -// a.ElementsMatch([1, 3, 2, 3], [1, 3, 3, 2]) -func (a *Assertions) ElementsMatch(listA interface{}, listB interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return ElementsMatch(a.t, listA, listB, msgAndArgs...) -} - -// ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified -// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, -// the number of appearances of each of them in both lists should match. -// -// a.ElementsMatchf([1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted") -func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return ElementsMatchf(a.t, listA, listB, msg, args...) -} - -// Empty asserts that the given value is "empty". -// -// [Zero values] are "empty". -// -// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). -// -// Slices, maps and channels with zero length are "empty". -// -// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". -// -// a.Empty(obj) -// -// [Zero values]: https://go.dev/ref/spec#The_zero_value -func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Empty(a.t, object, msgAndArgs...) -} - -// Emptyf asserts that the given value is "empty". -// -// [Zero values] are "empty". -// -// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). -// -// Slices, maps and channels with zero length are "empty". -// -// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". -// -// a.Emptyf(obj, "error message %s", "formatted") -// -// [Zero values]: https://go.dev/ref/spec#The_zero_value -func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Emptyf(a.t, object, msg, args...) -} - -// Equal asserts that two objects are equal. -// -// a.Equal(123, 123) -// -// Pointer variable equality is determined based on the equality of the -// referenced values (as opposed to the memory addresses). Function equality -// cannot be determined and will always fail. -func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Equal(a.t, expected, actual, msgAndArgs...) -} - -// EqualError asserts that a function returned a non-nil error (i.e. an error) -// and that it is equal to the provided error. -// -// actualObj, err := SomeFunction() -// a.EqualError(err, expectedErrorString) -func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return EqualError(a.t, theError, errString, msgAndArgs...) -} - -// EqualErrorf asserts that a function returned a non-nil error (i.e. an error) -// and that it is equal to the provided error. -// -// actualObj, err := SomeFunction() -// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted") -func (a *Assertions) EqualErrorf(theError error, errString string, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return EqualErrorf(a.t, theError, errString, msg, args...) -} - -// EqualExportedValues asserts that the types of two objects are equal and their public -// fields are also equal. This is useful for comparing structs that have private fields -// that could potentially differ. -// -// type S struct { -// Exported int -// notExported int -// } -// a.EqualExportedValues(S{1, 2}, S{1, 3}) => true -// a.EqualExportedValues(S{1, 2}, S{2, 3}) => false -func (a *Assertions) EqualExportedValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return EqualExportedValues(a.t, expected, actual, msgAndArgs...) -} - -// EqualExportedValuesf asserts that the types of two objects are equal and their public -// fields are also equal. This is useful for comparing structs that have private fields -// that could potentially differ. -// -// type S struct { -// Exported int -// notExported int -// } -// a.EqualExportedValuesf(S{1, 2}, S{1, 3}, "error message %s", "formatted") => true -// a.EqualExportedValuesf(S{1, 2}, S{2, 3}, "error message %s", "formatted") => false -func (a *Assertions) EqualExportedValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return EqualExportedValuesf(a.t, expected, actual, msg, args...) -} - -// EqualValues asserts that two objects are equal or convertible to the larger -// type and equal. -// -// a.EqualValues(uint32(123), int32(123)) -func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return EqualValues(a.t, expected, actual, msgAndArgs...) -} - -// EqualValuesf asserts that two objects are equal or convertible to the larger -// type and equal. -// -// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") -func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return EqualValuesf(a.t, expected, actual, msg, args...) -} - -// Equalf asserts that two objects are equal. -// -// a.Equalf(123, 123, "error message %s", "formatted") -// -// Pointer variable equality is determined based on the equality of the -// referenced values (as opposed to the memory addresses). Function equality -// cannot be determined and will always fail. -func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Equalf(a.t, expected, actual, msg, args...) -} - -// Error asserts that a function returned a non-nil error (ie. an error). -// -// actualObj, err := SomeFunction() -// a.Error(err) -func (a *Assertions) Error(err error, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Error(a.t, err, msgAndArgs...) -} - -// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. -// This is a wrapper for errors.As. -func (a *Assertions) ErrorAs(err error, target interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return ErrorAs(a.t, err, target, msgAndArgs...) -} - -// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. -// This is a wrapper for errors.As. -func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return ErrorAsf(a.t, err, target, msg, args...) -} - -// ErrorContains asserts that a function returned a non-nil error (i.e. an -// error) and that the error contains the specified substring. -// -// actualObj, err := SomeFunction() -// a.ErrorContains(err, expectedErrorSubString) -func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return ErrorContains(a.t, theError, contains, msgAndArgs...) -} - -// ErrorContainsf asserts that a function returned a non-nil error (i.e. an -// error) and that the error contains the specified substring. -// -// actualObj, err := SomeFunction() -// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") -func (a *Assertions) ErrorContainsf(theError error, contains string, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return ErrorContainsf(a.t, theError, contains, msg, args...) -} - -// ErrorIs asserts that at least one of the errors in err's chain matches target. -// This is a wrapper for errors.Is. -func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return ErrorIs(a.t, err, target, msgAndArgs...) -} - -// ErrorIsf asserts that at least one of the errors in err's chain matches target. -// This is a wrapper for errors.Is. -func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return ErrorIsf(a.t, err, target, msg, args...) -} - -// Errorf asserts that a function returned a non-nil error (ie. an error). -// -// actualObj, err := SomeFunction() -// a.Errorf(err, "error message %s", "formatted") -func (a *Assertions) Errorf(err error, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Errorf(a.t, err, msg, args...) -} - -// Eventually asserts that given condition will be met in waitFor time, -// periodically checking target function each tick. -// -// a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond) -func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Eventually(a.t, condition, waitFor, tick, msgAndArgs...) -} - -// EventuallyWithT asserts that given condition will be met in waitFor time, -// periodically checking target function each tick. In contrast to Eventually, -// it supplies a CollectT to the condition function, so that the condition -// function can use the CollectT to call other assertions. -// The condition is considered "met" if no errors are raised in a tick. -// The supplied CollectT collects all errors from one tick (if there are any). -// If the condition is not met before waitFor, the collected errors of -// the last tick are copied to t. -// -// externalValue := false -// go func() { -// time.Sleep(8*time.Second) -// externalValue = true -// }() -// a.EventuallyWithT(func(c *assert.CollectT) { -// // add assertions as needed; any assertion failure will fail the current tick -// assert.True(c, externalValue, "expected 'externalValue' to be true") -// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") -func (a *Assertions) EventuallyWithT(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return EventuallyWithT(a.t, condition, waitFor, tick, msgAndArgs...) -} - -// EventuallyWithTf asserts that given condition will be met in waitFor time, -// periodically checking target function each tick. In contrast to Eventually, -// it supplies a CollectT to the condition function, so that the condition -// function can use the CollectT to call other assertions. -// The condition is considered "met" if no errors are raised in a tick. -// The supplied CollectT collects all errors from one tick (if there are any). -// If the condition is not met before waitFor, the collected errors of -// the last tick are copied to t. -// -// externalValue := false -// go func() { -// time.Sleep(8*time.Second) -// externalValue = true -// }() -// a.EventuallyWithTf(func(c *assert.CollectT) { -// // add assertions as needed; any assertion failure will fail the current tick -// assert.True(c, externalValue, "expected 'externalValue' to be true") -// }, 10*time.Second, 1*time.Second, "error message %s", "formatted") -func (a *Assertions) EventuallyWithTf(condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return EventuallyWithTf(a.t, condition, waitFor, tick, msg, args...) -} - -// Eventuallyf asserts that given condition will be met in waitFor time, -// periodically checking target function each tick. -// -// a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") -func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Eventuallyf(a.t, condition, waitFor, tick, msg, args...) -} - -// Exactly asserts that two objects are equal in value and type. -// -// a.Exactly(int32(123), int64(123)) -func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Exactly(a.t, expected, actual, msgAndArgs...) -} - -// Exactlyf asserts that two objects are equal in value and type. -// -// a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") -func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Exactlyf(a.t, expected, actual, msg, args...) -} - -// Fail reports a failure through -func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Fail(a.t, failureMessage, msgAndArgs...) -} - -// FailNow fails test -func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return FailNow(a.t, failureMessage, msgAndArgs...) -} - -// FailNowf fails test -func (a *Assertions) FailNowf(failureMessage string, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return FailNowf(a.t, failureMessage, msg, args...) -} - -// Failf reports a failure through -func (a *Assertions) Failf(failureMessage string, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Failf(a.t, failureMessage, msg, args...) -} - -// False asserts that the specified value is false. -// -// a.False(myBool) -func (a *Assertions) False(value bool, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return False(a.t, value, msgAndArgs...) -} - -// Falsef asserts that the specified value is false. -// -// a.Falsef(myBool, "error message %s", "formatted") -func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Falsef(a.t, value, msg, args...) -} - -// FileExists checks whether a file exists in the given path. It also fails if -// the path points to a directory or there is an error when trying to check the file. -func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return FileExists(a.t, path, msgAndArgs...) -} - -// FileExistsf checks whether a file exists in the given path. It also fails if -// the path points to a directory or there is an error when trying to check the file. -func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return FileExistsf(a.t, path, msg, args...) -} - -// Greater asserts that the first element is greater than the second -// -// a.Greater(2, 1) -// a.Greater(float64(2), float64(1)) -// a.Greater("b", "a") -func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Greater(a.t, e1, e2, msgAndArgs...) -} - -// GreaterOrEqual asserts that the first element is greater than or equal to the second -// -// a.GreaterOrEqual(2, 1) -// a.GreaterOrEqual(2, 2) -// a.GreaterOrEqual("b", "a") -// a.GreaterOrEqual("b", "b") -func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return GreaterOrEqual(a.t, e1, e2, msgAndArgs...) -} - -// GreaterOrEqualf asserts that the first element is greater than or equal to the second -// -// a.GreaterOrEqualf(2, 1, "error message %s", "formatted") -// a.GreaterOrEqualf(2, 2, "error message %s", "formatted") -// a.GreaterOrEqualf("b", "a", "error message %s", "formatted") -// a.GreaterOrEqualf("b", "b", "error message %s", "formatted") -func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return GreaterOrEqualf(a.t, e1, e2, msg, args...) -} - -// Greaterf asserts that the first element is greater than the second -// -// a.Greaterf(2, 1, "error message %s", "formatted") -// a.Greaterf(float64(2), float64(1), "error message %s", "formatted") -// a.Greaterf("b", "a", "error message %s", "formatted") -func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Greaterf(a.t, e1, e2, msg, args...) -} - -// HTTPBodyContains asserts that a specified handler returns a -// body that contains a string. -// -// a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return HTTPBodyContains(a.t, handler, method, url, values, str, msgAndArgs...) -} - -// HTTPBodyContainsf asserts that a specified handler returns a -// body that contains a string. -// -// a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return HTTPBodyContainsf(a.t, handler, method, url, values, str, msg, args...) -} - -// HTTPBodyNotContains asserts that a specified handler returns a -// body that does not contain a string. -// -// a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return HTTPBodyNotContains(a.t, handler, method, url, values, str, msgAndArgs...) -} - -// HTTPBodyNotContainsf asserts that a specified handler returns a -// body that does not contain a string. -// -// a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return HTTPBodyNotContainsf(a.t, handler, method, url, values, str, msg, args...) -} - -// HTTPError asserts that a specified handler returns an error status code. -// -// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return HTTPError(a.t, handler, method, url, values, msgAndArgs...) -} - -// HTTPErrorf asserts that a specified handler returns an error status code. -// -// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return HTTPErrorf(a.t, handler, method, url, values, msg, args...) -} - -// HTTPRedirect asserts that a specified handler returns a redirect status code. -// -// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return HTTPRedirect(a.t, handler, method, url, values, msgAndArgs...) -} - -// HTTPRedirectf asserts that a specified handler returns a redirect status code. -// -// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return HTTPRedirectf(a.t, handler, method, url, values, msg, args...) -} - -// HTTPStatusCode asserts that a specified handler returns a specified status code. -// -// a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return HTTPStatusCode(a.t, handler, method, url, values, statuscode, msgAndArgs...) -} - -// HTTPStatusCodef asserts that a specified handler returns a specified status code. -// -// a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return HTTPStatusCodef(a.t, handler, method, url, values, statuscode, msg, args...) -} - -// HTTPSuccess asserts that a specified handler returns a success status code. -// -// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return HTTPSuccess(a.t, handler, method, url, values, msgAndArgs...) -} - -// HTTPSuccessf asserts that a specified handler returns a success status code. -// -// a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") -// -// Returns whether the assertion was successful (true) or not (false). -func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return HTTPSuccessf(a.t, handler, method, url, values, msg, args...) -} - -// Implements asserts that an object is implemented by the specified interface. -// -// a.Implements((*MyInterface)(nil), new(MyObject)) -func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Implements(a.t, interfaceObject, object, msgAndArgs...) -} - -// Implementsf asserts that an object is implemented by the specified interface. -// -// a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") -func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Implementsf(a.t, interfaceObject, object, msg, args...) -} - -// InDelta asserts that the two numerals are within delta of each other. -// -// a.InDelta(math.Pi, 22/7.0, 0.01) -func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return InDelta(a.t, expected, actual, delta, msgAndArgs...) -} - -// InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. -func (a *Assertions) InDeltaMapValues(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return InDeltaMapValues(a.t, expected, actual, delta, msgAndArgs...) -} - -// InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. -func (a *Assertions) InDeltaMapValuesf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return InDeltaMapValuesf(a.t, expected, actual, delta, msg, args...) -} - -// InDeltaSlice is the same as InDelta, except it compares two slices. -func (a *Assertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return InDeltaSlice(a.t, expected, actual, delta, msgAndArgs...) -} - -// InDeltaSlicef is the same as InDelta, except it compares two slices. -func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return InDeltaSlicef(a.t, expected, actual, delta, msg, args...) -} - -// InDeltaf asserts that the two numerals are within delta of each other. -// -// a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") -func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return InDeltaf(a.t, expected, actual, delta, msg, args...) -} - -// InEpsilon asserts that expected and actual have a relative error less than epsilon -func (a *Assertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...) -} - -// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. -func (a *Assertions) InEpsilonSlice(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return InEpsilonSlice(a.t, expected, actual, epsilon, msgAndArgs...) -} - -// InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices. -func (a *Assertions) InEpsilonSlicef(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return InEpsilonSlicef(a.t, expected, actual, epsilon, msg, args...) -} - -// InEpsilonf asserts that expected and actual have a relative error less than epsilon -func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return InEpsilonf(a.t, expected, actual, epsilon, msg, args...) -} - -// IsDecreasing asserts that the collection is decreasing -// -// a.IsDecreasing([]int{2, 1, 0}) -// a.IsDecreasing([]float{2, 1}) -// a.IsDecreasing([]string{"b", "a"}) -func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return IsDecreasing(a.t, object, msgAndArgs...) -} - -// IsDecreasingf asserts that the collection is decreasing -// -// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") -// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") -// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") -func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return IsDecreasingf(a.t, object, msg, args...) -} - -// IsIncreasing asserts that the collection is increasing -// -// a.IsIncreasing([]int{1, 2, 3}) -// a.IsIncreasing([]float{1, 2}) -// a.IsIncreasing([]string{"a", "b"}) -func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return IsIncreasing(a.t, object, msgAndArgs...) -} - -// IsIncreasingf asserts that the collection is increasing -// -// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") -// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") -// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") -func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return IsIncreasingf(a.t, object, msg, args...) -} - -// IsNonDecreasing asserts that the collection is not decreasing -// -// a.IsNonDecreasing([]int{1, 1, 2}) -// a.IsNonDecreasing([]float{1, 2}) -// a.IsNonDecreasing([]string{"a", "b"}) -func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return IsNonDecreasing(a.t, object, msgAndArgs...) -} - -// IsNonDecreasingf asserts that the collection is not decreasing -// -// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") -// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") -// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") -func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return IsNonDecreasingf(a.t, object, msg, args...) -} - -// IsNonIncreasing asserts that the collection is not increasing -// -// a.IsNonIncreasing([]int{2, 1, 1}) -// a.IsNonIncreasing([]float{2, 1}) -// a.IsNonIncreasing([]string{"b", "a"}) -func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return IsNonIncreasing(a.t, object, msgAndArgs...) -} - -// IsNonIncreasingf asserts that the collection is not increasing -// -// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") -// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") -// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") -func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return IsNonIncreasingf(a.t, object, msg, args...) -} - -// IsNotType asserts that the specified objects are not of the same type. -// -// a.IsNotType(&NotMyStruct{}, &MyStruct{}) -func (a *Assertions) IsNotType(theType interface{}, object interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return IsNotType(a.t, theType, object, msgAndArgs...) -} - -// IsNotTypef asserts that the specified objects are not of the same type. -// -// a.IsNotTypef(&NotMyStruct{}, &MyStruct{}, "error message %s", "formatted") -func (a *Assertions) IsNotTypef(theType interface{}, object interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return IsNotTypef(a.t, theType, object, msg, args...) -} - -// IsType asserts that the specified objects are of the same type. -// -// a.IsType(&MyStruct{}, &MyStruct{}) -func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return IsType(a.t, expectedType, object, msgAndArgs...) -} - -// IsTypef asserts that the specified objects are of the same type. -// -// a.IsTypef(&MyStruct{}, &MyStruct{}, "error message %s", "formatted") -func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return IsTypef(a.t, expectedType, object, msg, args...) -} - -// JSONEq asserts that two JSON strings are equivalent. -// -// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) -func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return JSONEq(a.t, expected, actual, msgAndArgs...) -} - -// JSONEqf asserts that two JSON strings are equivalent. -// -// a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") -func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return JSONEqf(a.t, expected, actual, msg, args...) -} - -// Len asserts that the specified object has specific length. -// Len also fails if the object has a type that len() not accept. -// -// a.Len(mySlice, 3) -func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Len(a.t, object, length, msgAndArgs...) -} - -// Lenf asserts that the specified object has specific length. -// Lenf also fails if the object has a type that len() not accept. -// -// a.Lenf(mySlice, 3, "error message %s", "formatted") -func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Lenf(a.t, object, length, msg, args...) -} - -// Less asserts that the first element is less than the second -// -// a.Less(1, 2) -// a.Less(float64(1), float64(2)) -// a.Less("a", "b") -func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Less(a.t, e1, e2, msgAndArgs...) -} - -// LessOrEqual asserts that the first element is less than or equal to the second -// -// a.LessOrEqual(1, 2) -// a.LessOrEqual(2, 2) -// a.LessOrEqual("a", "b") -// a.LessOrEqual("b", "b") -func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return LessOrEqual(a.t, e1, e2, msgAndArgs...) -} - -// LessOrEqualf asserts that the first element is less than or equal to the second -// -// a.LessOrEqualf(1, 2, "error message %s", "formatted") -// a.LessOrEqualf(2, 2, "error message %s", "formatted") -// a.LessOrEqualf("a", "b", "error message %s", "formatted") -// a.LessOrEqualf("b", "b", "error message %s", "formatted") -func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return LessOrEqualf(a.t, e1, e2, msg, args...) -} - -// Lessf asserts that the first element is less than the second -// -// a.Lessf(1, 2, "error message %s", "formatted") -// a.Lessf(float64(1), float64(2), "error message %s", "formatted") -// a.Lessf("a", "b", "error message %s", "formatted") -func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Lessf(a.t, e1, e2, msg, args...) -} - -// Negative asserts that the specified element is negative -// -// a.Negative(-1) -// a.Negative(-1.23) -func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Negative(a.t, e, msgAndArgs...) -} - -// Negativef asserts that the specified element is negative -// -// a.Negativef(-1, "error message %s", "formatted") -// a.Negativef(-1.23, "error message %s", "formatted") -func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Negativef(a.t, e, msg, args...) -} - -// Never asserts that the given condition doesn't satisfy in waitFor time, -// periodically checking the target function each tick. -// -// a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) -func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Never(a.t, condition, waitFor, tick, msgAndArgs...) -} - -// Neverf asserts that the given condition doesn't satisfy in waitFor time, -// periodically checking the target function each tick. -// -// a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") -func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Neverf(a.t, condition, waitFor, tick, msg, args...) -} - -// Nil asserts that the specified object is nil. -// -// a.Nil(err) -func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Nil(a.t, object, msgAndArgs...) -} - -// Nilf asserts that the specified object is nil. -// -// a.Nilf(err, "error message %s", "formatted") -func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Nilf(a.t, object, msg, args...) -} - -// NoDirExists checks whether a directory does not exist in the given path. -// It fails if the path points to an existing _directory_ only. -func (a *Assertions) NoDirExists(path string, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NoDirExists(a.t, path, msgAndArgs...) -} - -// NoDirExistsf checks whether a directory does not exist in the given path. -// It fails if the path points to an existing _directory_ only. -func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NoDirExistsf(a.t, path, msg, args...) -} - -// NoError asserts that a function returned a nil error (ie. no error). -// -// actualObj, err := SomeFunction() -// if a.NoError(err) { -// assert.Equal(t, expectedObj, actualObj) -// } -func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NoError(a.t, err, msgAndArgs...) -} - -// NoErrorf asserts that a function returned a nil error (ie. no error). -// -// actualObj, err := SomeFunction() -// if a.NoErrorf(err, "error message %s", "formatted") { -// assert.Equal(t, expectedObj, actualObj) -// } -func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NoErrorf(a.t, err, msg, args...) -} - -// NoFileExists checks whether a file does not exist in a given path. It fails -// if the path points to an existing _file_ only. -func (a *Assertions) NoFileExists(path string, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NoFileExists(a.t, path, msgAndArgs...) -} - -// NoFileExistsf checks whether a file does not exist in a given path. It fails -// if the path points to an existing _file_ only. -func (a *Assertions) NoFileExistsf(path string, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NoFileExistsf(a.t, path, msg, args...) -} - -// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the -// specified substring or element. -// -// a.NotContains("Hello World", "Earth") -// a.NotContains(["Hello", "World"], "Earth") -// a.NotContains({"Hello": "World"}, "Earth") -func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NotContains(a.t, s, contains, msgAndArgs...) -} - -// NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the -// specified substring or element. -// -// a.NotContainsf("Hello World", "Earth", "error message %s", "formatted") -// a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted") -// a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted") -func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NotContainsf(a.t, s, contains, msg, args...) -} - -// NotElementsMatch asserts that the specified listA(array, slice...) is NOT equal to specified -// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, -// the number of appearances of each of them in both lists should not match. -// This is an inverse of ElementsMatch. -// -// a.NotElementsMatch([1, 1, 2, 3], [1, 1, 2, 3]) -> false -// -// a.NotElementsMatch([1, 1, 2, 3], [1, 2, 3]) -> true -// -// a.NotElementsMatch([1, 2, 3], [1, 2, 4]) -> true -func (a *Assertions) NotElementsMatch(listA interface{}, listB interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NotElementsMatch(a.t, listA, listB, msgAndArgs...) -} - -// NotElementsMatchf asserts that the specified listA(array, slice...) is NOT equal to specified -// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, -// the number of appearances of each of them in both lists should not match. -// This is an inverse of ElementsMatch. -// -// a.NotElementsMatchf([1, 1, 2, 3], [1, 1, 2, 3], "error message %s", "formatted") -> false -// -// a.NotElementsMatchf([1, 1, 2, 3], [1, 2, 3], "error message %s", "formatted") -> true -// -// a.NotElementsMatchf([1, 2, 3], [1, 2, 4], "error message %s", "formatted") -> true -func (a *Assertions) NotElementsMatchf(listA interface{}, listB interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NotElementsMatchf(a.t, listA, listB, msg, args...) -} - -// NotEmpty asserts that the specified object is NOT [Empty]. -// -// if a.NotEmpty(obj) { -// assert.Equal(t, "two", obj[1]) -// } -func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NotEmpty(a.t, object, msgAndArgs...) -} - -// NotEmptyf asserts that the specified object is NOT [Empty]. -// -// if a.NotEmptyf(obj, "error message %s", "formatted") { -// assert.Equal(t, "two", obj[1]) -// } -func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NotEmptyf(a.t, object, msg, args...) -} - -// NotEqual asserts that the specified values are NOT equal. -// -// a.NotEqual(obj1, obj2) -// -// Pointer variable equality is determined based on the equality of the -// referenced values (as opposed to the memory addresses). -func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NotEqual(a.t, expected, actual, msgAndArgs...) -} - -// NotEqualValues asserts that two objects are not equal even when converted to the same type -// -// a.NotEqualValues(obj1, obj2) -func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NotEqualValues(a.t, expected, actual, msgAndArgs...) -} - -// NotEqualValuesf asserts that two objects are not equal even when converted to the same type -// -// a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") -func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NotEqualValuesf(a.t, expected, actual, msg, args...) -} - -// NotEqualf asserts that the specified values are NOT equal. -// -// a.NotEqualf(obj1, obj2, "error message %s", "formatted") -// -// Pointer variable equality is determined based on the equality of the -// referenced values (as opposed to the memory addresses). -func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NotEqualf(a.t, expected, actual, msg, args...) -} - -// NotErrorAs asserts that none of the errors in err's chain matches target, -// but if so, sets target to that error value. -func (a *Assertions) NotErrorAs(err error, target interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NotErrorAs(a.t, err, target, msgAndArgs...) -} - -// NotErrorAsf asserts that none of the errors in err's chain matches target, -// but if so, sets target to that error value. -func (a *Assertions) NotErrorAsf(err error, target interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NotErrorAsf(a.t, err, target, msg, args...) -} - -// NotErrorIs asserts that none of the errors in err's chain matches target. -// This is a wrapper for errors.Is. -func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NotErrorIs(a.t, err, target, msgAndArgs...) -} - -// NotErrorIsf asserts that none of the errors in err's chain matches target. -// This is a wrapper for errors.Is. -func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NotErrorIsf(a.t, err, target, msg, args...) -} - -// NotImplements asserts that an object does not implement the specified interface. -// -// a.NotImplements((*MyInterface)(nil), new(MyObject)) -func (a *Assertions) NotImplements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NotImplements(a.t, interfaceObject, object, msgAndArgs...) -} - -// NotImplementsf asserts that an object does not implement the specified interface. -// -// a.NotImplementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") -func (a *Assertions) NotImplementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NotImplementsf(a.t, interfaceObject, object, msg, args...) -} - -// NotNil asserts that the specified object is not nil. -// -// a.NotNil(err) -func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NotNil(a.t, object, msgAndArgs...) -} - -// NotNilf asserts that the specified object is not nil. -// -// a.NotNilf(err, "error message %s", "formatted") -func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NotNilf(a.t, object, msg, args...) -} - -// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. -// -// a.NotPanics(func(){ RemainCalm() }) -func (a *Assertions) NotPanics(f PanicTestFunc, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NotPanics(a.t, f, msgAndArgs...) -} - -// NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. -// -// a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted") -func (a *Assertions) NotPanicsf(f PanicTestFunc, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NotPanicsf(a.t, f, msg, args...) -} - -// NotRegexp asserts that a specified regexp does not match a string. -// -// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") -// a.NotRegexp("^start", "it's not starting") -func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NotRegexp(a.t, rx, str, msgAndArgs...) -} - -// NotRegexpf asserts that a specified regexp does not match a string. -// -// a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") -// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") -func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NotRegexpf(a.t, rx, str, msg, args...) -} - -// NotSame asserts that two pointers do not reference the same object. -// -// a.NotSame(ptr1, ptr2) -// -// Both arguments must be pointer variables. Pointer variable sameness is -// determined based on the equality of both type and value. -func (a *Assertions) NotSame(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NotSame(a.t, expected, actual, msgAndArgs...) -} - -// NotSamef asserts that two pointers do not reference the same object. -// -// a.NotSamef(ptr1, ptr2, "error message %s", "formatted") -// -// Both arguments must be pointer variables. Pointer variable sameness is -// determined based on the equality of both type and value. -func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NotSamef(a.t, expected, actual, msg, args...) -} - -// NotSubset asserts that the list (array, slice, or map) does NOT contain all -// elements given in the subset (array, slice, or map). -// Map elements are key-value pairs unless compared with an array or slice where -// only the map key is evaluated. -// -// a.NotSubset([1, 3, 4], [1, 2]) -// a.NotSubset({"x": 1, "y": 2}, {"z": 3}) -// a.NotSubset([1, 3, 4], {1: "one", 2: "two"}) -// a.NotSubset({"x": 1, "y": 2}, ["z"]) -func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NotSubset(a.t, list, subset, msgAndArgs...) -} - -// NotSubsetf asserts that the list (array, slice, or map) does NOT contain all -// elements given in the subset (array, slice, or map). -// Map elements are key-value pairs unless compared with an array or slice where -// only the map key is evaluated. -// -// a.NotSubsetf([1, 3, 4], [1, 2], "error message %s", "formatted") -// a.NotSubsetf({"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") -// a.NotSubsetf([1, 3, 4], {1: "one", 2: "two"}, "error message %s", "formatted") -// a.NotSubsetf({"x": 1, "y": 2}, ["z"], "error message %s", "formatted") -func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NotSubsetf(a.t, list, subset, msg, args...) -} - -// NotZero asserts that i is not the zero value for its type. -func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NotZero(a.t, i, msgAndArgs...) -} - -// NotZerof asserts that i is not the zero value for its type. -func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return NotZerof(a.t, i, msg, args...) -} - -// Panics asserts that the code inside the specified PanicTestFunc panics. -// -// a.Panics(func(){ GoCrazy() }) -func (a *Assertions) Panics(f PanicTestFunc, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Panics(a.t, f, msgAndArgs...) -} - -// PanicsWithError asserts that the code inside the specified PanicTestFunc -// panics, and that the recovered panic value is an error that satisfies the -// EqualError comparison. -// -// a.PanicsWithError("crazy error", func(){ GoCrazy() }) -func (a *Assertions) PanicsWithError(errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return PanicsWithError(a.t, errString, f, msgAndArgs...) -} - -// PanicsWithErrorf asserts that the code inside the specified PanicTestFunc -// panics, and that the recovered panic value is an error that satisfies the -// EqualError comparison. -// -// a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") -func (a *Assertions) PanicsWithErrorf(errString string, f PanicTestFunc, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return PanicsWithErrorf(a.t, errString, f, msg, args...) -} - -// PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that -// the recovered panic value equals the expected panic value. -// -// a.PanicsWithValue("crazy error", func(){ GoCrazy() }) -func (a *Assertions) PanicsWithValue(expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return PanicsWithValue(a.t, expected, f, msgAndArgs...) -} - -// PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that -// the recovered panic value equals the expected panic value. -// -// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") -func (a *Assertions) PanicsWithValuef(expected interface{}, f PanicTestFunc, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return PanicsWithValuef(a.t, expected, f, msg, args...) -} - -// Panicsf asserts that the code inside the specified PanicTestFunc panics. -// -// a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted") -func (a *Assertions) Panicsf(f PanicTestFunc, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Panicsf(a.t, f, msg, args...) -} - -// Positive asserts that the specified element is positive -// -// a.Positive(1) -// a.Positive(1.23) -func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Positive(a.t, e, msgAndArgs...) -} - -// Positivef asserts that the specified element is positive -// -// a.Positivef(1, "error message %s", "formatted") -// a.Positivef(1.23, "error message %s", "formatted") -func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Positivef(a.t, e, msg, args...) -} - -// Regexp asserts that a specified regexp matches a string. -// -// a.Regexp(regexp.MustCompile("start"), "it's starting") -// a.Regexp("start...$", "it's not starting") -func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Regexp(a.t, rx, str, msgAndArgs...) -} - -// Regexpf asserts that a specified regexp matches a string. -// -// a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") -// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") -func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Regexpf(a.t, rx, str, msg, args...) -} - -// Same asserts that two pointers reference the same object. -// -// a.Same(ptr1, ptr2) -// -// Both arguments must be pointer variables. Pointer variable sameness is -// determined based on the equality of both type and value. -func (a *Assertions) Same(expected interface{}, actual interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Same(a.t, expected, actual, msgAndArgs...) -} - -// Samef asserts that two pointers reference the same object. -// -// a.Samef(ptr1, ptr2, "error message %s", "formatted") -// -// Both arguments must be pointer variables. Pointer variable sameness is -// determined based on the equality of both type and value. -func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Samef(a.t, expected, actual, msg, args...) -} - -// Subset asserts that the list (array, slice, or map) contains all elements -// given in the subset (array, slice, or map). -// Map elements are key-value pairs unless compared with an array or slice where -// only the map key is evaluated. -// -// a.Subset([1, 2, 3], [1, 2]) -// a.Subset({"x": 1, "y": 2}, {"x": 1}) -// a.Subset([1, 2, 3], {1: "one", 2: "two"}) -// a.Subset({"x": 1, "y": 2}, ["x"]) -func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Subset(a.t, list, subset, msgAndArgs...) -} - -// Subsetf asserts that the list (array, slice, or map) contains all elements -// given in the subset (array, slice, or map). -// Map elements are key-value pairs unless compared with an array or slice where -// only the map key is evaluated. -// -// a.Subsetf([1, 2, 3], [1, 2], "error message %s", "formatted") -// a.Subsetf({"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") -// a.Subsetf([1, 2, 3], {1: "one", 2: "two"}, "error message %s", "formatted") -// a.Subsetf({"x": 1, "y": 2}, ["x"], "error message %s", "formatted") -func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Subsetf(a.t, list, subset, msg, args...) -} - -// True asserts that the specified value is true. -// -// a.True(myBool) -func (a *Assertions) True(value bool, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return True(a.t, value, msgAndArgs...) -} - -// Truef asserts that the specified value is true. -// -// a.Truef(myBool, "error message %s", "formatted") -func (a *Assertions) Truef(value bool, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Truef(a.t, value, msg, args...) -} - -// WithinDuration asserts that the two times are within duration delta of each other. -// -// a.WithinDuration(time.Now(), time.Now(), 10*time.Second) -func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return WithinDuration(a.t, expected, actual, delta, msgAndArgs...) -} - -// WithinDurationf asserts that the two times are within duration delta of each other. -// -// a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") -func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return WithinDurationf(a.t, expected, actual, delta, msg, args...) -} - -// WithinRange asserts that a time is within a time range (inclusive). -// -// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) -func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return WithinRange(a.t, actual, start, end, msgAndArgs...) -} - -// WithinRangef asserts that a time is within a time range (inclusive). -// -// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") -func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return WithinRangef(a.t, actual, start, end, msg, args...) -} - -// YAMLEq asserts that the first documents in the two YAML strings are equivalent. -// -// expected := `--- -// key: value -// --- -// key: this is a second document, it is not evaluated -// ` -// actual := `--- -// key: value -// --- -// key: this is a subsequent document, it is not evaluated -// ` -// a.YAMLEq(expected, actual) -func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return YAMLEq(a.t, expected, actual, msgAndArgs...) -} - -// YAMLEqf asserts that the first documents in the two YAML strings are equivalent. -// -// expected := `--- -// key: value -// --- -// key: this is a second document, it is not evaluated -// ` -// actual := `--- -// key: value -// --- -// key: this is a subsequent document, it is not evaluated -// ` -// a.YAMLEqf(expected, actual, "error message %s", "formatted") -func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return YAMLEqf(a.t, expected, actual, msg, args...) -} - -// Zero asserts that i is the zero value for its type. -func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Zero(a.t, i, msgAndArgs...) -} - -// Zerof asserts that i is the zero value for its type. -func (a *Assertions) Zerof(i interface{}, msg string, args ...interface{}) bool { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - return Zerof(a.t, i, msg, args...) -} diff --git a/vendor/github.com/stretchr/testify/assert/assertion_forward.go.tmpl b/vendor/github.com/stretchr/testify/assert/assertion_forward.go.tmpl deleted file mode 100644 index 188bb9e17..000000000 --- a/vendor/github.com/stretchr/testify/assert/assertion_forward.go.tmpl +++ /dev/null @@ -1,5 +0,0 @@ -{{.CommentWithoutT "a"}} -func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) bool { - if h, ok := a.t.(tHelper); ok { h.Helper() } - return {{.DocInfo.Name}}(a.t, {{.ForwardedParams}}) -} diff --git a/vendor/github.com/stretchr/testify/assert/assertion_order.go b/vendor/github.com/stretchr/testify/assert/assertion_order.go deleted file mode 100644 index a44b40ed3..000000000 --- a/vendor/github.com/stretchr/testify/assert/assertion_order.go +++ /dev/null @@ -1,93 +0,0 @@ -package assert - -import ( - "fmt" - "reflect" -) - -// isOrdered checks that collection contains orderable elements. -func isOrdered(t TestingT, object interface{}, allowedComparesResults []compareResult, failMessage string, msgAndArgs ...interface{}) bool { - objKind := reflect.TypeOf(object).Kind() - if objKind != reflect.Slice && objKind != reflect.Array { - return Fail(t, fmt.Sprintf("object %T is not an ordered collection", object), msgAndArgs...) - } - - objValue := reflect.ValueOf(object) - objLen := objValue.Len() - - if objLen <= 1 { - return true - } - - value := objValue.Index(0) - valueInterface := value.Interface() - firstValueKind := value.Kind() - - for i := 1; i < objLen; i++ { - prevValue := value - prevValueInterface := valueInterface - - value = objValue.Index(i) - valueInterface = value.Interface() - - compareResult, isComparable := compare(prevValueInterface, valueInterface, firstValueKind) - - if !isComparable { - return Fail(t, fmt.Sprintf(`Can not compare type "%T" and "%T"`, value, prevValue), msgAndArgs...) - } - - if !containsValue(allowedComparesResults, compareResult) { - return Fail(t, fmt.Sprintf(failMessage, prevValue, value), msgAndArgs...) - } - } - - return true -} - -// IsIncreasing asserts that the collection is increasing -// -// assert.IsIncreasing(t, []int{1, 2, 3}) -// assert.IsIncreasing(t, []float{1, 2}) -// assert.IsIncreasing(t, []string{"a", "b"}) -func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return isOrdered(t, object, []compareResult{compareLess}, "\"%v\" is not less than \"%v\"", msgAndArgs...) -} - -// IsNonIncreasing asserts that the collection is not increasing -// -// assert.IsNonIncreasing(t, []int{2, 1, 1}) -// assert.IsNonIncreasing(t, []float{2, 1}) -// assert.IsNonIncreasing(t, []string{"b", "a"}) -func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return isOrdered(t, object, []compareResult{compareEqual, compareGreater}, "\"%v\" is not greater than or equal to \"%v\"", msgAndArgs...) -} - -// IsDecreasing asserts that the collection is decreasing -// -// assert.IsDecreasing(t, []int{2, 1, 0}) -// assert.IsDecreasing(t, []float{2, 1}) -// assert.IsDecreasing(t, []string{"b", "a"}) -func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return isOrdered(t, object, []compareResult{compareGreater}, "\"%v\" is not greater than \"%v\"", msgAndArgs...) -} - -// IsNonDecreasing asserts that the collection is not decreasing -// -// assert.IsNonDecreasing(t, []int{1, 1, 2}) -// assert.IsNonDecreasing(t, []float{1, 2}) -// assert.IsNonDecreasing(t, []string{"a", "b"}) -func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return isOrdered(t, object, []compareResult{compareLess, compareEqual}, "\"%v\" is not less than or equal to \"%v\"", msgAndArgs...) -} diff --git a/vendor/github.com/stretchr/testify/assert/assertions.go b/vendor/github.com/stretchr/testify/assert/assertions.go deleted file mode 100644 index 166f63726..000000000 --- a/vendor/github.com/stretchr/testify/assert/assertions.go +++ /dev/null @@ -1,2314 +0,0 @@ -package assert - -import ( - "bufio" - "bytes" - "encoding/json" - "errors" - "fmt" - "math" - "os" - "reflect" - "regexp" - "runtime" - "runtime/debug" - "strings" - "time" - "unicode" - "unicode/utf8" - - // Wrapper around go.yaml.in/yaml/v3 - "github.com/stretchr/testify/assert/yaml" - "github.com/stretchr/testify/internal/difflib" - "github.com/stretchr/testify/internal/spew" -) - -//go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_format.go.tmpl" - -// TestingT is an interface wrapper around *testing.T -type TestingT interface { - Errorf(format string, args ...interface{}) -} - -// ComparisonAssertionFunc is a common function prototype when comparing two values. Can be useful -// for table driven tests. -type ComparisonAssertionFunc = func(TestingT, interface{}, interface{}, ...interface{}) bool - -// ValueAssertionFunc is a common function prototype when validating a single value. Can be useful -// for table driven tests. -type ValueAssertionFunc = func(TestingT, interface{}, ...interface{}) bool - -// BoolAssertionFunc is a common function prototype when validating a bool value. Can be useful -// for table driven tests. -type BoolAssertionFunc = func(TestingT, bool, ...interface{}) bool - -// ErrorAssertionFunc is a common function prototype when validating an error value. Can be useful -// for table driven tests. -type ErrorAssertionFunc = func(TestingT, error, ...interface{}) bool - -// PanicAssertionFunc is a common function prototype when validating a panic value. Can be useful -// for table driven tests. -type PanicAssertionFunc = func(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool - -// Comparison is a custom function that returns true on success and false on failure -type Comparison func() (success bool) - -/* - Helper functions -*/ - -// ObjectsAreEqual determines if two objects are considered equal. -// -// This function does no assertion of any kind. -func ObjectsAreEqual(expected, actual interface{}) bool { - if expected == nil || actual == nil { - return expected == actual - } - - exp, ok := expected.([]byte) - if !ok { - return reflect.DeepEqual(expected, actual) - } - - act, ok := actual.([]byte) - if !ok { - return false - } - if exp == nil || act == nil { - return exp == nil && act == nil - } - return bytes.Equal(exp, act) -} - -// copyExportedFields iterates downward through nested data structures and creates a copy -// that only contains the exported struct fields. -func copyExportedFields(expected interface{}) interface{} { - if isNil(expected) { - return expected - } - - expectedType := reflect.TypeOf(expected) - expectedKind := expectedType.Kind() - expectedValue := reflect.ValueOf(expected) - - switch expectedKind { - case reflect.Struct: - result := reflect.New(expectedType).Elem() - for i := 0; i < expectedType.NumField(); i++ { - field := expectedType.Field(i) - isExported := field.IsExported() - if isExported { - fieldValue := expectedValue.Field(i) - if isNil(fieldValue) || isNil(fieldValue.Interface()) { - continue - } - newValue := copyExportedFields(fieldValue.Interface()) - result.Field(i).Set(reflect.ValueOf(newValue)) - } - } - return result.Interface() - - case reflect.Ptr: - result := reflect.New(expectedType.Elem()) - unexportedRemoved := copyExportedFields(expectedValue.Elem().Interface()) - result.Elem().Set(reflect.ValueOf(unexportedRemoved)) - return result.Interface() - - case reflect.Array, reflect.Slice: - var result reflect.Value - if expectedKind == reflect.Array { - result = reflect.New(reflect.ArrayOf(expectedValue.Len(), expectedType.Elem())).Elem() - } else { - result = reflect.MakeSlice(expectedType, expectedValue.Len(), expectedValue.Len()) - } - for i := 0; i < expectedValue.Len(); i++ { - index := expectedValue.Index(i) - if isNil(index) { - continue - } - unexportedRemoved := copyExportedFields(index.Interface()) - result.Index(i).Set(reflect.ValueOf(unexportedRemoved)) - } - return result.Interface() - - case reflect.Map: - result := reflect.MakeMap(expectedType) - for _, k := range expectedValue.MapKeys() { - index := expectedValue.MapIndex(k) - unexportedRemoved := copyExportedFields(index.Interface()) - result.SetMapIndex(k, reflect.ValueOf(unexportedRemoved)) - } - return result.Interface() - - default: - return expected - } -} - -// ObjectsExportedFieldsAreEqual determines if the exported (public) fields of two objects are -// considered equal. This comparison of only exported fields is applied recursively to nested data -// structures. -// -// This function does no assertion of any kind. -// -// Deprecated: Use [EqualExportedValues] instead. -func ObjectsExportedFieldsAreEqual(expected, actual interface{}) bool { - expectedCleaned := copyExportedFields(expected) - actualCleaned := copyExportedFields(actual) - return ObjectsAreEqualValues(expectedCleaned, actualCleaned) -} - -// ObjectsAreEqualValues gets whether two objects are equal, or if their -// values are equal. -func ObjectsAreEqualValues(expected, actual interface{}) bool { - if ObjectsAreEqual(expected, actual) { - return true - } - - expectedValue := reflect.ValueOf(expected) - actualValue := reflect.ValueOf(actual) - if !expectedValue.IsValid() || !actualValue.IsValid() { - return false - } - - expectedType := expectedValue.Type() - actualType := actualValue.Type() - if !expectedType.ConvertibleTo(actualType) { - return false - } - - if !isNumericType(expectedType) || !isNumericType(actualType) { - // Attempt comparison after type conversion - return reflect.DeepEqual( - expectedValue.Convert(actualType).Interface(), actual, - ) - } - - // If BOTH values are numeric, there are chances of false positives due - // to overflow or underflow. So, we need to make sure to always convert - // the smaller type to a larger type before comparing. - if expectedType.Size() >= actualType.Size() { - return actualValue.Convert(expectedType).Interface() == expected - } - - return expectedValue.Convert(actualType).Interface() == actual -} - -// isNumericType returns true if the type is one of: -// int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, -// float32, float64, complex64, complex128 -func isNumericType(t reflect.Type) bool { - return t.Kind() >= reflect.Int && t.Kind() <= reflect.Complex128 -} - -/* CallerInfo is necessary because the assert functions use the testing object -internally, causing it to print the file:line of the assert method, rather than where -the problem actually occurred in calling code.*/ - -// CallerInfo returns an array of strings containing the file and line number -// of each stack frame leading from the current test to the assert call that -// failed. -func CallerInfo() []string { - var pc uintptr - var file string - var line int - var name string - - const stackFrameBufferSize = 10 - pcs := make([]uintptr, stackFrameBufferSize) - - callers := []string{} - offset := 1 - - for { - n := runtime.Callers(offset, pcs) - - if n == 0 { - break - } - - frames := runtime.CallersFrames(pcs[:n]) - - for { - frame, more := frames.Next() - pc = frame.PC - file = frame.File - line = frame.Line - - // This is a huge edge case, but it will panic if this is the case, see #180 - if file == "" { - break - } - - f := runtime.FuncForPC(pc) - if f == nil { - break - } - name = f.Name() - - // testing.tRunner is the standard library function that calls - // tests. Subtests are called directly by tRunner, without going through - // the Test/Benchmark/Example function that contains the t.Run calls, so - // with subtests we should break when we hit tRunner, without adding it - // to the list of callers. - if name == "testing.tRunner" { - break - } - - parts := strings.Split(file, "/") - if len(parts) > 1 { - filename := parts[len(parts)-1] - dir := parts[len(parts)-2] - if (dir != "assert" && dir != "mock" && dir != "require") || filename == "mock_test.go" { - callers = append(callers, fmt.Sprintf("%s:%d", file, line)) - } - } - - // Drop the package - dotPos := strings.LastIndexByte(name, '.') - name = name[dotPos+1:] - if isTest(name, "Test") || - isTest(name, "Benchmark") || - isTest(name, "Example") { - break - } - - if !more { - break - } - } - - // Next batch - offset += cap(pcs) - } - - return callers -} - -// Stolen from the `go test` tool. -// isTest tells whether name looks like a test (or benchmark, according to prefix). -// It is a Test (say) if there is a character after Test that is not a lower-case letter. -// We don't want TesticularCancer. -func isTest(name, prefix string) bool { - if !strings.HasPrefix(name, prefix) { - return false - } - if len(name) == len(prefix) { // "Test" is ok - return true - } - r, _ := utf8.DecodeRuneInString(name[len(prefix):]) - return !unicode.IsLower(r) -} - -func messageFromMsgAndArgs(msgAndArgs ...interface{}) string { - if len(msgAndArgs) == 0 || msgAndArgs == nil { - return "" - } - if len(msgAndArgs) == 1 { - msg := msgAndArgs[0] - if msgAsStr, ok := msg.(string); ok { - return msgAsStr - } - return fmt.Sprintf("%+v", msg) - } - if len(msgAndArgs) > 1 { - return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...) - } - return "" -} - -// Aligns the provided message so that all lines after the first line start at the same location as the first line. -// Assumes that the first line starts at the correct location (after carriage return, tab, label, spacer and tab). -// The longestLabelLen parameter specifies the length of the longest label in the output (required because this is the -// basis on which the alignment occurs). -func indentMessageLines(message string, longestLabelLen int) string { - outBuf := new(bytes.Buffer) - - scanner := bufio.NewScanner(strings.NewReader(message)) - for firstLine := true; scanner.Scan(); firstLine = false { - if !firstLine { - fmt.Fprint(outBuf, "\n\t"+strings.Repeat(" ", longestLabelLen+1)+"\t") - } - fmt.Fprint(outBuf, scanner.Text()) - } - if err := scanner.Err(); err != nil { - return fmt.Sprintf("cannot display message: %s", err) - } - - return outBuf.String() -} - -type failNower interface { - FailNow() -} - -// FailNow fails test -func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - Fail(t, failureMessage, msgAndArgs...) - - // We cannot extend TestingT with FailNow() and - // maintain backwards compatibility, so we fallback - // to panicking when FailNow is not available in - // TestingT. - // See issue #263 - - if t, ok := t.(failNower); ok { - t.FailNow() - } else { - panic("test failed and t is missing `FailNow()`") - } - return false -} - -// Fail reports a failure through -func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - content := []labeledContent{ - {"Error Trace", strings.Join(CallerInfo(), "\n\t\t\t")}, - {"Error", failureMessage}, - } - - // Add test name if the Go version supports it - if n, ok := t.(interface { - Name() string - }); ok { - content = append(content, labeledContent{"Test", n.Name()}) - } - - message := messageFromMsgAndArgs(msgAndArgs...) - if len(message) > 0 { - content = append(content, labeledContent{"Messages", message}) - } - - t.Errorf("\n%s", ""+labeledOutput(content...)) - - return false -} - -type labeledContent struct { - label string - content string -} - -// labeledOutput returns a string consisting of the provided labeledContent. Each labeled output is appended in the following manner: -// -// \t{{label}}:{{align_spaces}}\t{{content}}\n -// -// The initial carriage return is required to undo/erase any padding added by testing.T.Errorf. The "\t{{label}}:" is for the label. -// If a label is shorter than the longest label provided, padding spaces are added to make all the labels match in length. Once this -// alignment is achieved, "\t{{content}}\n" is added for the output. -// -// If the content of the labeledOutput contains line breaks, the subsequent lines are aligned so that they start at the same location as the first line. -func labeledOutput(content ...labeledContent) string { - longestLabel := 0 - for _, v := range content { - if len(v.label) > longestLabel { - longestLabel = len(v.label) - } - } - var output string - for _, v := range content { - output += "\t" + v.label + ":" + strings.Repeat(" ", longestLabel-len(v.label)) + "\t" + indentMessageLines(v.content, longestLabel) + "\n" - } - return output -} - -// Implements asserts that an object is implemented by the specified interface. -// -// assert.Implements(t, (*MyInterface)(nil), new(MyObject)) -func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - interfaceType := reflect.TypeOf(interfaceObject).Elem() - - if object == nil { - return Fail(t, fmt.Sprintf("Cannot check if nil implements %v", interfaceType), msgAndArgs...) - } - if !reflect.TypeOf(object).Implements(interfaceType) { - return Fail(t, fmt.Sprintf("%T must implement %v", object, interfaceType), msgAndArgs...) - } - - return true -} - -// NotImplements asserts that an object does not implement the specified interface. -// -// assert.NotImplements(t, (*MyInterface)(nil), new(MyObject)) -func NotImplements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - interfaceType := reflect.TypeOf(interfaceObject).Elem() - - if object == nil { - return Fail(t, fmt.Sprintf("Cannot check if nil does not implement %v", interfaceType), msgAndArgs...) - } - if reflect.TypeOf(object).Implements(interfaceType) { - return Fail(t, fmt.Sprintf("%T implements %v", object, interfaceType), msgAndArgs...) - } - - return true -} - -func isType(expectedType, object interface{}) bool { - return ObjectsAreEqual(reflect.TypeOf(object), reflect.TypeOf(expectedType)) -} - -// IsType asserts that the specified objects are of the same type. -// -// assert.IsType(t, &MyStruct{}, &MyStruct{}) -func IsType(t TestingT, expectedType, object interface{}, msgAndArgs ...interface{}) bool { - if isType(expectedType, object) { - return true - } - if h, ok := t.(tHelper); ok { - h.Helper() - } - return Fail(t, fmt.Sprintf("Object expected to be of type %T, but was %T", expectedType, object), msgAndArgs...) -} - -// IsNotType asserts that the specified objects are not of the same type. -// -// assert.IsNotType(t, &NotMyStruct{}, &MyStruct{}) -func IsNotType(t TestingT, theType, object interface{}, msgAndArgs ...interface{}) bool { - if !isType(theType, object) { - return true - } - if h, ok := t.(tHelper); ok { - h.Helper() - } - return Fail(t, fmt.Sprintf("Object type expected to be different than %T", theType), msgAndArgs...) -} - -// Equal asserts that two objects are equal. -// -// assert.Equal(t, 123, 123) -// -// Pointer variable equality is determined based on the equality of the -// referenced values (as opposed to the memory addresses). Function equality -// cannot be determined and will always fail. -func Equal(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if err := validateEqualArgs(expected, actual); err != nil { - return Fail(t, fmt.Sprintf("Invalid operation: %#v == %#v (%s)", - expected, actual, err), msgAndArgs...) - } - - if !ObjectsAreEqual(expected, actual) { - diff := diff(expected, actual) - expected, actual = formatUnequalValues(expected, actual) - return Fail(t, fmt.Sprintf("Not equal: \n"+ - "expected: %s\n"+ - "actual : %s%s", expected, actual, diff), msgAndArgs...) - } - - return true -} - -// validateEqualArgs checks whether provided arguments can be safely used in the -// Equal/NotEqual functions. -func validateEqualArgs(expected, actual interface{}) error { - if expected == nil && actual == nil { - return nil - } - - if isFunction(expected) || isFunction(actual) { - return errors.New("cannot take func type as argument") - } - return nil -} - -// Same asserts that two pointers reference the same object. -// -// assert.Same(t, ptr1, ptr2) -// -// Both arguments must be pointer variables. Pointer variable sameness is -// determined based on the equality of both type and value. -func Same(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - - same, ok := samePointers(expected, actual) - if !ok { - return Fail(t, "Both arguments must be pointers", msgAndArgs...) - } - - if !same { - // both are pointers but not the same type & pointing to the same address - return Fail(t, fmt.Sprintf("Not same: \n"+ - "expected: %[2]s (%[1]T)(%[1]p)\n"+ - "actual : %[4]s (%[3]T)(%[3]p)", expected, truncatingFormat("%#v", expected), actual, truncatingFormat("%#v", actual)), msgAndArgs...) - } - - return true -} - -// NotSame asserts that two pointers do not reference the same object. -// -// assert.NotSame(t, ptr1, ptr2) -// -// Both arguments must be pointer variables. Pointer variable sameness is -// determined based on the equality of both type and value. -func NotSame(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - - same, ok := samePointers(expected, actual) - if !ok { - // fails when the arguments are not pointers - return !(Fail(t, "Both arguments must be pointers", msgAndArgs...)) - } - - if same { - return Fail(t, fmt.Sprintf( - "Expected and actual point to the same object: %p %s", - expected, truncatingFormat("%#v", expected)), msgAndArgs...) - } - return true -} - -// samePointers checks if two generic interface objects are pointers of the same -// type pointing to the same object. It returns two values: same indicating if -// they are the same type and point to the same object, and ok indicating that -// both inputs are pointers. -func samePointers(first, second interface{}) (same bool, ok bool) { - firstPtr, secondPtr := reflect.ValueOf(first), reflect.ValueOf(second) - if firstPtr.Kind() != reflect.Ptr || secondPtr.Kind() != reflect.Ptr { - return false, false // not both are pointers - } - - firstType, secondType := reflect.TypeOf(first), reflect.TypeOf(second) - if firstType != secondType { - return false, true // both are pointers, but of different types - } - - // compare pointer addresses - return first == second, true -} - -// formatUnequalValues takes two values of arbitrary types and returns string -// representations appropriate to be presented to the user. -// -// If the values are not of like type, the returned strings will be prefixed -// with the type name, and the value will be enclosed in parentheses similar -// to a type conversion in the Go grammar. -func formatUnequalValues(expected, actual interface{}) (e string, a string) { - if reflect.TypeOf(expected) != reflect.TypeOf(actual) { - return fmt.Sprintf("%T(%s)", expected, truncatingFormat("%#v", expected)), - fmt.Sprintf("%T(%s)", actual, truncatingFormat("%#v", actual)) - } - switch expected.(type) { - case time.Duration: - return fmt.Sprintf("%v", expected), fmt.Sprintf("%v", actual) - } - return truncatingFormat("%#v", expected), truncatingFormat("%#v", actual) -} - -// truncatingFormat formats the data and truncates it if it's too long. -// -// This helps keep formatted error messages lines from exceeding the -// bufio.MaxScanTokenSize max line length that the go testing framework imposes. -func truncatingFormat(format string, data interface{}) string { - value := fmt.Sprintf(format, data) - // Give us space for two truncated objects and the surrounding sentence. - maxMessageSize := bufio.MaxScanTokenSize/2 - 100 - if len(value) > maxMessageSize { - value = value[0:maxMessageSize] + "<... truncated>" - } - return value -} - -// EqualValues asserts that two objects are equal or convertible to the larger -// type and equal. -// -// assert.EqualValues(t, uint32(123), int32(123)) -func EqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - - if !ObjectsAreEqualValues(expected, actual) { - diff := diff(expected, actual) - expected, actual = formatUnequalValues(expected, actual) - return Fail(t, fmt.Sprintf("Not equal: \n"+ - "expected: %s\n"+ - "actual : %s%s", expected, actual, diff), msgAndArgs...) - } - - return true -} - -// EqualExportedValues asserts that the types of two objects are equal and their public -// fields are also equal. This is useful for comparing structs that have private fields -// that could potentially differ. -// -// type S struct { -// Exported int -// notExported int -// } -// assert.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true -// assert.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false -func EqualExportedValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - - aType := reflect.TypeOf(expected) - bType := reflect.TypeOf(actual) - - if aType != bType { - return Fail(t, fmt.Sprintf("Types expected to match exactly\n\t%v != %v", aType, bType), msgAndArgs...) - } - - expected = copyExportedFields(expected) - actual = copyExportedFields(actual) - - if !ObjectsAreEqualValues(expected, actual) { - diff := diff(expected, actual) - expected, actual = formatUnequalValues(expected, actual) - return Fail(t, fmt.Sprintf("Not equal (comparing only exported fields): \n"+ - "expected: %s\n"+ - "actual : %s%s", expected, actual, diff), msgAndArgs...) - } - - return true -} - -// Exactly asserts that two objects are equal in value and type. -// -// assert.Exactly(t, int32(123), int64(123)) -func Exactly(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - - aType := reflect.TypeOf(expected) - bType := reflect.TypeOf(actual) - - if aType != bType { - return Fail(t, fmt.Sprintf("Types expected to match exactly\n\t%v != %v", aType, bType), msgAndArgs...) - } - - return Equal(t, expected, actual, msgAndArgs...) -} - -// NotNil asserts that the specified object is not nil. -// -// assert.NotNil(t, err) -func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - if !isNil(object) { - return true - } - if h, ok := t.(tHelper); ok { - h.Helper() - } - return Fail(t, "Expected value not to be nil.", msgAndArgs...) -} - -// isNil checks if a specified object is nil or not, without Failing. -func isNil(object interface{}) bool { - if object == nil { - return true - } - - value := reflect.ValueOf(object) - switch value.Kind() { - case - reflect.Chan, reflect.Func, - reflect.Interface, reflect.Map, - reflect.Ptr, reflect.Slice, reflect.UnsafePointer: - - return value.IsNil() - } - - return false -} - -// Nil asserts that the specified object is nil. -// -// assert.Nil(t, err) -func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - if isNil(object) { - return true - } - if h, ok := t.(tHelper); ok { - h.Helper() - } - return Fail(t, fmt.Sprintf("Expected nil, but got: %s", truncatingFormat("%#v", object)), msgAndArgs...) -} - -// isEmpty gets whether the specified object is considered empty or not. -func isEmpty(object interface{}) bool { - // get nil case out of the way - if object == nil { - return true - } - - return isEmptyValue(reflect.ValueOf(object)) -} - -// isEmptyValue gets whether the specified reflect.Value is considered empty or not. -func isEmptyValue(objValue reflect.Value) bool { - if objValue.IsZero() { - return true - } - // Special cases of non-zero values that we consider empty - switch objValue.Kind() { - // collection types are empty when they have no element - // Note: array types are empty when they match their zero-initialized state. - case reflect.Chan, reflect.Map, reflect.Slice: - return objValue.Len() == 0 - // non-nil pointers are empty if the value they point to is empty - case reflect.Ptr: - return isEmptyValue(objValue.Elem()) - } - return false -} - -// Empty asserts that the given value is "empty". -// -// [Zero values] are "empty". -// -// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). -// -// Slices, maps and channels with zero length are "empty". -// -// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". -// -// assert.Empty(t, obj) -// -// [Zero values]: https://go.dev/ref/spec#The_zero_value -func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - pass := isEmpty(object) - if !pass { - if h, ok := t.(tHelper); ok { - h.Helper() - } - Fail(t, fmt.Sprintf("Should be empty, but was %s", truncatingFormat("%v", object)), msgAndArgs...) - } - - return pass -} - -// NotEmpty asserts that the specified object is NOT [Empty]. -// -// if assert.NotEmpty(t, obj) { -// assert.Equal(t, "two", obj[1]) -// } -func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) bool { - pass := !isEmpty(object) - if !pass { - if h, ok := t.(tHelper); ok { - h.Helper() - } - Fail(t, fmt.Sprintf("Should NOT be empty, but was %v", object), msgAndArgs...) - } - - return pass -} - -// getLen tries to get the length of an object. -// It returns (0, false) if impossible. -func getLen(x interface{}) (length int, ok bool) { - v := reflect.ValueOf(x) - defer func() { - ok = recover() == nil - }() - return v.Len(), true -} - -// Len asserts that the specified object has specific length. -// Len also fails if the object has a type that len() not accept. -// -// assert.Len(t, mySlice, 3) -func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - l, ok := getLen(object) - if !ok { - return Fail(t, fmt.Sprintf("%q could not be applied builtin len()", truncatingFormat("%v", object)), msgAndArgs...) - } - - if l != length { - return Fail(t, fmt.Sprintf("%q should have %d item(s), but has %d", truncatingFormat("%v", object), length, l), msgAndArgs...) - } - return true -} - -// True asserts that the specified value is true. -// -// assert.True(t, myBool) -func True(t TestingT, value bool, msgAndArgs ...interface{}) bool { - if !value { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return Fail(t, "Should be true", msgAndArgs...) - } - - return true -} - -// False asserts that the specified value is false. -// -// assert.False(t, myBool) -func False(t TestingT, value bool, msgAndArgs ...interface{}) bool { - if value { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return Fail(t, "Should be false", msgAndArgs...) - } - - return true -} - -// NotEqual asserts that the specified values are NOT equal. -// -// assert.NotEqual(t, obj1, obj2) -// -// Pointer variable equality is determined based on the equality of the -// referenced values (as opposed to the memory addresses). -func NotEqual(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if err := validateEqualArgs(expected, actual); err != nil { - return Fail(t, fmt.Sprintf("Invalid operation: %#v != %#v (%s)", - expected, actual, err), msgAndArgs...) - } - - if ObjectsAreEqual(expected, actual) { - return Fail(t, fmt.Sprintf("Should not be: %s\n", truncatingFormat("%#v", actual)), msgAndArgs...) - } - - return true -} - -// NotEqualValues asserts that two objects are not equal even when converted to the same type -// -// assert.NotEqualValues(t, obj1, obj2) -func NotEqualValues(t TestingT, expected, actual interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - - if ObjectsAreEqualValues(expected, actual) { - return Fail(t, fmt.Sprintf("Should not be: %s\n", truncatingFormat("%#v", actual)), msgAndArgs...) - } - - return true -} - -// containsElement try loop over the list check if the list includes the element. -// return (false, false) if impossible. -// return (true, false) if element was not found. -// return (true, true) if element was found. -func containsElement(list interface{}, element interface{}) (ok, found bool) { - listValue := reflect.ValueOf(list) - listType := reflect.TypeOf(list) - if listType == nil { - return false, false - } - listKind := listType.Kind() - defer func() { - if e := recover(); e != nil { - ok = false - found = false - } - }() - - if listKind == reflect.String { - elementValue := reflect.ValueOf(element) - return true, strings.Contains(listValue.String(), elementValue.String()) - } - - if listKind == reflect.Map { - mapKeys := listValue.MapKeys() - for i := 0; i < len(mapKeys); i++ { - if ObjectsAreEqual(mapKeys[i].Interface(), element) { - return true, true - } - } - return true, false - } - - for i := 0; i < listValue.Len(); i++ { - if ObjectsAreEqual(listValue.Index(i).Interface(), element) { - return true, true - } - } - return true, false -} - -// Contains asserts that the specified string, list(array, slice...) or map contains the -// specified substring or element. -// -// assert.Contains(t, "Hello World", "World") -// assert.Contains(t, ["Hello", "World"], "World") -// assert.Contains(t, {"Hello": "World"}, "Hello") -func Contains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - - ok, found := containsElement(s, contains) - if !ok { - return Fail(t, fmt.Sprintf("%s could not be applied builtin len()", truncatingFormat("%#v", s)), msgAndArgs...) - } - if !found { - return Fail(t, fmt.Sprintf("%s does not contain %#v", truncatingFormat("%#v", s), contains), msgAndArgs...) - } - - return true -} - -// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the -// specified substring or element. -// -// assert.NotContains(t, "Hello World", "Earth") -// assert.NotContains(t, ["Hello", "World"], "Earth") -// assert.NotContains(t, {"Hello": "World"}, "Earth") -func NotContains(t TestingT, s, contains interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - - ok, found := containsElement(s, contains) - if !ok { - return Fail(t, fmt.Sprintf("%s could not be applied builtin len()", truncatingFormat("%#v", s)), msgAndArgs...) - } - if found { - return Fail(t, fmt.Sprintf("%s should not contain %#v", truncatingFormat("%#v", s), contains), msgAndArgs...) - } - - return true -} - -// Subset asserts that the list (array, slice, or map) contains all elements -// given in the subset (array, slice, or map). -// Map elements are key-value pairs unless compared with an array or slice where -// only the map key is evaluated. -// -// assert.Subset(t, [1, 2, 3], [1, 2]) -// assert.Subset(t, {"x": 1, "y": 2}, {"x": 1}) -// assert.Subset(t, [1, 2, 3], {1: "one", 2: "two"}) -// assert.Subset(t, {"x": 1, "y": 2}, ["x"]) -func Subset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if subset == nil { - return true // we consider nil to be equal to the nil set - } - - listKind := reflect.TypeOf(list).Kind() - if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { - return Fail(t, fmt.Sprintf("%q has an unsupported type %s", list, listKind), msgAndArgs...) - } - - subsetKind := reflect.TypeOf(subset).Kind() - if subsetKind != reflect.Array && subsetKind != reflect.Slice && subsetKind != reflect.Map { - return Fail(t, fmt.Sprintf("%q has an unsupported type %s", subset, subsetKind), msgAndArgs...) - } - - if subsetKind == reflect.Map && listKind == reflect.Map { - subsetMap := reflect.ValueOf(subset) - actualMap := reflect.ValueOf(list) - - for _, k := range subsetMap.MapKeys() { - ev := subsetMap.MapIndex(k) - av := actualMap.MapIndex(k) - - if !av.IsValid() { - return Fail(t, fmt.Sprintf("%s does not contain %s", truncatingFormat("%#v", list), truncatingFormat("%#v", subset)), msgAndArgs...) - } - if !ObjectsAreEqual(ev.Interface(), av.Interface()) { - return Fail(t, fmt.Sprintf("%s does not contain %s", truncatingFormat("%#v", list), truncatingFormat("%#v", subset)), msgAndArgs...) - } - } - - return true - } - - subsetList := reflect.ValueOf(subset) - if subsetKind == reflect.Map { - keys := make([]interface{}, subsetList.Len()) - for idx, key := range subsetList.MapKeys() { - keys[idx] = key.Interface() - } - subsetList = reflect.ValueOf(keys) - } - for i := 0; i < subsetList.Len(); i++ { - element := subsetList.Index(i).Interface() - ok, found := containsElement(list, element) - if !ok { - return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", list), msgAndArgs...) - } - if !found { - return Fail(t, fmt.Sprintf("%s does not contain %#v", truncatingFormat("%#v", list), element), msgAndArgs...) - } - } - - return true -} - -// NotSubset asserts that the list (array, slice, or map) does NOT contain all -// elements given in the subset (array, slice, or map). -// Map elements are key-value pairs unless compared with an array or slice where -// only the map key is evaluated. -// -// assert.NotSubset(t, [1, 3, 4], [1, 2]) -// assert.NotSubset(t, {"x": 1, "y": 2}, {"z": 3}) -// assert.NotSubset(t, [1, 3, 4], {1: "one", 2: "two"}) -// assert.NotSubset(t, {"x": 1, "y": 2}, ["z"]) -func NotSubset(t TestingT, list, subset interface{}, msgAndArgs ...interface{}) (ok bool) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if subset == nil { - return Fail(t, "nil is the empty set which is a subset of every set", msgAndArgs...) - } - - listKind := reflect.TypeOf(list).Kind() - if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { - return Fail(t, fmt.Sprintf("%#v has an unsupported type %s", list, listKind), msgAndArgs...) - } - - subsetKind := reflect.TypeOf(subset).Kind() - if subsetKind != reflect.Array && subsetKind != reflect.Slice && subsetKind != reflect.Map { - return Fail(t, fmt.Sprintf("%#v has an unsupported type %s", subset, subsetKind), msgAndArgs...) - } - - if subsetKind == reflect.Map && listKind == reflect.Map { - subsetMap := reflect.ValueOf(subset) - actualMap := reflect.ValueOf(list) - - for _, k := range subsetMap.MapKeys() { - ev := subsetMap.MapIndex(k) - av := actualMap.MapIndex(k) - - if !av.IsValid() { - return true - } - if !ObjectsAreEqual(ev.Interface(), av.Interface()) { - return true - } - } - - return Fail(t, fmt.Sprintf("%s is a subset of %s", truncatingFormat("%#v", subset), truncatingFormat("%#v", list)), msgAndArgs...) - } - - subsetList := reflect.ValueOf(subset) - if subsetKind == reflect.Map { - keys := make([]interface{}, subsetList.Len()) - for idx, key := range subsetList.MapKeys() { - keys[idx] = key.Interface() - } - subsetList = reflect.ValueOf(keys) - } - for i := 0; i < subsetList.Len(); i++ { - element := subsetList.Index(i).Interface() - ok, found := containsElement(list, element) - if !ok { - return Fail(t, fmt.Sprintf("%#v could not be applied builtin len()", list), msgAndArgs...) - } - if !found { - return true - } - } - - return Fail(t, fmt.Sprintf("%s is a subset of %s", truncatingFormat("%#v", subset), truncatingFormat("%#v", list)), msgAndArgs...) -} - -// ElementsMatch asserts that the specified listA(array, slice...) is equal to specified -// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, -// the number of appearances of each of them in both lists should match. -// -// assert.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2]) -func ElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface{}) (ok bool) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if isEmpty(listA) && isEmpty(listB) { - return true - } - - if !isList(t, listA, msgAndArgs...) || !isList(t, listB, msgAndArgs...) { - return false - } - - extraA, extraB := diffLists(listA, listB) - - if len(extraA) == 0 && len(extraB) == 0 { - return true - } - - return Fail(t, formatListDiff(listA, listB, extraA, extraB), msgAndArgs...) -} - -// isList checks that the provided value is array or slice. -func isList(t TestingT, list interface{}, msgAndArgs ...interface{}) (ok bool) { - kind := reflect.TypeOf(list).Kind() - if kind != reflect.Array && kind != reflect.Slice { - return Fail(t, fmt.Sprintf("%q has an unsupported type %s, expecting array or slice", list, kind), - msgAndArgs...) - } - return true -} - -// diffLists diffs two arrays/slices and returns slices of elements that are only in A and only in B. -// If some element is present multiple times, each instance is counted separately (e.g. if something is 2x in A and -// 5x in B, it will be 0x in extraA and 3x in extraB). The order of items in both lists is ignored. -func diffLists(listA, listB interface{}) (extraA, extraB []interface{}) { - aValue := reflect.ValueOf(listA) - bValue := reflect.ValueOf(listB) - - aLen := aValue.Len() - bLen := bValue.Len() - - // Mark indexes in bValue that we already used - visited := make([]bool, bLen) - for i := 0; i < aLen; i++ { - element := aValue.Index(i).Interface() - found := false - for j := 0; j < bLen; j++ { - if visited[j] { - continue - } - if ObjectsAreEqual(bValue.Index(j).Interface(), element) { - visited[j] = true - found = true - break - } - } - if !found { - extraA = append(extraA, element) - } - } - - for j := 0; j < bLen; j++ { - if visited[j] { - continue - } - extraB = append(extraB, bValue.Index(j).Interface()) - } - - return -} - -func formatListDiff(listA, listB interface{}, extraA, extraB []interface{}) string { - var msg bytes.Buffer - - msg.WriteString("elements differ") - if len(extraA) > 0 { - msg.WriteString("\n\nextra elements in list A:\n") - msg.WriteString(spewConfig.Sdump(extraA)) - } - if len(extraB) > 0 { - msg.WriteString("\n\nextra elements in list B:\n") - msg.WriteString(spewConfig.Sdump(extraB)) - } - msg.WriteString("\n\nlistA:\n") - msg.WriteString(spewConfig.Sdump(listA)) - msg.WriteString("\n\nlistB:\n") - msg.WriteString(spewConfig.Sdump(listB)) - - return msg.String() -} - -// NotElementsMatch asserts that the specified listA(array, slice...) is NOT equal to specified -// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, -// the number of appearances of each of them in both lists should not match. -// This is an inverse of ElementsMatch. -// -// assert.NotElementsMatch(t, [1, 1, 2, 3], [1, 1, 2, 3]) -> false -// -// assert.NotElementsMatch(t, [1, 1, 2, 3], [1, 2, 3]) -> true -// -// assert.NotElementsMatch(t, [1, 2, 3], [1, 2, 4]) -> true -func NotElementsMatch(t TestingT, listA, listB interface{}, msgAndArgs ...interface{}) (ok bool) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if isEmpty(listA) && isEmpty(listB) { - return Fail(t, "listA and listB contain the same elements", msgAndArgs) - } - - if !isList(t, listA, msgAndArgs...) { - return Fail(t, "listA is not a list type", msgAndArgs...) - } - if !isList(t, listB, msgAndArgs...) { - return Fail(t, "listB is not a list type", msgAndArgs...) - } - - extraA, extraB := diffLists(listA, listB) - if len(extraA) == 0 && len(extraB) == 0 { - return Fail(t, "listA and listB contain the same elements", msgAndArgs) - } - - return true -} - -// Condition uses a Comparison to assert a complex condition. -func Condition(t TestingT, comp Comparison, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - result := comp() - if !result { - Fail(t, "Condition failed!", msgAndArgs...) - } - return result -} - -// PanicTestFunc defines a func that should be passed to the assert.Panics and assert.NotPanics -// methods, and represents a simple func that takes no arguments, and returns nothing. -type PanicTestFunc func() - -// didPanic returns true if the function passed to it panics. Otherwise, it returns false. -func didPanic(f PanicTestFunc) (didPanic bool, message interface{}, stack string) { - didPanic = true - - defer func() { - message = recover() - if didPanic { - stack = string(debug.Stack()) - } - }() - - // call the target function - f() - didPanic = false - - return -} - -// Panics asserts that the code inside the specified PanicTestFunc panics. -// -// assert.Panics(t, func(){ GoCrazy() }) -func Panics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - - if funcDidPanic, panicValue, _ := didPanic(f); !funcDidPanic { - return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) - } - - return true -} - -// PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that -// the recovered panic value equals the expected panic value. -// -// assert.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) -func PanicsWithValue(t TestingT, expected interface{}, f PanicTestFunc, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - - funcDidPanic, panicValue, panickedStack := didPanic(f) - if !funcDidPanic { - return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) - } - if panicValue != expected { - return Fail(t, fmt.Sprintf("func %#v should panic with value:\t%#v\n\tPanic value:\t%#v\n\tPanic stack:\t%s", f, expected, panicValue, panickedStack), msgAndArgs...) - } - - return true -} - -// PanicsWithError asserts that the code inside the specified PanicTestFunc -// panics, and that the recovered panic value is an error that satisfies the -// EqualError comparison. -// -// assert.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) -func PanicsWithError(t TestingT, errString string, f PanicTestFunc, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - - funcDidPanic, panicValue, panickedStack := didPanic(f) - if !funcDidPanic { - return Fail(t, fmt.Sprintf("func %#v should panic\n\tPanic value:\t%#v", f, panicValue), msgAndArgs...) - } - panicErr, isError := panicValue.(error) - if !isError || panicErr.Error() != errString { - msg := fmt.Sprintf("func %#v should panic with error message:\t%#v\n", f, errString) - if isError { - msg += fmt.Sprintf("\tError message:\t%#v\n", panicErr.Error()) - } - msg += fmt.Sprintf("\tPanic value:\t%#v\n", panicValue) - msg += fmt.Sprintf("\tPanic stack:\t%s\n", panickedStack) - return Fail(t, msg, msgAndArgs...) - } - - return true -} - -// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. -// -// assert.NotPanics(t, func(){ RemainCalm() }) -func NotPanics(t TestingT, f PanicTestFunc, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - - if funcDidPanic, panicValue, panickedStack := didPanic(f); funcDidPanic { - return Fail(t, fmt.Sprintf("func %#v should not panic\n\tPanic value:\t%v\n\tPanic stack:\t%s", f, panicValue, panickedStack), msgAndArgs...) - } - - return true -} - -// WithinDuration asserts that the two times are within duration delta of each other. -// -// assert.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) -func WithinDuration(t TestingT, expected, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - - dt := expected.Sub(actual) - if dt < -delta || dt > delta { - return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...) - } - - return true -} - -// WithinRange asserts that a time is within a time range (inclusive). -// -// assert.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) -func WithinRange(t TestingT, actual, start, end time.Time, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - - if end.Before(start) { - return Fail(t, "Start should be before end", msgAndArgs...) - } - - if actual.Before(start) { - return Fail(t, fmt.Sprintf("Time %v expected to be in time range %v to %v, but is before the range", actual, start, end), msgAndArgs...) - } else if actual.After(end) { - return Fail(t, fmt.Sprintf("Time %v expected to be in time range %v to %v, but is after the range", actual, start, end), msgAndArgs...) - } - - return true -} - -func toFloat(x interface{}) (float64, bool) { - var xf float64 - xok := true - - switch xn := x.(type) { - case uint: - xf = float64(xn) - case uint8: - xf = float64(xn) - case uint16: - xf = float64(xn) - case uint32: - xf = float64(xn) - case uint64: - xf = float64(xn) - case int: - xf = float64(xn) - case int8: - xf = float64(xn) - case int16: - xf = float64(xn) - case int32: - xf = float64(xn) - case int64: - xf = float64(xn) - case float32: - xf = float64(xn) - case float64: - xf = xn - case time.Duration: - xf = float64(xn) - default: - xok = false - } - - return xf, xok -} - -// InDelta asserts that the two numerals are within delta of each other. -// -// assert.InDelta(t, math.Pi, 22/7.0, 0.01) -func InDelta(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - - af, aok := toFloat(expected) - bf, bok := toFloat(actual) - - if !aok || !bok { - return Fail(t, "Parameters must be numerical", msgAndArgs...) - } - - if math.IsNaN(af) && math.IsNaN(bf) { - return true - } - - if math.IsNaN(af) { - return Fail(t, "Expected must not be NaN", msgAndArgs...) - } - - if math.IsNaN(bf) { - return Fail(t, fmt.Sprintf("Expected %v with delta %v, but was NaN", expected, delta), msgAndArgs...) - } - - dt := af - bf - if dt < -delta || dt > delta { - return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...) - } - - return true -} - -// InDeltaSlice is the same as InDelta, except it compares two slices. -func InDeltaSlice(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if expected == nil || actual == nil || - reflect.TypeOf(actual).Kind() != reflect.Slice || - reflect.TypeOf(expected).Kind() != reflect.Slice { - return Fail(t, "Parameters must be slice", msgAndArgs...) - } - - actualSlice := reflect.ValueOf(actual) - expectedSlice := reflect.ValueOf(expected) - - for i := 0; i < actualSlice.Len(); i++ { - result := InDelta(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), delta, msgAndArgs...) - if !result { - return result - } - } - - return true -} - -// InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. -func InDeltaMapValues(t TestingT, expected, actual interface{}, delta float64, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if expected == nil || actual == nil || - reflect.TypeOf(actual).Kind() != reflect.Map || - reflect.TypeOf(expected).Kind() != reflect.Map { - return Fail(t, "Arguments must be maps", msgAndArgs...) - } - - expectedMap := reflect.ValueOf(expected) - actualMap := reflect.ValueOf(actual) - - if expectedMap.Len() != actualMap.Len() { - return Fail(t, "Arguments must have the same number of keys", msgAndArgs...) - } - - for _, k := range expectedMap.MapKeys() { - ev := expectedMap.MapIndex(k) - av := actualMap.MapIndex(k) - - if !ev.IsValid() { - return Fail(t, fmt.Sprintf("missing key %q in expected map", k), msgAndArgs...) - } - - if !av.IsValid() { - return Fail(t, fmt.Sprintf("missing key %q in actual map", k), msgAndArgs...) - } - - if !InDelta( - t, - ev.Interface(), - av.Interface(), - delta, - msgAndArgs..., - ) { - return false - } - } - - return true -} - -func calcRelativeError(expected, actual interface{}) (float64, error) { - af, aok := toFloat(expected) - bf, bok := toFloat(actual) - if !aok || !bok { - return 0, fmt.Errorf("Parameters must be numerical") - } - if math.IsNaN(af) && math.IsNaN(bf) { - return 0, nil - } - if math.IsNaN(af) { - return 0, errors.New("expected value must not be NaN") - } - if af == 0 { - return 0, fmt.Errorf("expected value must have a value other than zero to calculate the relative error") - } - if math.IsNaN(bf) { - return 0, errors.New("actual value must not be NaN") - } - - return math.Abs(af-bf) / math.Abs(af), nil -} - -// InEpsilon asserts that expected and actual have a relative error less than epsilon -func InEpsilon(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if math.IsNaN(epsilon) { - return Fail(t, "epsilon must not be NaN", msgAndArgs...) - } - actualEpsilon, err := calcRelativeError(expected, actual) - if err != nil { - return Fail(t, err.Error(), msgAndArgs...) - } - if math.IsNaN(actualEpsilon) { - return Fail(t, "relative error is NaN", msgAndArgs...) - } - if actualEpsilon > epsilon { - return Fail(t, fmt.Sprintf("Relative error is too high: %#v (expected)\n"+ - " < %#v (actual)", epsilon, actualEpsilon), msgAndArgs...) - } - - return true -} - -// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. -func InEpsilonSlice(t TestingT, expected, actual interface{}, epsilon float64, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - - if expected == nil || actual == nil { - return Fail(t, "Parameters must be slice", msgAndArgs...) - } - - expectedSlice := reflect.ValueOf(expected) - actualSlice := reflect.ValueOf(actual) - - if expectedSlice.Type().Kind() != reflect.Slice { - return Fail(t, "Expected value must be slice", msgAndArgs...) - } - - expectedLen := expectedSlice.Len() - if !IsType(t, expected, actual) || !Len(t, actual, expectedLen) { - return false - } - - for i := 0; i < expectedLen; i++ { - if !InEpsilon(t, expectedSlice.Index(i).Interface(), actualSlice.Index(i).Interface(), epsilon, "at index %d", i) { - return false - } - } - - return true -} - -/* - Errors -*/ - -// NoError asserts that a function returned a nil error (ie. no error). -// -// actualObj, err := SomeFunction() -// if assert.NoError(t, err) { -// assert.Equal(t, expectedObj, actualObj) -// } -func NoError(t TestingT, err error, msgAndArgs ...interface{}) bool { - if err != nil { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return Fail(t, fmt.Sprintf("Received unexpected error:\n%s", truncatingFormat("%+v", err)), msgAndArgs...) - } - - return true -} - -// Error asserts that a function returned a non-nil error (ie. an error). -// -// actualObj, err := SomeFunction() -// assert.Error(t, err) -func Error(t TestingT, err error, msgAndArgs ...interface{}) bool { - if err == nil { - if h, ok := t.(tHelper); ok { - h.Helper() - } - return Fail(t, "An error is expected but got nil.", msgAndArgs...) - } - - return true -} - -// EqualError asserts that a function returned a non-nil error (i.e. an error) -// and that it is equal to the provided error. -// -// actualObj, err := SomeFunction() -// assert.EqualError(t, err, expectedErrorString) -func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if !Error(t, theError, msgAndArgs...) { - return false - } - expected := errString - actual := theError.Error() - // don't need to use deep equals here, we know they are both strings - if expected != actual { - return Fail(t, fmt.Sprintf("Error message not equal:\n"+ - "expected: %q\n"+ - "actual : %s", expected, truncatingFormat("%q", actual)), msgAndArgs...) - } - return true -} - -// ErrorContains asserts that a function returned a non-nil error (i.e. an -// error) and that the error contains the specified substring. -// -// actualObj, err := SomeFunction() -// assert.ErrorContains(t, err, expectedErrorSubString) -func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if !Error(t, theError, msgAndArgs...) { - return false - } - - actual := theError.Error() - if !strings.Contains(actual, contains) { - return Fail(t, fmt.Sprintf("Error %s does not contain %#v", truncatingFormat("%#v", actual), contains), msgAndArgs...) - } - - return true -} - -// matchRegexp return true if a specified regexp matches a string. -func matchRegexp(rx interface{}, str interface{}) bool { - var r *regexp.Regexp - if rr, ok := rx.(*regexp.Regexp); ok { - r = rr - } else { - r = regexp.MustCompile(fmt.Sprint(rx)) - } - - switch v := str.(type) { - case []byte: - return r.Match(v) - case string: - return r.MatchString(v) - default: - return r.MatchString(fmt.Sprint(v)) - } -} - -// Regexp asserts that a specified regexp matches a string. -// -// assert.Regexp(t, regexp.MustCompile("start"), "it's starting") -// assert.Regexp(t, "start...$", "it's not starting") -func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - - match := matchRegexp(rx, str) - - if !match { - Fail(t, fmt.Sprintf("Expect \"%v\" to match \"%v\"", str, rx), msgAndArgs...) - } - - return match -} - -// NotRegexp asserts that a specified regexp does not match a string. -// -// assert.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") -// assert.NotRegexp(t, "^start", "it's not starting") -func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - match := matchRegexp(rx, str) - - if match { - Fail(t, fmt.Sprintf("Expect \"%v\" to NOT match \"%v\"", str, rx), msgAndArgs...) - } - - return !match -} - -// Zero asserts that i is the zero value for its type. -func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if i != nil && !reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) { - return Fail(t, fmt.Sprintf("Should be zero, but was %s", truncatingFormat("%v", i)), msgAndArgs...) - } - return true -} - -// NotZero asserts that i is not the zero value for its type. -func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if i == nil || reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) { - return Fail(t, fmt.Sprintf("Should not be zero, but was %v", i), msgAndArgs...) - } - return true -} - -// FileExists checks whether a file exists in the given path. It also fails if -// the path points to a directory or there is an error when trying to check the file. -func FileExists(t TestingT, path string, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - info, err := os.Lstat(path) - if err != nil { - if os.IsNotExist(err) { - return Fail(t, fmt.Sprintf("unable to find file %q", path), msgAndArgs...) - } - return Fail(t, fmt.Sprintf("error when running os.Lstat(%q): %s", path, err), msgAndArgs...) - } - if info.IsDir() { - return Fail(t, fmt.Sprintf("%q is a directory", path), msgAndArgs...) - } - return true -} - -// NoFileExists checks whether a file does not exist in a given path. It fails -// if the path points to an existing _file_ only. -func NoFileExists(t TestingT, path string, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - info, err := os.Lstat(path) - if err != nil { - return true - } - if info.IsDir() { - return true - } - return Fail(t, fmt.Sprintf("file %q exists", path), msgAndArgs...) -} - -// DirExists checks whether a directory exists in the given path. It also fails -// if the path is a file rather a directory or there is an error checking whether it exists. -func DirExists(t TestingT, path string, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - info, err := os.Lstat(path) - if err != nil { - if os.IsNotExist(err) { - return Fail(t, fmt.Sprintf("unable to find file %q", path), msgAndArgs...) - } - return Fail(t, fmt.Sprintf("error when running os.Lstat(%q): %s", path, err), msgAndArgs...) - } - if !info.IsDir() { - return Fail(t, fmt.Sprintf("%q is a file", path), msgAndArgs...) - } - return true -} - -// NoDirExists checks whether a directory does not exist in the given path. -// It fails if the path points to an existing _directory_ only. -func NoDirExists(t TestingT, path string, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - info, err := os.Lstat(path) - if err != nil { - if os.IsNotExist(err) { - return true - } - return true - } - if !info.IsDir() { - return true - } - return Fail(t, fmt.Sprintf("directory %q exists", path), msgAndArgs...) -} - -// JSONEq asserts that two JSON strings are equivalent. -// -// assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) -func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - var expectedJSONAsInterface, actualJSONAsInterface interface{} - - if err := json.Unmarshal([]byte(expected), &expectedJSONAsInterface); err != nil { - return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid json.\nJSON parsing error: '%s'", expected, err.Error()), msgAndArgs...) - } - - // Shortcut if same bytes - if actual == expected { - return true - } - - if err := json.Unmarshal([]byte(actual), &actualJSONAsInterface); err != nil { - return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid json.\nJSON parsing error: '%s'", actual, err.Error()), msgAndArgs...) - } - - return Equal(t, expectedJSONAsInterface, actualJSONAsInterface, msgAndArgs...) -} - -// YAMLEq asserts that the first documents in the two YAML strings are equivalent. -// -// expected := `--- -// key: value -// --- -// key: this is a second document, it is not evaluated -// ` -// actual := `--- -// key: value -// --- -// key: this is a subsequent document, it is not evaluated -// ` -// assert.YAMLEq(t, expected, actual) -func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - var expectedYAMLAsInterface, actualYAMLAsInterface interface{} - - if err := yaml.Unmarshal([]byte(expected), &expectedYAMLAsInterface); err != nil { - return Fail(t, fmt.Sprintf("Expected value ('%s') is not valid yaml.\nYAML parsing error: '%s'", expected, err.Error()), msgAndArgs...) - } - - // Shortcut if same bytes - if actual == expected { - return true - } - - if err := yaml.Unmarshal([]byte(actual), &actualYAMLAsInterface); err != nil { - return Fail(t, fmt.Sprintf("Input ('%s') needs to be valid yaml.\nYAML error: '%s'", actual, err.Error()), msgAndArgs...) - } - - return Equal(t, expectedYAMLAsInterface, actualYAMLAsInterface, msgAndArgs...) -} - -func typeAndKind(v interface{}) (reflect.Type, reflect.Kind) { - t := reflect.TypeOf(v) - k := t.Kind() - - if k == reflect.Ptr { - t = t.Elem() - k = t.Kind() - } - return t, k -} - -// diff returns a diff of both values as long as both are of the same type and -// are a struct, map, slice, array or string. Otherwise it returns an empty string. -func diff(expected interface{}, actual interface{}) string { - if expected == nil || actual == nil { - return "" - } - - et, ek := typeAndKind(expected) - at, _ := typeAndKind(actual) - - if et != at { - return "" - } - - if ek != reflect.Struct && ek != reflect.Map && ek != reflect.Slice && ek != reflect.Array && ek != reflect.String { - return "" - } - - var e, a string - - switch et { - case reflect.TypeOf(""): - e = reflect.ValueOf(expected).String() - a = reflect.ValueOf(actual).String() - case reflect.TypeOf(time.Time{}): - e = spewConfigStringerEnabled.Sdump(expected) - a = spewConfigStringerEnabled.Sdump(actual) - default: - e = spewConfig.Sdump(expected) - a = spewConfig.Sdump(actual) - } - - diff, _ := difflib.GetUnifiedDiffString(difflib.UnifiedDiff{ - A: difflib.SplitLines(e), - B: difflib.SplitLines(a), - FromFile: "Expected", - FromDate: "", - ToFile: "Actual", - ToDate: "", - Context: 1, - }) - - return "\n\nDiff:\n" + diff -} - -func isFunction(arg interface{}) bool { - if arg == nil { - return false - } - return reflect.TypeOf(arg).Kind() == reflect.Func -} - -var spewConfig = spew.ConfigState{ - Indent: " ", - DisablePointerAddresses: true, - DisableCapacities: true, - SortKeys: true, - DisableMethods: true, - MaxDepth: 10, -} - -var spewConfigStringerEnabled = spew.ConfigState{ - Indent: " ", - DisablePointerAddresses: true, - DisableCapacities: true, - SortKeys: true, - MaxDepth: 10, -} - -type tHelper = interface { - Helper() -} - -// Eventually asserts that given condition will be met in waitFor time, -// periodically checking target function each tick. -// -// assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) -func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - - ch := make(chan bool, 1) - checkCond := func() { ch <- condition() } - - timer := time.NewTimer(waitFor) - defer timer.Stop() - - ticker := time.NewTicker(tick) - defer ticker.Stop() - - var tickC <-chan time.Time - - // Check the condition once first on the initial call. - go checkCond() - - for { - select { - case <-timer.C: - return Fail(t, "Condition never satisfied", msgAndArgs...) - case <-tickC: - tickC = nil - go checkCond() - case v := <-ch: - if v { - return true - } - tickC = ticker.C - } - } -} - -// CollectT implements the TestingT interface and collects all errors. -type CollectT struct { - // A slice of errors. Non-nil slice denotes a failure. - // If it's non-nil but len(c.errors) == 0, this is also a failure - // obtained by direct c.FailNow() call. - errors []error -} - -// Helper is like [testing.T.Helper] but does nothing. -func (CollectT) Helper() {} - -// Errorf collects the error. -func (c *CollectT) Errorf(format string, args ...interface{}) { - c.errors = append(c.errors, fmt.Errorf(format, args...)) -} - -// FailNow stops execution by calling runtime.Goexit. -func (c *CollectT) FailNow() { - c.fail() - runtime.Goexit() -} - -// Deprecated: That was a method for internal usage that should not have been published. Now just panics. -func (*CollectT) Reset() { - panic("Reset() is deprecated") -} - -// Deprecated: That was a method for internal usage that should not have been published. Now just panics. -func (*CollectT) Copy(TestingT) { - panic("Copy() is deprecated") -} - -func (c *CollectT) fail() { - if !c.failed() { - c.errors = []error{} // Make it non-nil to mark a failure. - } -} - -func (c *CollectT) failed() bool { - return c.errors != nil -} - -// EventuallyWithT asserts that given condition will be met in waitFor time, -// periodically checking target function each tick. In contrast to Eventually, -// it supplies a CollectT to the condition function, so that the condition -// function can use the CollectT to call other assertions. -// The condition is considered "met" if no errors are raised in a tick. -// The supplied CollectT collects all errors from one tick (if there are any). -// If the condition is not met before waitFor, the collected errors of -// the last tick are copied to t. -// -// externalValue := false -// go func() { -// time.Sleep(8*time.Second) -// externalValue = true -// }() -// assert.EventuallyWithT(t, func(c *assert.CollectT) { -// // add assertions as needed; any assertion failure will fail the current tick -// assert.True(c, externalValue, "expected 'externalValue' to be true") -// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") -func EventuallyWithT(t TestingT, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - - var lastFinishedTickErrs []error - ch := make(chan *CollectT, 1) - - checkCond := func() { - collect := new(CollectT) - defer func() { - ch <- collect - }() - condition(collect) - } - - timer := time.NewTimer(waitFor) - defer timer.Stop() - - ticker := time.NewTicker(tick) - defer ticker.Stop() - - var tickC <-chan time.Time - - // Check the condition once first on the initial call. - go checkCond() - - for { - select { - case <-timer.C: - for _, err := range lastFinishedTickErrs { - t.Errorf("%v", err) - } - return Fail(t, "Condition never satisfied", msgAndArgs...) - case <-tickC: - tickC = nil - go checkCond() - case collect := <-ch: - if !collect.failed() { - return true - } - // Keep the errors from the last ended condition, so that they can be copied to t if timeout is reached. - lastFinishedTickErrs = collect.errors - tickC = ticker.C - } - } -} - -// Never asserts that the given condition doesn't satisfy in waitFor time, -// periodically checking the target function each tick. -// -// assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) -func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - - ch := make(chan bool, 1) - checkCond := func() { ch <- condition() } - - timer := time.NewTimer(waitFor) - defer timer.Stop() - - ticker := time.NewTicker(tick) - defer ticker.Stop() - - var tickC <-chan time.Time - - // Check the condition once first on the initial call. - go checkCond() - - for { - select { - case <-timer.C: - return true - case <-tickC: - tickC = nil - go checkCond() - case v := <-ch: - if v { - return Fail(t, "Condition satisfied", msgAndArgs...) - } - tickC = ticker.C - } - } -} - -// ErrorIs asserts that at least one of the errors in err's chain matches target. -// This is a wrapper for errors.Is. -func ErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if errors.Is(err, target) { - return true - } - - var expectedText string - if target != nil { - expectedText = target.Error() - if err == nil { - return Fail(t, fmt.Sprintf("Expected error with %q in chain but got nil.", expectedText), msgAndArgs...) - } - } - - chain := buildErrorChainString(err, false) - - return Fail(t, fmt.Sprintf("Target error should be in err chain:\n"+ - "expected: %s\n"+ - "in chain: %s", truncatingFormat("%q", expectedText), truncatingFormat("%s", chain), - ), msgAndArgs...) -} - -// NotErrorIs asserts that none of the errors in err's chain matches target. -// This is a wrapper for errors.Is. -func NotErrorIs(t TestingT, err, target error, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if !errors.Is(err, target) { - return true - } - - var expectedText string - if target != nil { - expectedText = target.Error() - } - - chain := buildErrorChainString(err, false) - - return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+ - "found: %s\n"+ - "in chain: %s", truncatingFormat("%q", expectedText), truncatingFormat("%s", chain), - ), msgAndArgs...) -} - -// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. -// This is a wrapper for errors.As. -func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if errors.As(err, target) { - return true - } - - expectedType := reflect.TypeOf(target).Elem().String() - if err == nil { - return Fail(t, fmt.Sprintf("An error is expected but got nil.\n"+ - "expected: %s", expectedType), msgAndArgs...) - } - - chain := buildErrorChainString(err, true) - - return Fail(t, fmt.Sprintf("Should be in error chain:\n"+ - "expected: %s\n"+ - "in chain: %s", expectedType, truncatingFormat("%s", chain), - ), msgAndArgs...) -} - -// NotErrorAs asserts that none of the errors in err's chain matches target, -// but if so, sets target to that error value. -func NotErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if !errors.As(err, target) { - return true - } - - chain := buildErrorChainString(err, true) - - return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+ - "found: %s\n"+ - "in chain: %s", reflect.TypeOf(target).Elem().String(), truncatingFormat("%s", chain), - ), msgAndArgs...) -} - -func unwrapAll(err error) (errs []error) { - errs = append(errs, err) - switch x := err.(type) { - case interface{ Unwrap() error }: - err = x.Unwrap() - if err == nil { - return - } - errs = append(errs, unwrapAll(err)...) - case interface{ Unwrap() []error }: - for _, err := range x.Unwrap() { - errs = append(errs, unwrapAll(err)...) - } - } - return -} - -func buildErrorChainString(err error, withType bool) string { - if err == nil { - return "" - } - - var chain string - errs := unwrapAll(err) - for i := range errs { - if i != 0 { - chain += "\n\t" - } - chain += fmt.Sprintf("%q", errs[i].Error()) - if withType { - chain += fmt.Sprintf(" (%T)", errs[i]) - } - } - return chain -} diff --git a/vendor/github.com/stretchr/testify/assert/doc.go b/vendor/github.com/stretchr/testify/assert/doc.go deleted file mode 100644 index c111589c7..000000000 --- a/vendor/github.com/stretchr/testify/assert/doc.go +++ /dev/null @@ -1,50 +0,0 @@ -// Package assert provides a set of comprehensive testing tools for use with the normal Go testing system. -// -// # Note -// -// All functions in this package return a bool value indicating whether the assertion has passed. -// -// # Example Usage -// -// The following is a complete example using assert in a standard test function: -// -// import ( -// "testing" -// "github.com/stretchr/testify/assert" -// ) -// -// func TestSomething(t *testing.T) { -// -// var a string = "Hello" -// var b string = "Hello" -// -// assert.Equal(t, a, b, "The two words should be the same.") -// -// } -// -// if you assert many times, use the format below: -// -// import ( -// "testing" -// "github.com/stretchr/testify/assert" -// ) -// -// func TestSomething(t *testing.T) { -// assert := assert.New(t) -// -// var a string = "Hello" -// var b string = "Hello" -// -// assert.Equal(a, b, "The two words should be the same.") -// } -// -// # Assertions -// -// Assertions allow you to easily write test code, and are global funcs in the assert package. -// All assertion functions take, as the first argument, the [*testing.T] object provided by the -// testing framework. This allows the assertion funcs to write the failings and other details to -// the correct place. -// -// Every assertion function also takes an optional string message as the final argument, -// allowing custom error messages to be appended to the message the assertion method outputs. -package assert diff --git a/vendor/github.com/stretchr/testify/assert/errors.go b/vendor/github.com/stretchr/testify/assert/errors.go deleted file mode 100644 index ac9dc9d1d..000000000 --- a/vendor/github.com/stretchr/testify/assert/errors.go +++ /dev/null @@ -1,10 +0,0 @@ -package assert - -import ( - "errors" -) - -// AnError is an error instance useful for testing. If the code does not care -// about error specifics, and only needs to return the error for example, this -// error should be used to make the test code more readable. -var AnError = errors.New("assert.AnError general error for testing") diff --git a/vendor/github.com/stretchr/testify/assert/forward_assertions.go b/vendor/github.com/stretchr/testify/assert/forward_assertions.go deleted file mode 100644 index df189d234..000000000 --- a/vendor/github.com/stretchr/testify/assert/forward_assertions.go +++ /dev/null @@ -1,16 +0,0 @@ -package assert - -// Assertions provides assertion methods around the -// TestingT interface. -type Assertions struct { - t TestingT -} - -// New makes a new Assertions object for the specified TestingT. -func New(t TestingT) *Assertions { - return &Assertions{ - t: t, - } -} - -//go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=assert -template=assertion_forward.go.tmpl -include-format-funcs" diff --git a/vendor/github.com/stretchr/testify/assert/http_assertions.go b/vendor/github.com/stretchr/testify/assert/http_assertions.go deleted file mode 100644 index 5a6bb75f2..000000000 --- a/vendor/github.com/stretchr/testify/assert/http_assertions.go +++ /dev/null @@ -1,165 +0,0 @@ -package assert - -import ( - "fmt" - "net/http" - "net/http/httptest" - "net/url" - "strings" -) - -// httpCode is a helper that returns HTTP code of the response. It returns -1 and -// an error if building a new request fails. -func httpCode(handler http.HandlerFunc, method, url string, values url.Values) (int, error) { - w := httptest.NewRecorder() - req, err := http.NewRequest(method, url, http.NoBody) - if err != nil { - return -1, err - } - req.URL.RawQuery = values.Encode() - handler(w, req) - return w.Code, nil -} - -// HTTPSuccess asserts that a specified handler returns a success status code. -// -// assert.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) -// -// Returns whether the assertion was successful (true) or not (false). -func HTTPSuccess(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - code, err := httpCode(handler, method, url, values) - if err != nil { - Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...) - } - - isSuccessCode := code >= http.StatusOK && code <= http.StatusPartialContent - if !isSuccessCode { - Fail(t, fmt.Sprintf("Expected HTTP success status code for %q but received %d", url+"?"+values.Encode(), code), msgAndArgs...) - } - - return isSuccessCode -} - -// HTTPRedirect asserts that a specified handler returns a redirect status code. -// -// assert.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} -// -// Returns whether the assertion was successful (true) or not (false). -func HTTPRedirect(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - code, err := httpCode(handler, method, url, values) - if err != nil { - Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...) - } - - isRedirectCode := code >= http.StatusMultipleChoices && code <= http.StatusTemporaryRedirect - if !isRedirectCode { - Fail(t, fmt.Sprintf("Expected HTTP redirect status code for %q but received %d", url+"?"+values.Encode(), code), msgAndArgs...) - } - - return isRedirectCode -} - -// HTTPError asserts that a specified handler returns an error status code. -// -// assert.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} -// -// Returns whether the assertion was successful (true) or not (false). -func HTTPError(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - code, err := httpCode(handler, method, url, values) - if err != nil { - Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...) - } - - isErrorCode := code >= http.StatusBadRequest - if !isErrorCode { - Fail(t, fmt.Sprintf("Expected HTTP error status code for %q but received %d", url+"?"+values.Encode(), code), msgAndArgs...) - } - - return isErrorCode -} - -// HTTPStatusCode asserts that a specified handler returns a specified status code. -// -// assert.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) -// -// Returns whether the assertion was successful (true) or not (false). -func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - code, err := httpCode(handler, method, url, values) - if err != nil { - Fail(t, fmt.Sprintf("Failed to build test request, got error: %s", err), msgAndArgs...) - } - - successful := code == statuscode - if !successful { - Fail(t, fmt.Sprintf("Expected HTTP status code %d for %q but received %d", statuscode, url+"?"+values.Encode(), code), msgAndArgs...) - } - - return successful -} - -// HTTPBody is a helper that returns HTTP body of the response. It returns -// empty string if building a new request fails. -func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string { - w := httptest.NewRecorder() - if len(values) > 0 { - url += "?" + values.Encode() - } - req, err := http.NewRequest(method, url, http.NoBody) - if err != nil { - return "" - } - handler(w, req) - return w.Body.String() -} - -// HTTPBodyContains asserts that a specified handler returns a -// body that contains a string. -// -// assert.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") -// -// Returns whether the assertion was successful (true) or not (false). -func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - body := HTTPBody(handler, method, url, values) - - contains := strings.Contains(body, fmt.Sprint(str)) - if !contains { - Fail(t, fmt.Sprintf("Expected response body for %q to contain %q but found %q", url+"?"+values.Encode(), str, body), msgAndArgs...) - } - - return contains -} - -// HTTPBodyNotContains asserts that a specified handler returns a -// body that does not contain a string. -// -// assert.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") -// -// Returns whether the assertion was successful (true) or not (false). -func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) bool { - if h, ok := t.(tHelper); ok { - h.Helper() - } - body := HTTPBody(handler, method, url, values) - - contains := strings.Contains(body, fmt.Sprint(str)) - if contains { - Fail(t, fmt.Sprintf("Expected response body for %q to NOT contain %q but found %q", url+"?"+values.Encode(), str, body), msgAndArgs...) - } - - return !contains -} diff --git a/vendor/github.com/stretchr/testify/assert/yaml/yaml_custom.go b/vendor/github.com/stretchr/testify/assert/yaml/yaml_custom.go deleted file mode 100644 index 956227ca2..000000000 --- a/vendor/github.com/stretchr/testify/assert/yaml/yaml_custom.go +++ /dev/null @@ -1,24 +0,0 @@ -//go:build testify_yaml_custom && !testify_yaml_fail && !testify_yaml_default - -// Package yaml is an implementation of YAML functions that calls a pluggable implementation. -// -// This implementation is selected with the testify_yaml_custom build tag. -// -// go test -tags testify_yaml_custom -// -// This implementation can be used at build time to replace the default implementation -// to avoid linking with [go.yaml.in/yaml/v3]. -// -// In your test package: -// -// import assertYaml "github.com/stretchr/testify/assert/yaml" -// -// func init() { -// assertYaml.Unmarshal = func (in []byte, out interface{}) error { -// // ... -// return nil -// } -// } -package yaml - -var Unmarshal func(in []byte, out interface{}) error diff --git a/vendor/github.com/stretchr/testify/assert/yaml/yaml_default.go b/vendor/github.com/stretchr/testify/assert/yaml/yaml_default.go deleted file mode 100644 index dd89ac03a..000000000 --- a/vendor/github.com/stretchr/testify/assert/yaml/yaml_default.go +++ /dev/null @@ -1,36 +0,0 @@ -//go:build !testify_yaml_fail && !testify_yaml_custom - -// Package yaml is just an indirection to handle YAML deserialization. -// -// This package is just an indirection that allows the builder to override the -// indirection with an alternative implementation of this package that uses -// another implementation of YAML deserialization. This allows to not either not -// use YAML deserialization at all, or to use another implementation than -// [go.yaml.in/yaml/v3] (for example for license compatibility reasons, see [PR #1120]). -// -// Alternative implementations are selected using build tags: -// -// - testify_yaml_fail: [Unmarshal] always fails with an error -// - testify_yaml_custom: [Unmarshal] is a variable. Caller must initialize it -// before calling any of [github.com/stretchr/testify/assert.YAMLEq] or -// [github.com/stretchr/testify/assert.YAMLEqf]. -// -// Usage: -// -// go test -tags testify_yaml_fail -// -// You can check with "go list" which implementation is linked: -// -// go list -f '{{.Imports}}' github.com/stretchr/testify/assert/yaml -// go list -tags testify_yaml_fail -f '{{.Imports}}' github.com/stretchr/testify/assert/yaml -// go list -tags testify_yaml_custom -f '{{.Imports}}' github.com/stretchr/testify/assert/yaml -// -// [PR #1120]: https://github.com/stretchr/testify/pull/1120 -package yaml - -import goyaml "go.yaml.in/yaml/v3" - -// Unmarshal is just a wrapper of [go.yaml.in/yaml/v3.Unmarshal]. -func Unmarshal(in []byte, out interface{}) error { - return goyaml.Unmarshal(in, out) -} diff --git a/vendor/github.com/stretchr/testify/assert/yaml/yaml_fail.go b/vendor/github.com/stretchr/testify/assert/yaml/yaml_fail.go deleted file mode 100644 index a51d27925..000000000 --- a/vendor/github.com/stretchr/testify/assert/yaml/yaml_fail.go +++ /dev/null @@ -1,17 +0,0 @@ -//go:build testify_yaml_fail && !testify_yaml_custom && !testify_yaml_default - -// Package yaml is an implementation of YAML functions that always fail. -// -// This implementation can be used at build time to replace the default implementation -// to avoid linking with [go.yaml.in/yaml/v3]: -// -// go test -tags testify_yaml_fail -package yaml - -import "errors" - -var errNotImplemented = errors.New("YAML functions are not available (see https://pkg.go.dev/github.com/stretchr/testify/assert/yaml)") - -func Unmarshal([]byte, interface{}) error { - return errNotImplemented -} diff --git a/vendor/github.com/stretchr/testify/internal/difflib/LICENSE b/vendor/github.com/stretchr/testify/internal/difflib/LICENSE deleted file mode 100644 index 485be13c6..000000000 --- a/vendor/github.com/stretchr/testify/internal/difflib/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2013, Patrick Mezard -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - Redistributions in binary form must reproduce the above copyright -notice, this list of conditions and the following disclaimer in the -documentation and/or other materials provided with the distribution. - The names of its contributors may not be used to endorse or promote -products derived from this software without specific prior written -permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED -TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/vendor/github.com/stretchr/testify/internal/difflib/difflib.go b/vendor/github.com/stretchr/testify/internal/difflib/difflib.go deleted file mode 100644 index 9984599b4..000000000 --- a/vendor/github.com/stretchr/testify/internal/difflib/difflib.go +++ /dev/null @@ -1,592 +0,0 @@ -// Package difflib is a partial port of Python difflib module. -// -// It provides tools to compare sequences of strings and generate textual diffs. -// -// The following class and functions have been ported: -// -// - SequenceMatcher -// -// - unified_diff -// -// Getting unified diffs was the main goal of the port. Keep in mind this code -// is mostly suitable to output text differences in a human friendly way, there -// are no guarantees generated diffs are consumable by patch(1). -// -// This package was adopted from [github.com/pmezard/go-difflib] which -// is no longer maintained. -// -// [github.com/pmezard/go-difflib]: https://github.com/pmezard/go-difflib -package difflib - -import ( - "bufio" - "bytes" - "fmt" - "io" - "strings" -) - -func min(a, b int) int { - if a < b { - return a - } - return b -} - -func max(a, b int) int { - if a > b { - return a - } - return b -} - -type Match struct { - A int - B int - Size int -} - -type OpCode struct { - Tag byte - I1 int - I2 int - J1 int - J2 int -} - -// SequenceMatcher compares sequence of strings. The basic -// algorithm predates, and is a little fancier than, an algorithm -// published in the late 1980's by Ratcliff and Obershelp under the -// hyperbolic name "gestalt pattern matching". The basic idea is to find -// the longest contiguous matching subsequence that contains no "junk" -// elements (R-O doesn't address junk). The same idea is then applied -// recursively to the pieces of the sequences to the left and to the right -// of the matching subsequence. This does not yield minimal edit -// sequences, but does tend to yield matches that "look right" to people. -// -// SequenceMatcher tries to compute a "human-friendly diff" between two -// sequences. Unlike e.g. UNIX(tm) diff, the fundamental notion is the -// longest *contiguous* & junk-free matching subsequence. That's what -// catches peoples' eyes. The Windows(tm) windiff has another interesting -// notion, pairing up elements that appear uniquely in each sequence. -// That, and the method here, appear to yield more intuitive difference -// reports than does diff. This method appears to be the least vulnerable -// to synching up on blocks of "junk lines", though (like blank lines in -// ordinary text files, or maybe "

" lines in HTML files). That may be -// because this is the only method of the 3 that has a *concept* of -// "junk" . -// -// Timing: Basic R-O is cubic time worst case and quadratic time expected -// case. SequenceMatcher is quadratic time for the worst case and has -// expected-case behavior dependent in a complicated way on how many -// elements the sequences have in common; best case time is linear. -type SequenceMatcher struct { - a []string - b []string - b2j map[string][]int - IsJunk func(string) bool - autoJunk bool - bJunk map[string]struct{} - matchingBlocks []Match - fullBCount map[string]int - bPopular map[string]struct{} - opCodes []OpCode -} - -func NewMatcher(a, b []string) *SequenceMatcher { - m := SequenceMatcher{autoJunk: true} - m.SetSeqs(a, b) - return &m -} - -// Set two sequences to be compared. -func (m *SequenceMatcher) SetSeqs(a, b []string) { - m.SetSeq1(a) - m.SetSeq2(b) -} - -// Set the first sequence to be compared. The second sequence to be compared is -// not changed. -// -// SequenceMatcher computes and caches detailed information about the second -// sequence, so if you want to compare one sequence S against many sequences, -// use .SetSeq2(s) once and call .SetSeq1(x) repeatedly for each of the other -// sequences. -// -// See also SetSeqs() and SetSeq2(). -func (m *SequenceMatcher) SetSeq1(a []string) { - if &a == &m.a { - return - } - m.a = a - m.matchingBlocks = nil - m.opCodes = nil -} - -// Set the second sequence to be compared. The first sequence to be compared is -// not changed. -func (m *SequenceMatcher) SetSeq2(b []string) { - if &b == &m.b { - return - } - m.b = b - m.matchingBlocks = nil - m.opCodes = nil - m.fullBCount = nil - m.chainB() -} - -func (m *SequenceMatcher) chainB() { - // Populate line -> index mapping - b2j := map[string][]int{} - for i, s := range m.b { - indices := b2j[s] - indices = append(indices, i) - b2j[s] = indices - } - - // Purge junk elements - m.bJunk = map[string]struct{}{} - if m.IsJunk != nil { - junk := m.bJunk - for s, _ := range b2j { - if m.IsJunk(s) { - junk[s] = struct{}{} - } - } - for s, _ := range junk { - delete(b2j, s) - } - } - - // Purge remaining popular elements - popular := map[string]struct{}{} - n := len(m.b) - if m.autoJunk && n >= 200 { - ntest := n/100 + 1 - for s, indices := range b2j { - if len(indices) > ntest { - popular[s] = struct{}{} - } - } - for s, _ := range popular { - delete(b2j, s) - } - } - m.bPopular = popular - m.b2j = b2j -} - -func (m *SequenceMatcher) isBJunk(s string) bool { - _, ok := m.bJunk[s] - return ok -} - -// Find longest matching block in a[alo:ahi] and b[blo:bhi]. -// -// If IsJunk is not defined: -// -// Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where -// -// alo <= i <= i+k <= ahi -// blo <= j <= j+k <= bhi -// -// and for all (i',j',k') meeting those conditions, -// -// k >= k' -// i <= i' -// and if i == i', j <= j' -// -// In other words, of all maximal matching blocks, return one that -// starts earliest in a, and of all those maximal matching blocks that -// start earliest in a, return the one that starts earliest in b. -// -// If IsJunk is defined, first the longest matching block is -// determined as above, but with the additional restriction that no -// junk element appears in the block. Then that block is extended as -// far as possible by matching (only) junk elements on both sides. So -// the resulting block never matches on junk except as identical junk -// happens to be adjacent to an "interesting" match. -// -// If no blocks match, return (alo, blo, 0). -func (m *SequenceMatcher) findLongestMatch(alo, ahi, blo, bhi int) Match { - // CAUTION: stripping common prefix or suffix would be incorrect. - // E.g., - // ab - // acab - // Longest matching block is "ab", but if common prefix is - // stripped, it's "a" (tied with "b"). UNIX(tm) diff does so - // strip, so ends up claiming that ab is changed to acab by - // inserting "ca" in the middle. That's minimal but unintuitive: - // "it's obvious" that someone inserted "ac" at the front. - // Windiff ends up at the same place as diff, but by pairing up - // the unique 'b's and then matching the first two 'a's. - besti, bestj, bestsize := alo, blo, 0 - - // find longest junk-free match - // during an iteration of the loop, j2len[j] = length of longest - // junk-free match ending with a[i-1] and b[j] - j2len := map[int]int{} - for i := alo; i != ahi; i++ { - // look at all instances of a[i] in b; note that because - // b2j has no junk keys, the loop is skipped if a[i] is junk - newj2len := map[int]int{} - for _, j := range m.b2j[m.a[i]] { - // a[i] matches b[j] - if j < blo { - continue - } - if j >= bhi { - break - } - k := j2len[j-1] + 1 - newj2len[j] = k - if k > bestsize { - besti, bestj, bestsize = i-k+1, j-k+1, k - } - } - j2len = newj2len - } - - // Extend the best by non-junk elements on each end. In particular, - // "popular" non-junk elements aren't in b2j, which greatly speeds - // the inner loop above, but also means "the best" match so far - // doesn't contain any junk *or* popular non-junk elements. - for besti > alo && bestj > blo && !m.isBJunk(m.b[bestj-1]) && - m.a[besti-1] == m.b[bestj-1] { - besti, bestj, bestsize = besti-1, bestj-1, bestsize+1 - } - for besti+bestsize < ahi && bestj+bestsize < bhi && - !m.isBJunk(m.b[bestj+bestsize]) && - m.a[besti+bestsize] == m.b[bestj+bestsize] { - bestsize += 1 - } - - // Now that we have a wholly interesting match (albeit possibly - // empty!), we may as well suck up the matching junk on each - // side of it too. Can't think of a good reason not to, and it - // saves post-processing the (possibly considerable) expense of - // figuring out what to do with it. In the case of an empty - // interesting match, this is clearly the right thing to do, - // because no other kind of match is possible in the regions. - for besti > alo && bestj > blo && m.isBJunk(m.b[bestj-1]) && - m.a[besti-1] == m.b[bestj-1] { - besti, bestj, bestsize = besti-1, bestj-1, bestsize+1 - } - for besti+bestsize < ahi && bestj+bestsize < bhi && - m.isBJunk(m.b[bestj+bestsize]) && - m.a[besti+bestsize] == m.b[bestj+bestsize] { - bestsize += 1 - } - - return Match{A: besti, B: bestj, Size: bestsize} -} - -// Return list of triples describing matching subsequences. -// -// Each triple is of the form (i, j, n), and means that -// a[i:i+n] == b[j:j+n]. The triples are monotonically increasing in -// i and in j. It's also guaranteed that if (i, j, n) and (i', j', n') are -// adjacent triples in the list, and the second is not the last triple in the -// list, then i+n != i' or j+n != j'. IOW, adjacent triples never describe -// adjacent equal blocks. -// -// The last triple is a dummy, (len(a), len(b), 0), and is the only -// triple with n==0. -func (m *SequenceMatcher) GetMatchingBlocks() []Match { - if m.matchingBlocks != nil { - return m.matchingBlocks - } - - var matchBlocks func(alo, ahi, blo, bhi int, matched []Match) []Match - matchBlocks = func(alo, ahi, blo, bhi int, matched []Match) []Match { - match := m.findLongestMatch(alo, ahi, blo, bhi) - i, j, k := match.A, match.B, match.Size - if match.Size > 0 { - if alo < i && blo < j { - matched = matchBlocks(alo, i, blo, j, matched) - } - matched = append(matched, match) - if i+k < ahi && j+k < bhi { - matched = matchBlocks(i+k, ahi, j+k, bhi, matched) - } - } - return matched - } - matched := matchBlocks(0, len(m.a), 0, len(m.b), nil) - - // It's possible that we have adjacent equal blocks in the - // matching_blocks list now. - nonAdjacent := []Match{} - i1, j1, k1 := 0, 0, 0 - for _, b := range matched { - // Is this block adjacent to i1, j1, k1? - i2, j2, k2 := b.A, b.B, b.Size - if i1+k1 == i2 && j1+k1 == j2 { - // Yes, so collapse them -- this just increases the length of - // the first block by the length of the second, and the first - // block so lengthened remains the block to compare against. - k1 += k2 - } else { - // Not adjacent. Remember the first block (k1==0 means it's - // the dummy we started with), and make the second block the - // new block to compare against. - if k1 > 0 { - nonAdjacent = append(nonAdjacent, Match{i1, j1, k1}) - } - i1, j1, k1 = i2, j2, k2 - } - } - if k1 > 0 { - nonAdjacent = append(nonAdjacent, Match{i1, j1, k1}) - } - - nonAdjacent = append(nonAdjacent, Match{len(m.a), len(m.b), 0}) - m.matchingBlocks = nonAdjacent - return m.matchingBlocks -} - -// Return list of 5-tuples describing how to turn a into b. -// -// Each tuple is of the form (tag, i1, i2, j1, j2). The first tuple -// has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the -// tuple preceding it, and likewise for j1 == the previous j2. -// -// The tags are characters, with these meanings: -// -// 'r' (replace): a[i1:i2] should be replaced by b[j1:j2] -// -// 'd' (delete): a[i1:i2] should be deleted, j1==j2 in this case. -// -// 'i' (insert): b[j1:j2] should be inserted at a[i1:i1], i1==i2 in this case. -// -// 'e' (equal): a[i1:i2] == b[j1:j2] -func (m *SequenceMatcher) GetOpCodes() []OpCode { - if m.opCodes != nil { - return m.opCodes - } - i, j := 0, 0 - matching := m.GetMatchingBlocks() - opCodes := make([]OpCode, 0, len(matching)) - for _, m := range matching { - // invariant: we've pumped out correct diffs to change - // a[:i] into b[:j], and the next matching block is - // a[ai:ai+size] == b[bj:bj+size]. So we need to pump - // out a diff to change a[i:ai] into b[j:bj], pump out - // the matching block, and move (i,j) beyond the match - ai, bj, size := m.A, m.B, m.Size - tag := byte(0) - if i < ai && j < bj { - tag = 'r' - } else if i < ai { - tag = 'd' - } else if j < bj { - tag = 'i' - } - if tag > 0 { - opCodes = append(opCodes, OpCode{tag, i, ai, j, bj}) - } - i, j = ai+size, bj+size - // the list of matching blocks is terminated by a - // sentinel with size 0 - if size > 0 { - opCodes = append(opCodes, OpCode{'e', ai, i, bj, j}) - } - } - m.opCodes = opCodes - return m.opCodes -} - -// Isolate change clusters by eliminating ranges with no changes. -// -// Return a generator of groups with up to n lines of context. -// Each group is in the same format as returned by GetOpCodes(). -func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode { - if n < 0 { - n = 3 - } - codes := m.GetOpCodes() - if len(codes) == 0 { - codes = []OpCode{OpCode{'e', 0, 1, 0, 1}} - } - // Fixup leading and trailing groups if they show no changes. - if codes[0].Tag == 'e' { - c := codes[0] - i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 - codes[0] = OpCode{c.Tag, max(i1, i2-n), i2, max(j1, j2-n), j2} - } - if codes[len(codes)-1].Tag == 'e' { - c := codes[len(codes)-1] - i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 - codes[len(codes)-1] = OpCode{c.Tag, i1, min(i2, i1+n), j1, min(j2, j1+n)} - } - nn := n + n - groups := [][]OpCode{} - group := []OpCode{} - for _, c := range codes { - i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 - // End the current group and start a new one whenever - // there is a large range with no changes. - if c.Tag == 'e' && i2-i1 > nn { - group = append(group, OpCode{c.Tag, i1, min(i2, i1+n), - j1, min(j2, j1+n)}) - groups = append(groups, group) - group = []OpCode{} - i1, j1 = max(i1, i2-n), max(j1, j2-n) - } - group = append(group, OpCode{c.Tag, i1, i2, j1, j2}) - } - if len(group) > 0 && !(len(group) == 1 && group[0].Tag == 'e') { - groups = append(groups, group) - } - return groups -} - -// Convert range to the "ed" format -func formatRangeUnified(start, stop int) string { - // Per the diff spec at http://www.unix.org/single_unix_specification/ - beginning := start + 1 // lines start numbering with one - length := stop - start - if length == 1 { - return fmt.Sprintf("%d", beginning) - } - if length == 0 { - beginning -= 1 // empty ranges begin at line just before the range - } - return fmt.Sprintf("%d,%d", beginning, length) -} - -// Unified diff parameters -type UnifiedDiff struct { - A []string // First sequence lines - FromFile string // First file name - FromDate string // First file time - B []string // Second sequence lines - ToFile string // Second file name - ToDate string // Second file time - Eol string // Headers end of line, defaults to LF - Context int // Number of context lines -} - -// Compare two sequences of lines; generate the delta as a unified diff. -// -// Unified diffs are a compact way of showing line changes and a few -// lines of context. The number of context lines is set by 'n' which -// defaults to three. -// -// By default, the diff control lines (those with ---, +++, or @@) are -// created with a trailing newline. This is helpful so that inputs -// created from file.readlines() result in diffs that are suitable for -// file.writelines() since both the inputs and outputs have trailing -// newlines. -// -// For inputs that do not have trailing newlines, set the lineterm -// argument to "" so that the output will be uniformly newline free. -// -// The unidiff format normally has a header for filenames and modification -// times. Any or all of these may be specified using strings for -// 'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'. -// The modification times are normally expressed in the ISO 8601 format. -func WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error { - buf := bufio.NewWriter(writer) - defer buf.Flush() - wf := func(format string, args ...interface{}) error { - _, err := buf.WriteString(fmt.Sprintf(format, args...)) - return err - } - ws := func(s string) error { - _, err := buf.WriteString(s) - return err - } - - if len(diff.Eol) == 0 { - diff.Eol = "\n" - } - - started := false - m := NewMatcher(diff.A, diff.B) - for _, g := range m.GetGroupedOpCodes(diff.Context) { - if !started { - started = true - fromDate := "" - if len(diff.FromDate) > 0 { - fromDate = "\t" + diff.FromDate - } - toDate := "" - if len(diff.ToDate) > 0 { - toDate = "\t" + diff.ToDate - } - if diff.FromFile != "" || diff.ToFile != "" { - err := wf("--- %s%s%s", diff.FromFile, fromDate, diff.Eol) - if err != nil { - return err - } - err = wf("+++ %s%s%s", diff.ToFile, toDate, diff.Eol) - if err != nil { - return err - } - } - } - first, last := g[0], g[len(g)-1] - range1 := formatRangeUnified(first.I1, last.I2) - range2 := formatRangeUnified(first.J1, last.J2) - if err := wf("@@ -%s +%s @@%s", range1, range2, diff.Eol); err != nil { - return err - } - for _, c := range g { - i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 - if c.Tag == 'e' { - for _, line := range diff.A[i1:i2] { - if err := ws(" " + line); err != nil { - return err - } - } - continue - } - if c.Tag == 'r' || c.Tag == 'd' { - for _, line := range diff.A[i1:i2] { - if err := ws("-" + line); err != nil { - return err - } - } - } - if c.Tag == 'r' || c.Tag == 'i' { - for _, line := range diff.B[j1:j2] { - if err := ws("+" + line); err != nil { - return err - } - } - } - } - } - return nil -} - -// Like WriteUnifiedDiff but returns the diff a string. -func GetUnifiedDiffString(diff UnifiedDiff) (string, error) { - w := &bytes.Buffer{} - err := WriteUnifiedDiff(w, diff) - return string(w.Bytes()), err -} - -// Convert range to the "ed" format. -func formatRangeContext(start, stop int) string { - // Per the diff spec at http://www.unix.org/single_unix_specification/ - beginning := start + 1 // lines start numbering with one - length := stop - start - if length == 0 { - beginning -= 1 // empty ranges begin at line just before the range - } - if length <= 1 { - return fmt.Sprintf("%d", beginning) - } - return fmt.Sprintf("%d,%d", beginning, beginning+length-1) -} - -// Split a string on "\n" while preserving them. The output can be used -// as input for UnifiedDiff and ContextDiff structures. -func SplitLines(s string) []string { - lines := strings.SplitAfter(s, "\n") - lines[len(lines)-1] += "\n" - return lines -} diff --git a/vendor/github.com/stretchr/testify/internal/spew/LICENSE b/vendor/github.com/stretchr/testify/internal/spew/LICENSE deleted file mode 100644 index bc52e96f2..000000000 --- a/vendor/github.com/stretchr/testify/internal/spew/LICENSE +++ /dev/null @@ -1,15 +0,0 @@ -ISC License - -Copyright (c) 2012-2016 Dave Collins - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/vendor/github.com/stretchr/testify/internal/spew/README.md b/vendor/github.com/stretchr/testify/internal/spew/README.md deleted file mode 100644 index 51a909e2e..000000000 --- a/vendor/github.com/stretchr/testify/internal/spew/README.md +++ /dev/null @@ -1,12 +0,0 @@ -go-spew -======= - -[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) - -Go-spew implements a deep pretty printer for Go data structures to aid in -debugging. A comprehensive suite of tests with 100% test coverage is provided -to ensure proper functionality. - -## License - -Go-spew is licensed under the [copyfree](http://copyfree.org) ISC License. diff --git a/vendor/github.com/stretchr/testify/internal/spew/bypass.go b/vendor/github.com/stretchr/testify/internal/spew/bypass.go deleted file mode 100644 index 70ddeaad3..000000000 --- a/vendor/github.com/stretchr/testify/internal/spew/bypass.go +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright (c) 2015-2016 Dave Collins -// -// Permission to use, copy, modify, and distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -// NOTE: Due to the following build constraints, this file will only be compiled -// when the code is not running on Google App Engine, compiled by GopherJS, and -// "-tags safe" is not added to the go build command line. The "disableunsafe" -// tag is deprecated and thus should not be used. -// Go versions prior to 1.4 are disabled because they use a different layout -// for interfaces which make the implementation of unsafeReflectValue more complex. -//go:build !js && !appengine && !safe && !disableunsafe && go1.4 -// +build !js,!appengine,!safe,!disableunsafe,go1.4 - -package spew - -import ( - "reflect" - "unsafe" -) - -const ( - // UnsafeDisabled is a build-time constant which specifies whether or - // not access to the unsafe package is available. - UnsafeDisabled = false - - // ptrSize is the size of a pointer on the current arch. - ptrSize = unsafe.Sizeof((*byte)(nil)) -) - -type flag uintptr - -var ( - // flagRO indicates whether the value field of a reflect.Value - // is read-only. - flagRO flag - - // flagAddr indicates whether the address of the reflect.Value's - // value may be taken. - flagAddr flag -) - -// flagKindMask holds the bits that make up the kind -// part of the flags field. In all the supported versions, -// it is in the lower 5 bits. -const flagKindMask = flag(0x1f) - -// Different versions of Go have used different -// bit layouts for the flags type. This table -// records the known combinations. -var okFlags = []struct { - ro, addr flag -}{{ - // From Go 1.4 to 1.5 - ro: 1 << 5, - addr: 1 << 7, -}, { - // Up to Go tip. - ro: 1<<5 | 1<<6, - addr: 1 << 8, -}} - -var flagValOffset = func() uintptr { - field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") - if !ok { - panic("reflect.Value has no flag field") - } - return field.Offset -}() - -// flagField returns a pointer to the flag field of a reflect.Value. -func flagField(v *reflect.Value) *flag { - return (*flag)(unsafe.Pointer(uintptr(unsafe.Pointer(v)) + flagValOffset)) -} - -// unsafeReflectValue converts the passed reflect.Value into a one that bypasses -// the typical safety restrictions preventing access to unaddressable and -// unexported data. It works by digging the raw pointer to the underlying -// value out of the protected value and generating a new unprotected (unsafe) -// reflect.Value to it. -// -// This allows us to check for implementations of the Stringer and error -// interfaces to be used for pretty printing ordinarily unaddressable and -// inaccessible values such as unexported struct fields. -func unsafeReflectValue(v reflect.Value) reflect.Value { - if !v.IsValid() || (v.CanInterface() && v.CanAddr()) { - return v - } - flagFieldPtr := flagField(&v) - *flagFieldPtr &^= flagRO - *flagFieldPtr |= flagAddr - return v -} - -// Sanity checks against future reflect package changes -// to the type or semantics of the Value.flag field. -func init() { - field, ok := reflect.TypeOf(reflect.Value{}).FieldByName("flag") - if !ok { - panic("reflect.Value has no flag field") - } - if field.Type.Kind() != reflect.TypeOf(flag(0)).Kind() { - panic("reflect.Value flag field has changed kind") - } - type t0 int - var t struct { - A t0 - // t0 will have flagEmbedRO set. - t0 - // a will have flagStickyRO set - a t0 - } - vA := reflect.ValueOf(t).FieldByName("A") - va := reflect.ValueOf(t).FieldByName("a") - vt0 := reflect.ValueOf(t).FieldByName("t0") - - // Infer flagRO from the difference between the flags - // for the (otherwise identical) fields in t. - flagPublic := *flagField(&vA) - flagWithRO := *flagField(&va) | *flagField(&vt0) - flagRO = flagPublic ^ flagWithRO - - // Infer flagAddr from the difference between a value - // taken from a pointer and not. - vPtrA := reflect.ValueOf(&t).Elem().FieldByName("A") - flagNoPtr := *flagField(&vA) - flagPtr := *flagField(&vPtrA) - flagAddr = flagNoPtr ^ flagPtr - - // Check that the inferred flags tally with one of the known versions. - for _, f := range okFlags { - if flagRO == f.ro && flagAddr == f.addr { - return - } - } - panic("reflect.Value read-only flag has changed semantics") -} diff --git a/vendor/github.com/stretchr/testify/internal/spew/bypasssafe.go b/vendor/github.com/stretchr/testify/internal/spew/bypasssafe.go deleted file mode 100644 index 5e2d890d6..000000000 --- a/vendor/github.com/stretchr/testify/internal/spew/bypasssafe.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright (c) 2015-2016 Dave Collins -// -// Permission to use, copy, modify, and distribute this software for any -// purpose with or without fee is hereby granted, provided that the above -// copyright notice and this permission notice appear in all copies. -// -// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -// NOTE: Due to the following build constraints, this file will only be compiled -// when the code is running on Google App Engine, compiled by GopherJS, or -// "-tags safe" is added to the go build command line. The "disableunsafe" -// tag is deprecated and thus should not be used. -//go:build js || appengine || safe || disableunsafe || !go1.4 -// +build js appengine safe disableunsafe !go1.4 - -package spew - -import "reflect" - -const ( - // UnsafeDisabled is a build-time constant which specifies whether or - // not access to the unsafe package is available. - UnsafeDisabled = true -) - -// unsafeReflectValue typically converts the passed reflect.Value into a one -// that bypasses the typical safety restrictions preventing access to -// unaddressable and unexported data. However, doing this relies on access to -// the unsafe package. This is a stub version which simply returns the passed -// reflect.Value when the unsafe package is not available. -func unsafeReflectValue(v reflect.Value) reflect.Value { - return v -} diff --git a/vendor/github.com/stretchr/testify/internal/spew/common.go b/vendor/github.com/stretchr/testify/internal/spew/common.go deleted file mode 100644 index 1be8ce945..000000000 --- a/vendor/github.com/stretchr/testify/internal/spew/common.go +++ /dev/null @@ -1,341 +0,0 @@ -/* - * Copyright (c) 2013-2016 Dave Collins - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -package spew - -import ( - "bytes" - "fmt" - "io" - "reflect" - "sort" - "strconv" -) - -// Some constants in the form of bytes to avoid string overhead. This mirrors -// the technique used in the fmt package. -var ( - panicBytes = []byte("(PANIC=") - plusBytes = []byte("+") - iBytes = []byte("i") - trueBytes = []byte("true") - falseBytes = []byte("false") - interfaceBytes = []byte("(interface {})") - commaNewlineBytes = []byte(",\n") - newlineBytes = []byte("\n") - openBraceBytes = []byte("{") - openBraceNewlineBytes = []byte("{\n") - closeBraceBytes = []byte("}") - asteriskBytes = []byte("*") - colonBytes = []byte(":") - colonSpaceBytes = []byte(": ") - openParenBytes = []byte("(") - closeParenBytes = []byte(")") - spaceBytes = []byte(" ") - pointerChainBytes = []byte("->") - nilAngleBytes = []byte("") - maxNewlineBytes = []byte("\n") - maxShortBytes = []byte("") - circularBytes = []byte("") - circularShortBytes = []byte("") - invalidAngleBytes = []byte("") - openBracketBytes = []byte("[") - closeBracketBytes = []byte("]") - percentBytes = []byte("%") - precisionBytes = []byte(".") - openAngleBytes = []byte("<") - closeAngleBytes = []byte(">") - openMapBytes = []byte("map[") - closeMapBytes = []byte("]") - lenEqualsBytes = []byte("len=") - capEqualsBytes = []byte("cap=") -) - -// hexDigits is used to map a decimal value to a hex digit. -var hexDigits = "0123456789abcdef" - -// catchPanic handles any panics that might occur during the handleMethods -// calls. -func catchPanic(w io.Writer, v reflect.Value) { - if err := recover(); err != nil { - w.Write(panicBytes) - fmt.Fprintf(w, "%v", err) - w.Write(closeParenBytes) - } -} - -// handleMethods attempts to call the Error and String methods on the underlying -// type the passed reflect.Value represents and outputes the result to Writer w. -// -// It handles panics in any called methods by catching and displaying the error -// as the formatted value. -func handleMethods(cs *ConfigState, w io.Writer, v reflect.Value) (handled bool) { - // We need an interface to check if the type implements the error or - // Stringer interface. However, the reflect package won't give us an - // interface on certain things like unexported struct fields in order - // to enforce visibility rules. We use unsafe, when it's available, - // to bypass these restrictions since this package does not mutate the - // values. - if !v.CanInterface() { - if UnsafeDisabled { - return false - } - - v = unsafeReflectValue(v) - } - - // Choose whether or not to do error and Stringer interface lookups against - // the base type or a pointer to the base type depending on settings. - // Technically calling one of these methods with a pointer receiver can - // mutate the value, however, types which choose to satisify an error or - // Stringer interface with a pointer receiver should not be mutating their - // state inside these interface methods. - if !cs.DisablePointerMethods && !UnsafeDisabled && !v.CanAddr() { - v = unsafeReflectValue(v) - } - if v.CanAddr() { - v = v.Addr() - } - - // Is it an error or Stringer? - switch iface := v.Interface().(type) { - case error: - defer catchPanic(w, v) - if cs.ContinueOnMethod { - w.Write(openParenBytes) - w.Write([]byte(iface.Error())) - w.Write(closeParenBytes) - w.Write(spaceBytes) - return false - } - - w.Write([]byte(iface.Error())) - return true - - case fmt.Stringer: - defer catchPanic(w, v) - if cs.ContinueOnMethod { - w.Write(openParenBytes) - w.Write([]byte(iface.String())) - w.Write(closeParenBytes) - w.Write(spaceBytes) - return false - } - w.Write([]byte(iface.String())) - return true - } - return false -} - -// printBool outputs a boolean value as true or false to Writer w. -func printBool(w io.Writer, val bool) { - if val { - w.Write(trueBytes) - } else { - w.Write(falseBytes) - } -} - -// printInt outputs a signed integer value to Writer w. -func printInt(w io.Writer, val int64, base int) { - w.Write([]byte(strconv.FormatInt(val, base))) -} - -// printUint outputs an unsigned integer value to Writer w. -func printUint(w io.Writer, val uint64, base int) { - w.Write([]byte(strconv.FormatUint(val, base))) -} - -// printFloat outputs a floating point value using the specified precision, -// which is expected to be 32 or 64bit, to Writer w. -func printFloat(w io.Writer, val float64, precision int) { - w.Write([]byte(strconv.FormatFloat(val, 'g', -1, precision))) -} - -// printComplex outputs a complex value using the specified float precision -// for the real and imaginary parts to Writer w. -func printComplex(w io.Writer, c complex128, floatPrecision int) { - r := real(c) - w.Write(openParenBytes) - w.Write([]byte(strconv.FormatFloat(r, 'g', -1, floatPrecision))) - i := imag(c) - if i >= 0 { - w.Write(plusBytes) - } - w.Write([]byte(strconv.FormatFloat(i, 'g', -1, floatPrecision))) - w.Write(iBytes) - w.Write(closeParenBytes) -} - -// printHexPtr outputs a uintptr formatted as hexadecimal with a leading '0x' -// prefix to Writer w. -func printHexPtr(w io.Writer, p uintptr) { - // Null pointer. - num := uint64(p) - if num == 0 { - w.Write(nilAngleBytes) - return - } - - // Max uint64 is 16 bytes in hex + 2 bytes for '0x' prefix - buf := make([]byte, 18) - - // It's simpler to construct the hex string right to left. - base := uint64(16) - i := len(buf) - 1 - for num >= base { - buf[i] = hexDigits[num%base] - num /= base - i-- - } - buf[i] = hexDigits[num] - - // Add '0x' prefix. - i-- - buf[i] = 'x' - i-- - buf[i] = '0' - - // Strip unused leading bytes. - buf = buf[i:] - w.Write(buf) -} - -// valuesSorter implements sort.Interface to allow a slice of reflect.Value -// elements to be sorted. -type valuesSorter struct { - values []reflect.Value - strings []string // either nil or same len and values - cs *ConfigState -} - -// newValuesSorter initializes a valuesSorter instance, which holds a set of -// surrogate keys on which the data should be sorted. It uses flags in -// ConfigState to decide if and how to populate those surrogate keys. -func newValuesSorter(values []reflect.Value, cs *ConfigState) sort.Interface { - vs := &valuesSorter{values: values, cs: cs} - if canSortSimply(vs.values[0].Kind()) { - return vs - } - if !cs.DisableMethods { - vs.strings = make([]string, len(values)) - for i := range vs.values { - b := bytes.Buffer{} - if !handleMethods(cs, &b, vs.values[i]) { - vs.strings = nil - break - } - vs.strings[i] = b.String() - } - } - if vs.strings == nil && cs.SpewKeys { - vs.strings = make([]string, len(values)) - for i := range vs.values { - vs.strings[i] = Sprintf("%#v", vs.values[i].Interface()) - } - } - return vs -} - -// canSortSimply tests whether a reflect.Kind is a primitive that can be sorted -// directly, or whether it should be considered for sorting by surrogate keys -// (if the ConfigState allows it). -func canSortSimply(kind reflect.Kind) bool { - // This switch parallels valueSortLess, except for the default case. - switch kind { - case reflect.Bool: - return true - case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: - return true - case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: - return true - case reflect.Float32, reflect.Float64: - return true - case reflect.String: - return true - case reflect.Uintptr: - return true - case reflect.Array: - return true - } - return false -} - -// Len returns the number of values in the slice. It is part of the -// sort.Interface implementation. -func (s *valuesSorter) Len() int { - return len(s.values) -} - -// Swap swaps the values at the passed indices. It is part of the -// sort.Interface implementation. -func (s *valuesSorter) Swap(i, j int) { - s.values[i], s.values[j] = s.values[j], s.values[i] - if s.strings != nil { - s.strings[i], s.strings[j] = s.strings[j], s.strings[i] - } -} - -// valueSortLess returns whether the first value should sort before the second -// value. It is used by valueSorter.Less as part of the sort.Interface -// implementation. -func valueSortLess(a, b reflect.Value) bool { - switch a.Kind() { - case reflect.Bool: - return !a.Bool() && b.Bool() - case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: - return a.Int() < b.Int() - case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: - return a.Uint() < b.Uint() - case reflect.Float32, reflect.Float64: - return a.Float() < b.Float() - case reflect.String: - return a.String() < b.String() - case reflect.Uintptr: - return a.Uint() < b.Uint() - case reflect.Array: - // Compare the contents of both arrays. - l := a.Len() - for i := 0; i < l; i++ { - av := a.Index(i) - bv := b.Index(i) - if av.Interface() == bv.Interface() { - continue - } - return valueSortLess(av, bv) - } - } - return a.String() < b.String() -} - -// Less returns whether the value at index i should sort before the -// value at index j. It is part of the sort.Interface implementation. -func (s *valuesSorter) Less(i, j int) bool { - if s.strings == nil { - return valueSortLess(s.values[i], s.values[j]) - } - return s.strings[i] < s.strings[j] -} - -// sortValues is a sort function that handles both native types and any type that -// can be converted to error or Stringer. Other inputs are sorted according to -// their Value.String() value to ensure display stability. -func sortValues(values []reflect.Value, cs *ConfigState) { - if len(values) == 0 { - return - } - sort.Sort(newValuesSorter(values, cs)) -} diff --git a/vendor/github.com/stretchr/testify/internal/spew/config.go b/vendor/github.com/stretchr/testify/internal/spew/config.go deleted file mode 100644 index 161895fc6..000000000 --- a/vendor/github.com/stretchr/testify/internal/spew/config.go +++ /dev/null @@ -1,306 +0,0 @@ -/* - * Copyright (c) 2013-2016 Dave Collins - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -package spew - -import ( - "bytes" - "fmt" - "io" - "os" -) - -// ConfigState houses the configuration options used by spew to format and -// display values. There is a global instance, Config, that is used to control -// all top-level Formatter and Dump functionality. Each ConfigState instance -// provides methods equivalent to the top-level functions. -// -// The zero value for ConfigState provides no indentation. You would typically -// want to set it to a space or a tab. -// -// Alternatively, you can use NewDefaultConfig to get a ConfigState instance -// with default settings. See the documentation of NewDefaultConfig for default -// values. -type ConfigState struct { - // Indent specifies the string to use for each indentation level. The - // global config instance that all top-level functions use set this to a - // single space by default. If you would like more indentation, you might - // set this to a tab with "\t" or perhaps two spaces with " ". - Indent string - - // MaxDepth controls the maximum number of levels to descend into nested - // data structures. The default, 0, means there is no limit. - // - // NOTE: Circular data structures are properly detected, so it is not - // necessary to set this value unless you specifically want to limit deeply - // nested data structures. - MaxDepth int - - // DisableMethods specifies whether or not error and Stringer interfaces are - // invoked for types that implement them. - DisableMethods bool - - // DisablePointerMethods specifies whether or not to check for and invoke - // error and Stringer interfaces on types which only accept a pointer - // receiver when the current type is not a pointer. - // - // NOTE: This might be an unsafe action since calling one of these methods - // with a pointer receiver could technically mutate the value, however, - // in practice, types which choose to satisify an error or Stringer - // interface with a pointer receiver should not be mutating their state - // inside these interface methods. As a result, this option relies on - // access to the unsafe package, so it will not have any effect when - // running in environments without access to the unsafe package such as - // Google App Engine or with the "safe" build tag specified. - DisablePointerMethods bool - - // DisablePointerAddresses specifies whether to disable the printing of - // pointer addresses. This is useful when diffing data structures in tests. - DisablePointerAddresses bool - - // DisableCapacities specifies whether to disable the printing of capacities - // for arrays, slices, maps and channels. This is useful when diffing - // data structures in tests. - DisableCapacities bool - - // ContinueOnMethod specifies whether or not recursion should continue once - // a custom error or Stringer interface is invoked. The default, false, - // means it will print the results of invoking the custom error or Stringer - // interface and return immediately instead of continuing to recurse into - // the internals of the data type. - // - // NOTE: This flag does not have any effect if method invocation is disabled - // via the DisableMethods or DisablePointerMethods options. - ContinueOnMethod bool - - // SortKeys specifies map keys should be sorted before being printed. Use - // this to have a more deterministic, diffable output. Note that only - // native types (bool, int, uint, floats, uintptr and string) and types - // that support the error or Stringer interfaces (if methods are - // enabled) are supported, with other types sorted according to the - // reflect.Value.String() output which guarantees display stability. - SortKeys bool - - // SpewKeys specifies that, as a last resort attempt, map keys should - // be spewed to strings and sorted by those strings. This is only - // considered if SortKeys is true. - SpewKeys bool -} - -// Config is the active configuration of the top-level functions. -// The configuration can be changed by modifying the contents of spew.Config. -var Config = ConfigState{Indent: " "} - -// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were -// passed with a Formatter interface returned by c.NewFormatter. It returns -// the formatted string as a value that satisfies error. See NewFormatter -// for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Errorf(format, c.NewFormatter(a), c.NewFormatter(b)) -func (c *ConfigState) Errorf(format string, a ...interface{}) (err error) { - return fmt.Errorf(format, c.convertArgs(a)...) -} - -// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were -// passed with a Formatter interface returned by c.NewFormatter. It returns -// the number of bytes written and any write error encountered. See -// NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Fprint(w, c.NewFormatter(a), c.NewFormatter(b)) -func (c *ConfigState) Fprint(w io.Writer, a ...interface{}) (n int, err error) { - return fmt.Fprint(w, c.convertArgs(a)...) -} - -// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were -// passed with a Formatter interface returned by c.NewFormatter. It returns -// the number of bytes written and any write error encountered. See -// NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Fprintf(w, format, c.NewFormatter(a), c.NewFormatter(b)) -func (c *ConfigState) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { - return fmt.Fprintf(w, format, c.convertArgs(a)...) -} - -// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it -// passed with a Formatter interface returned by c.NewFormatter. See -// NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Fprintln(w, c.NewFormatter(a), c.NewFormatter(b)) -func (c *ConfigState) Fprintln(w io.Writer, a ...interface{}) (n int, err error) { - return fmt.Fprintln(w, c.convertArgs(a)...) -} - -// Print is a wrapper for fmt.Print that treats each argument as if it were -// passed with a Formatter interface returned by c.NewFormatter. It returns -// the number of bytes written and any write error encountered. See -// NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Print(c.NewFormatter(a), c.NewFormatter(b)) -func (c *ConfigState) Print(a ...interface{}) (n int, err error) { - return fmt.Print(c.convertArgs(a)...) -} - -// Printf is a wrapper for fmt.Printf that treats each argument as if it were -// passed with a Formatter interface returned by c.NewFormatter. It returns -// the number of bytes written and any write error encountered. See -// NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Printf(format, c.NewFormatter(a), c.NewFormatter(b)) -func (c *ConfigState) Printf(format string, a ...interface{}) (n int, err error) { - return fmt.Printf(format, c.convertArgs(a)...) -} - -// Println is a wrapper for fmt.Println that treats each argument as if it were -// passed with a Formatter interface returned by c.NewFormatter. It returns -// the number of bytes written and any write error encountered. See -// NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Println(c.NewFormatter(a), c.NewFormatter(b)) -func (c *ConfigState) Println(a ...interface{}) (n int, err error) { - return fmt.Println(c.convertArgs(a)...) -} - -// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were -// passed with a Formatter interface returned by c.NewFormatter. It returns -// the resulting string. See NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Sprint(c.NewFormatter(a), c.NewFormatter(b)) -func (c *ConfigState) Sprint(a ...interface{}) string { - return fmt.Sprint(c.convertArgs(a)...) -} - -// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were -// passed with a Formatter interface returned by c.NewFormatter. It returns -// the resulting string. See NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Sprintf(format, c.NewFormatter(a), c.NewFormatter(b)) -func (c *ConfigState) Sprintf(format string, a ...interface{}) string { - return fmt.Sprintf(format, c.convertArgs(a)...) -} - -// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it -// were passed with a Formatter interface returned by c.NewFormatter. It -// returns the resulting string. See NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Sprintln(c.NewFormatter(a), c.NewFormatter(b)) -func (c *ConfigState) Sprintln(a ...interface{}) string { - return fmt.Sprintln(c.convertArgs(a)...) -} - -/* -NewFormatter returns a custom formatter that satisfies the fmt.Formatter -interface. As a result, it integrates cleanly with standard fmt package -printing functions. The formatter is useful for inline printing of smaller data -types similar to the standard %v format specifier. - -The custom formatter only responds to the %v (most compact), %+v (adds pointer -addresses), %#v (adds types), and %#+v (adds types and pointer addresses) verb -combinations. Any other verbs such as %x and %q will be sent to the the -standard fmt package for formatting. In addition, the custom formatter ignores -the width and precision arguments (however they will still work on the format -specifiers not handled by the custom formatter). - -Typically this function shouldn't be called directly. It is much easier to make -use of the custom formatter by calling one of the convenience functions such as -c.Printf, c.Println, or c.Printf. -*/ -func (c *ConfigState) NewFormatter(v interface{}) fmt.Formatter { - return newFormatter(c, v) -} - -// Fdump formats and displays the passed arguments to io.Writer w. It formats -// exactly the same as Dump. -func (c *ConfigState) Fdump(w io.Writer, a ...interface{}) { - fdump(c, w, a...) -} - -/* -Dump displays the passed parameters to standard out with newlines, customizable -indentation, and additional debug information such as complete types and all -pointer addresses used to indirect to the final value. It provides the -following features over the built-in printing facilities provided by the fmt -package: - - - Pointers are dereferenced and followed - - Circular data structures are detected and handled properly - - Custom Stringer/error interfaces are optionally invoked, including - on unexported types - - Custom types which only implement the Stringer/error interfaces via - a pointer receiver are optionally invoked when passing non-pointer - variables - - Byte arrays and slices are dumped like the hexdump -C command which - includes offsets, byte values in hex, and ASCII output - -The configuration options are controlled by modifying the public members -of c. See ConfigState for options documentation. - -See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to -get the formatted result as a string. -*/ -func (c *ConfigState) Dump(a ...interface{}) { - fdump(c, os.Stdout, a...) -} - -// Sdump returns a string with the passed arguments formatted exactly the same -// as Dump. -func (c *ConfigState) Sdump(a ...interface{}) string { - var buf bytes.Buffer - fdump(c, &buf, a...) - return buf.String() -} - -// convertArgs accepts a slice of arguments and returns a slice of the same -// length with each argument converted to a spew Formatter interface using -// the ConfigState associated with s. -func (c *ConfigState) convertArgs(args []interface{}) (formatters []interface{}) { - formatters = make([]interface{}, len(args)) - for index, arg := range args { - formatters[index] = newFormatter(c, arg) - } - return formatters -} - -// NewDefaultConfig returns a ConfigState with the following default settings. -// -// Indent: " " -// MaxDepth: 0 -// DisableMethods: false -// DisablePointerMethods: false -// ContinueOnMethod: false -// SortKeys: false -func NewDefaultConfig() *ConfigState { - return &ConfigState{Indent: " "} -} diff --git a/vendor/github.com/stretchr/testify/internal/spew/doc.go b/vendor/github.com/stretchr/testify/internal/spew/doc.go deleted file mode 100644 index 722e9aa79..000000000 --- a/vendor/github.com/stretchr/testify/internal/spew/doc.go +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright (c) 2013-2016 Dave Collins - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -/* -Package spew implements a deep pretty printer for Go data structures to aid in -debugging. - -A quick overview of the additional features spew provides over the built-in -printing facilities for Go data types are as follows: - - - Pointers are dereferenced and followed - - Circular data structures are detected and handled properly - - Custom Stringer/error interfaces are optionally invoked, including - on unexported types - - Custom types which only implement the Stringer/error interfaces via - a pointer receiver are optionally invoked when passing non-pointer - variables - - Byte arrays and slices are dumped like the hexdump -C command which - includes offsets, byte values in hex, and ASCII output (only when using - Dump style) - -There are two different approaches spew allows for dumping Go data structures: - - - Dump style which prints with newlines, customizable indentation, - and additional debug information such as types and all pointer addresses - used to indirect to the final value - - A custom Formatter interface that integrates cleanly with the standard fmt - package and replaces %v, %+v, %#v, and %#+v to provide inline printing - similar to the default %v while providing the additional functionality - outlined above and passing unsupported format verbs such as %x and %q - along to fmt - -# Quick Start - -This section demonstrates how to quickly get started with spew. See the -sections below for further details on formatting and configuration options. - -To dump a variable with full newlines, indentation, type, and pointer -information use Dump, Fdump, or Sdump: - - spew.Dump(myVar1, myVar2, ...) - spew.Fdump(someWriter, myVar1, myVar2, ...) - str := spew.Sdump(myVar1, myVar2, ...) - -Alternatively, if you would prefer to use format strings with a compacted inline -printing style, use the convenience wrappers Printf, Fprintf, etc with -%v (most compact), %+v (adds pointer addresses), %#v (adds types), or -%#+v (adds types and pointer addresses): - - spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) - spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) - spew.Fprintf(someWriter, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) - spew.Fprintf(someWriter, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) - -# Configuration Options - -Configuration of spew is handled by fields in the ConfigState type. For -convenience, all of the top-level functions use a global state available -via the spew.Config global. - -It is also possible to create a ConfigState instance that provides methods -equivalent to the top-level functions. This allows concurrent configuration -options. See the ConfigState documentation for more details. - -The following configuration options are available: - - - Indent - String to use for each indentation level for Dump functions. - It is a single space by default. A popular alternative is "\t". - - - MaxDepth - Maximum number of levels to descend into nested data structures. - There is no limit by default. - - - DisableMethods - Disables invocation of error and Stringer interface methods. - Method invocation is enabled by default. - - - DisablePointerMethods - Disables invocation of error and Stringer interface methods on types - which only accept pointer receivers from non-pointer variables. - Pointer method invocation is enabled by default. - - - DisablePointerAddresses - DisablePointerAddresses specifies whether to disable the printing of - pointer addresses. This is useful when diffing data structures in tests. - - - DisableCapacities - DisableCapacities specifies whether to disable the printing of - capacities for arrays, slices, maps and channels. This is useful when - diffing data structures in tests. - - - ContinueOnMethod - Enables recursion into types after invoking error and Stringer interface - methods. Recursion after method invocation is disabled by default. - - - SortKeys - Specifies map keys should be sorted before being printed. Use - this to have a more deterministic, diffable output. Note that - only native types (bool, int, uint, floats, uintptr and string) - and types which implement error or Stringer interfaces are - supported with other types sorted according to the - reflect.Value.String() output which guarantees display - stability. Natural map order is used by default. - - - SpewKeys - Specifies that, as a last resort attempt, map keys should be - spewed to strings and sorted by those strings. This is only - considered if SortKeys is true. - -# Dump Usage - -Simply call spew.Dump with a list of variables you want to dump: - - spew.Dump(myVar1, myVar2, ...) - -You may also call spew.Fdump if you would prefer to output to an arbitrary -io.Writer. For example, to dump to standard error: - - spew.Fdump(os.Stderr, myVar1, myVar2, ...) - -A third option is to call spew.Sdump to get the formatted output as a string: - - str := spew.Sdump(myVar1, myVar2, ...) - -# Sample Dump Output - -See the Dump example for details on the setup of the types and variables being -shown here. - - (main.Foo) { - unexportedField: (*main.Bar)(0xf84002e210)({ - flag: (main.Flag) flagTwo, - data: (uintptr) - }), - ExportedField: (map[interface {}]interface {}) (len=1) { - (string) (len=3) "one": (bool) true - } - } - -Byte (and uint8) arrays and slices are displayed uniquely like the hexdump -C -command as shown. - - ([]uint8) (len=32 cap=32) { - 00000000 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20 |............... | - 00000010 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30 |!"#$%&'()*+,-./0| - 00000020 31 32 |12| - } - -# Custom Formatter - -Spew provides a custom formatter that implements the fmt.Formatter interface -so that it integrates cleanly with standard fmt package printing functions. The -formatter is useful for inline printing of smaller data types similar to the -standard %v format specifier. - -The custom formatter only responds to the %v (most compact), %+v (adds pointer -addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb -combinations. Any other verbs such as %x and %q will be sent to the the -standard fmt package for formatting. In addition, the custom formatter ignores -the width and precision arguments (however they will still work on the format -specifiers not handled by the custom formatter). - -# Custom Formatter Usage - -The simplest way to make use of the spew custom formatter is to call one of the -convenience functions such as spew.Printf, spew.Println, or spew.Printf. The -functions have syntax you are most likely already familiar with: - - spew.Printf("myVar1: %v -- myVar2: %+v", myVar1, myVar2) - spew.Printf("myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) - spew.Println(myVar, myVar2) - spew.Fprintf(os.Stderr, "myVar1: %v -- myVar2: %+v", myVar1, myVar2) - spew.Fprintf(os.Stderr, "myVar3: %#v -- myVar4: %#+v", myVar3, myVar4) - -See the Index for the full list convenience functions. - -# Sample Formatter Output - -Double pointer to a uint8: - - %v: <**>5 - %+v: <**>(0xf8400420d0->0xf8400420c8)5 - %#v: (**uint8)5 - %#+v: (**uint8)(0xf8400420d0->0xf8400420c8)5 - -Pointer to circular struct with a uint8 field and a pointer to itself: - - %v: <*>{1 <*>} - %+v: <*>(0xf84003e260){ui8:1 c:<*>(0xf84003e260)} - %#v: (*main.circular){ui8:(uint8)1 c:(*main.circular)} - %#+v: (*main.circular)(0xf84003e260){ui8:(uint8)1 c:(*main.circular)(0xf84003e260)} - -See the Printf example for details on the setup of variables being shown -here. - -# Errors - -Since it is possible for custom Stringer/error interfaces to panic, spew -detects them and handles them internally by printing the panic information -inline with the output. Since spew is intended to provide deep pretty printing -capabilities on structures, it intentionally does not return any errors. -*/ -package spew diff --git a/vendor/github.com/stretchr/testify/internal/spew/dump.go b/vendor/github.com/stretchr/testify/internal/spew/dump.go deleted file mode 100644 index 8323041a4..000000000 --- a/vendor/github.com/stretchr/testify/internal/spew/dump.go +++ /dev/null @@ -1,509 +0,0 @@ -/* - * Copyright (c) 2013-2016 Dave Collins - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -package spew - -import ( - "bytes" - "encoding/hex" - "fmt" - "io" - "os" - "reflect" - "regexp" - "strconv" - "strings" -) - -var ( - // uint8Type is a reflect.Type representing a uint8. It is used to - // convert cgo types to uint8 slices for hexdumping. - uint8Type = reflect.TypeOf(uint8(0)) - - // cCharRE is a regular expression that matches a cgo char. - // It is used to detect character arrays to hexdump them. - cCharRE = regexp.MustCompile(`^.*\._Ctype_char$`) - - // cUnsignedCharRE is a regular expression that matches a cgo unsigned - // char. It is used to detect unsigned character arrays to hexdump - // them. - cUnsignedCharRE = regexp.MustCompile(`^.*\._Ctype_unsignedchar$`) - - // cUint8tCharRE is a regular expression that matches a cgo uint8_t. - // It is used to detect uint8_t arrays to hexdump them. - cUint8tCharRE = regexp.MustCompile(`^.*\._Ctype_uint8_t$`) -) - -// dumpState contains information about the state of a dump operation. -type dumpState struct { - w io.Writer - depth int - pointers map[uintptr]int - ignoreNextType bool - ignoreNextIndent bool - cs *ConfigState -} - -// indent performs indentation according to the depth level and cs.Indent -// option. -func (d *dumpState) indent() { - if d.ignoreNextIndent { - d.ignoreNextIndent = false - return - } - d.w.Write(bytes.Repeat([]byte(d.cs.Indent), d.depth)) -} - -// unpackValue returns values inside of non-nil interfaces when possible. -// This is useful for data types like structs, arrays, slices, and maps which -// can contain varying types packed inside an interface. -func (d *dumpState) unpackValue(v reflect.Value) reflect.Value { - if v.Kind() == reflect.Interface && !v.IsNil() { - v = v.Elem() - } - return v -} - -// dumpPtr handles formatting of pointers by indirecting them as necessary. -func (d *dumpState) dumpPtr(v reflect.Value) { - // Remove pointers at or below the current depth from map used to detect - // circular refs. - for k, depth := range d.pointers { - if depth >= d.depth { - delete(d.pointers, k) - } - } - - // Keep list of all dereferenced pointers to show later. - pointerChain := make([]uintptr, 0) - - // Figure out how many levels of indirection there are by dereferencing - // pointers and unpacking interfaces down the chain while detecting circular - // references. - nilFound := false - cycleFound := false - indirects := 0 - ve := v - for ve.Kind() == reflect.Ptr { - if ve.IsNil() { - nilFound = true - break - } - indirects++ - addr := ve.Pointer() - pointerChain = append(pointerChain, addr) - if pd, ok := d.pointers[addr]; ok && pd < d.depth { - cycleFound = true - indirects-- - break - } - d.pointers[addr] = d.depth - - ve = ve.Elem() - if ve.Kind() == reflect.Interface { - if ve.IsNil() { - nilFound = true - break - } - ve = ve.Elem() - } - } - - // Display type information. - d.w.Write(openParenBytes) - d.w.Write(bytes.Repeat(asteriskBytes, indirects)) - d.w.Write([]byte(ve.Type().String())) - d.w.Write(closeParenBytes) - - // Display pointer information. - if !d.cs.DisablePointerAddresses && len(pointerChain) > 0 { - d.w.Write(openParenBytes) - for i, addr := range pointerChain { - if i > 0 { - d.w.Write(pointerChainBytes) - } - printHexPtr(d.w, addr) - } - d.w.Write(closeParenBytes) - } - - // Display dereferenced value. - d.w.Write(openParenBytes) - switch { - case nilFound: - d.w.Write(nilAngleBytes) - - case cycleFound: - d.w.Write(circularBytes) - - default: - d.ignoreNextType = true - d.dump(ve) - } - d.w.Write(closeParenBytes) -} - -// dumpSlice handles formatting of arrays and slices. Byte (uint8 under -// reflection) arrays and slices are dumped in hexdump -C fashion. -func (d *dumpState) dumpSlice(v reflect.Value) { - // Determine whether this type should be hex dumped or not. Also, - // for types which should be hexdumped, try to use the underlying data - // first, then fall back to trying to convert them to a uint8 slice. - var buf []uint8 - doConvert := false - doHexDump := false - numEntries := v.Len() - if numEntries > 0 { - vt := v.Index(0).Type() - vts := vt.String() - switch { - // C types that need to be converted. - case cCharRE.MatchString(vts): - fallthrough - case cUnsignedCharRE.MatchString(vts): - fallthrough - case cUint8tCharRE.MatchString(vts): - doConvert = true - - // Try to use existing uint8 slices and fall back to converting - // and copying if that fails. - case vt.Kind() == reflect.Uint8: - // We need an addressable interface to convert the type - // to a byte slice. However, the reflect package won't - // give us an interface on certain things like - // unexported struct fields in order to enforce - // visibility rules. We use unsafe, when available, to - // bypass these restrictions since this package does not - // mutate the values. - vs := v - if !vs.CanInterface() || !vs.CanAddr() { - vs = unsafeReflectValue(vs) - } - if !UnsafeDisabled { - vs = vs.Slice(0, numEntries) - - // Use the existing uint8 slice if it can be - // type asserted. - iface := vs.Interface() - if slice, ok := iface.([]uint8); ok { - buf = slice - doHexDump = true - break - } - } - - // The underlying data needs to be converted if it can't - // be type asserted to a uint8 slice. - doConvert = true - } - - // Copy and convert the underlying type if needed. - if doConvert && vt.ConvertibleTo(uint8Type) { - // Convert and copy each element into a uint8 byte - // slice. - buf = make([]uint8, numEntries) - for i := 0; i < numEntries; i++ { - vv := v.Index(i) - buf[i] = uint8(vv.Convert(uint8Type).Uint()) - } - doHexDump = true - } - } - - // Hexdump the entire slice as needed. - if doHexDump { - indent := strings.Repeat(d.cs.Indent, d.depth) - str := indent + hex.Dump(buf) - str = strings.Replace(str, "\n", "\n"+indent, -1) - str = strings.TrimRight(str, d.cs.Indent) - d.w.Write([]byte(str)) - return - } - - // Recursively call dump for each item. - for i := 0; i < numEntries; i++ { - d.dump(d.unpackValue(v.Index(i))) - if i < (numEntries - 1) { - d.w.Write(commaNewlineBytes) - } else { - d.w.Write(newlineBytes) - } - } -} - -// dump is the main workhorse for dumping a value. It uses the passed reflect -// value to figure out what kind of object we are dealing with and formats it -// appropriately. It is a recursive function, however circular data structures -// are detected and handled properly. -func (d *dumpState) dump(v reflect.Value) { - // Handle invalid reflect values immediately. - kind := v.Kind() - if kind == reflect.Invalid { - d.w.Write(invalidAngleBytes) - return - } - - // Handle pointers specially. - if kind == reflect.Ptr { - d.indent() - d.dumpPtr(v) - return - } - - // Print type information unless already handled elsewhere. - if !d.ignoreNextType { - d.indent() - d.w.Write(openParenBytes) - d.w.Write([]byte(v.Type().String())) - d.w.Write(closeParenBytes) - d.w.Write(spaceBytes) - } - d.ignoreNextType = false - - // Display length and capacity if the built-in len and cap functions - // work with the value's kind and the len/cap itself is non-zero. - valueLen, valueCap := 0, 0 - switch v.Kind() { - case reflect.Array, reflect.Slice, reflect.Chan: - valueLen, valueCap = v.Len(), v.Cap() - case reflect.Map, reflect.String: - valueLen = v.Len() - } - if valueLen != 0 || !d.cs.DisableCapacities && valueCap != 0 { - d.w.Write(openParenBytes) - if valueLen != 0 { - d.w.Write(lenEqualsBytes) - printInt(d.w, int64(valueLen), 10) - } - if !d.cs.DisableCapacities && valueCap != 0 { - if valueLen != 0 { - d.w.Write(spaceBytes) - } - d.w.Write(capEqualsBytes) - printInt(d.w, int64(valueCap), 10) - } - d.w.Write(closeParenBytes) - d.w.Write(spaceBytes) - } - - // Call Stringer/error interfaces if they exist and the handle methods flag - // is enabled - if !d.cs.DisableMethods { - if (kind != reflect.Invalid) && (kind != reflect.Interface) { - if handled := handleMethods(d.cs, d.w, v); handled { - return - } - } - } - - switch kind { - case reflect.Invalid: - // Do nothing. We should never get here since invalid has already - // been handled above. - - case reflect.Bool: - printBool(d.w, v.Bool()) - - case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: - printInt(d.w, v.Int(), 10) - - case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: - printUint(d.w, v.Uint(), 10) - - case reflect.Float32: - printFloat(d.w, v.Float(), 32) - - case reflect.Float64: - printFloat(d.w, v.Float(), 64) - - case reflect.Complex64: - printComplex(d.w, v.Complex(), 32) - - case reflect.Complex128: - printComplex(d.w, v.Complex(), 64) - - case reflect.Slice: - if v.IsNil() { - d.w.Write(nilAngleBytes) - break - } - fallthrough - - case reflect.Array: - d.w.Write(openBraceNewlineBytes) - d.depth++ - if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { - d.indent() - d.w.Write(maxNewlineBytes) - } else { - d.dumpSlice(v) - } - d.depth-- - d.indent() - d.w.Write(closeBraceBytes) - - case reflect.String: - d.w.Write([]byte(strconv.Quote(v.String()))) - - case reflect.Interface: - // The only time we should get here is for nil interfaces due to - // unpackValue calls. - if v.IsNil() { - d.w.Write(nilAngleBytes) - } - - case reflect.Ptr: - // Do nothing. We should never get here since pointers have already - // been handled above. - - case reflect.Map: - // nil maps should be indicated as different than empty maps - if v.IsNil() { - d.w.Write(nilAngleBytes) - break - } - - d.w.Write(openBraceNewlineBytes) - d.depth++ - if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { - d.indent() - d.w.Write(maxNewlineBytes) - } else { - numEntries := v.Len() - keys := v.MapKeys() - if d.cs.SortKeys { - sortValues(keys, d.cs) - } - for i, key := range keys { - d.dump(d.unpackValue(key)) - d.w.Write(colonSpaceBytes) - d.ignoreNextIndent = true - d.dump(d.unpackValue(v.MapIndex(key))) - if i < (numEntries - 1) { - d.w.Write(commaNewlineBytes) - } else { - d.w.Write(newlineBytes) - } - } - } - d.depth-- - d.indent() - d.w.Write(closeBraceBytes) - - case reflect.Struct: - d.w.Write(openBraceNewlineBytes) - d.depth++ - if (d.cs.MaxDepth != 0) && (d.depth > d.cs.MaxDepth) { - d.indent() - d.w.Write(maxNewlineBytes) - } else { - vt := v.Type() - numFields := v.NumField() - for i := 0; i < numFields; i++ { - d.indent() - vtf := vt.Field(i) - d.w.Write([]byte(vtf.Name)) - d.w.Write(colonSpaceBytes) - d.ignoreNextIndent = true - d.dump(d.unpackValue(v.Field(i))) - if i < (numFields - 1) { - d.w.Write(commaNewlineBytes) - } else { - d.w.Write(newlineBytes) - } - } - } - d.depth-- - d.indent() - d.w.Write(closeBraceBytes) - - case reflect.Uintptr: - printHexPtr(d.w, uintptr(v.Uint())) - - case reflect.UnsafePointer, reflect.Chan, reflect.Func: - printHexPtr(d.w, v.Pointer()) - - // There were not any other types at the time this code was written, but - // fall back to letting the default fmt package handle it in case any new - // types are added. - default: - if v.CanInterface() { - fmt.Fprintf(d.w, "%v", v.Interface()) - } else { - fmt.Fprintf(d.w, "%v", v.String()) - } - } -} - -// fdump is a helper function to consolidate the logic from the various public -// methods which take varying writers and config states. -func fdump(cs *ConfigState, w io.Writer, a ...interface{}) { - for _, arg := range a { - if arg == nil { - w.Write(interfaceBytes) - w.Write(spaceBytes) - w.Write(nilAngleBytes) - w.Write(newlineBytes) - continue - } - - d := dumpState{w: w, cs: cs} - d.pointers = make(map[uintptr]int) - d.dump(reflect.ValueOf(arg)) - d.w.Write(newlineBytes) - } -} - -// Fdump formats and displays the passed arguments to io.Writer w. It formats -// exactly the same as Dump. -func Fdump(w io.Writer, a ...interface{}) { - fdump(&Config, w, a...) -} - -// Sdump returns a string with the passed arguments formatted exactly the same -// as Dump. -func Sdump(a ...interface{}) string { - var buf bytes.Buffer - fdump(&Config, &buf, a...) - return buf.String() -} - -/* -Dump displays the passed parameters to standard out with newlines, customizable -indentation, and additional debug information such as complete types and all -pointer addresses used to indirect to the final value. It provides the -following features over the built-in printing facilities provided by the fmt -package: - - - Pointers are dereferenced and followed - - Circular data structures are detected and handled properly - - Custom Stringer/error interfaces are optionally invoked, including - on unexported types - - Custom types which only implement the Stringer/error interfaces via - a pointer receiver are optionally invoked when passing non-pointer - variables - - Byte arrays and slices are dumped like the hexdump -C command which - includes offsets, byte values in hex, and ASCII output - -The configuration options are controlled by an exported package global, -spew.Config. See ConfigState for options documentation. - -See Fdump if you would prefer dumping to an arbitrary io.Writer or Sdump to -get the formatted result as a string. -*/ -func Dump(a ...interface{}) { - fdump(&Config, os.Stdout, a...) -} diff --git a/vendor/github.com/stretchr/testify/internal/spew/format.go b/vendor/github.com/stretchr/testify/internal/spew/format.go deleted file mode 100644 index b04edb7d7..000000000 --- a/vendor/github.com/stretchr/testify/internal/spew/format.go +++ /dev/null @@ -1,419 +0,0 @@ -/* - * Copyright (c) 2013-2016 Dave Collins - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -package spew - -import ( - "bytes" - "fmt" - "reflect" - "strconv" - "strings" -) - -// supportedFlags is a list of all the character flags supported by fmt package. -const supportedFlags = "0-+# " - -// formatState implements the fmt.Formatter interface and contains information -// about the state of a formatting operation. The NewFormatter function can -// be used to get a new Formatter which can be used directly as arguments -// in standard fmt package printing calls. -type formatState struct { - value interface{} - fs fmt.State - depth int - pointers map[uintptr]int - ignoreNextType bool - cs *ConfigState -} - -// buildDefaultFormat recreates the original format string without precision -// and width information to pass in to fmt.Sprintf in the case of an -// unrecognized type. Unless new types are added to the language, this -// function won't ever be called. -func (f *formatState) buildDefaultFormat() (format string) { - buf := bytes.NewBuffer(percentBytes) - - for _, flag := range supportedFlags { - if f.fs.Flag(int(flag)) { - buf.WriteRune(flag) - } - } - - buf.WriteRune('v') - - format = buf.String() - return format -} - -// constructOrigFormat recreates the original format string including precision -// and width information to pass along to the standard fmt package. This allows -// automatic deferral of all format strings this package doesn't support. -func (f *formatState) constructOrigFormat(verb rune) (format string) { - buf := bytes.NewBuffer(percentBytes) - - for _, flag := range supportedFlags { - if f.fs.Flag(int(flag)) { - buf.WriteRune(flag) - } - } - - if width, ok := f.fs.Width(); ok { - buf.WriteString(strconv.Itoa(width)) - } - - if precision, ok := f.fs.Precision(); ok { - buf.Write(precisionBytes) - buf.WriteString(strconv.Itoa(precision)) - } - - buf.WriteRune(verb) - - format = buf.String() - return format -} - -// unpackValue returns values inside of non-nil interfaces when possible and -// ensures that types for values which have been unpacked from an interface -// are displayed when the show types flag is also set. -// This is useful for data types like structs, arrays, slices, and maps which -// can contain varying types packed inside an interface. -func (f *formatState) unpackValue(v reflect.Value) reflect.Value { - if v.Kind() == reflect.Interface { - f.ignoreNextType = false - if !v.IsNil() { - v = v.Elem() - } - } - return v -} - -// formatPtr handles formatting of pointers by indirecting them as necessary. -func (f *formatState) formatPtr(v reflect.Value) { - // Display nil if top level pointer is nil. - showTypes := f.fs.Flag('#') - if v.IsNil() && (!showTypes || f.ignoreNextType) { - f.fs.Write(nilAngleBytes) - return - } - - // Remove pointers at or below the current depth from map used to detect - // circular refs. - for k, depth := range f.pointers { - if depth >= f.depth { - delete(f.pointers, k) - } - } - - // Keep list of all dereferenced pointers to possibly show later. - pointerChain := make([]uintptr, 0) - - // Figure out how many levels of indirection there are by derferencing - // pointers and unpacking interfaces down the chain while detecting circular - // references. - nilFound := false - cycleFound := false - indirects := 0 - ve := v - for ve.Kind() == reflect.Ptr { - if ve.IsNil() { - nilFound = true - break - } - indirects++ - addr := ve.Pointer() - pointerChain = append(pointerChain, addr) - if pd, ok := f.pointers[addr]; ok && pd < f.depth { - cycleFound = true - indirects-- - break - } - f.pointers[addr] = f.depth - - ve = ve.Elem() - if ve.Kind() == reflect.Interface { - if ve.IsNil() { - nilFound = true - break - } - ve = ve.Elem() - } - } - - // Display type or indirection level depending on flags. - if showTypes && !f.ignoreNextType { - f.fs.Write(openParenBytes) - f.fs.Write(bytes.Repeat(asteriskBytes, indirects)) - f.fs.Write([]byte(ve.Type().String())) - f.fs.Write(closeParenBytes) - } else { - if nilFound || cycleFound { - indirects += strings.Count(ve.Type().String(), "*") - } - f.fs.Write(openAngleBytes) - f.fs.Write([]byte(strings.Repeat("*", indirects))) - f.fs.Write(closeAngleBytes) - } - - // Display pointer information depending on flags. - if f.fs.Flag('+') && (len(pointerChain) > 0) { - f.fs.Write(openParenBytes) - for i, addr := range pointerChain { - if i > 0 { - f.fs.Write(pointerChainBytes) - } - printHexPtr(f.fs, addr) - } - f.fs.Write(closeParenBytes) - } - - // Display dereferenced value. - switch { - case nilFound: - f.fs.Write(nilAngleBytes) - - case cycleFound: - f.fs.Write(circularShortBytes) - - default: - f.ignoreNextType = true - f.format(ve) - } -} - -// format is the main workhorse for providing the Formatter interface. It -// uses the passed reflect value to figure out what kind of object we are -// dealing with and formats it appropriately. It is a recursive function, -// however circular data structures are detected and handled properly. -func (f *formatState) format(v reflect.Value) { - // Handle invalid reflect values immediately. - kind := v.Kind() - if kind == reflect.Invalid { - f.fs.Write(invalidAngleBytes) - return - } - - // Handle pointers specially. - if kind == reflect.Ptr { - f.formatPtr(v) - return - } - - // Print type information unless already handled elsewhere. - if !f.ignoreNextType && f.fs.Flag('#') { - f.fs.Write(openParenBytes) - f.fs.Write([]byte(v.Type().String())) - f.fs.Write(closeParenBytes) - } - f.ignoreNextType = false - - // Call Stringer/error interfaces if they exist and the handle methods - // flag is enabled. - if !f.cs.DisableMethods { - if (kind != reflect.Invalid) && (kind != reflect.Interface) { - if handled := handleMethods(f.cs, f.fs, v); handled { - return - } - } - } - - switch kind { - case reflect.Invalid: - // Do nothing. We should never get here since invalid has already - // been handled above. - - case reflect.Bool: - printBool(f.fs, v.Bool()) - - case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int: - printInt(f.fs, v.Int(), 10) - - case reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint: - printUint(f.fs, v.Uint(), 10) - - case reflect.Float32: - printFloat(f.fs, v.Float(), 32) - - case reflect.Float64: - printFloat(f.fs, v.Float(), 64) - - case reflect.Complex64: - printComplex(f.fs, v.Complex(), 32) - - case reflect.Complex128: - printComplex(f.fs, v.Complex(), 64) - - case reflect.Slice: - if v.IsNil() { - f.fs.Write(nilAngleBytes) - break - } - fallthrough - - case reflect.Array: - f.fs.Write(openBracketBytes) - f.depth++ - if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { - f.fs.Write(maxShortBytes) - } else { - numEntries := v.Len() - for i := 0; i < numEntries; i++ { - if i > 0 { - f.fs.Write(spaceBytes) - } - f.ignoreNextType = true - f.format(f.unpackValue(v.Index(i))) - } - } - f.depth-- - f.fs.Write(closeBracketBytes) - - case reflect.String: - f.fs.Write([]byte(v.String())) - - case reflect.Interface: - // The only time we should get here is for nil interfaces due to - // unpackValue calls. - if v.IsNil() { - f.fs.Write(nilAngleBytes) - } - - case reflect.Ptr: - // Do nothing. We should never get here since pointers have already - // been handled above. - - case reflect.Map: - // nil maps should be indicated as different than empty maps - if v.IsNil() { - f.fs.Write(nilAngleBytes) - break - } - - f.fs.Write(openMapBytes) - f.depth++ - if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { - f.fs.Write(maxShortBytes) - } else { - keys := v.MapKeys() - if f.cs.SortKeys { - sortValues(keys, f.cs) - } - for i, key := range keys { - if i > 0 { - f.fs.Write(spaceBytes) - } - f.ignoreNextType = true - f.format(f.unpackValue(key)) - f.fs.Write(colonBytes) - f.ignoreNextType = true - f.format(f.unpackValue(v.MapIndex(key))) - } - } - f.depth-- - f.fs.Write(closeMapBytes) - - case reflect.Struct: - numFields := v.NumField() - f.fs.Write(openBraceBytes) - f.depth++ - if (f.cs.MaxDepth != 0) && (f.depth > f.cs.MaxDepth) { - f.fs.Write(maxShortBytes) - } else { - vt := v.Type() - for i := 0; i < numFields; i++ { - if i > 0 { - f.fs.Write(spaceBytes) - } - vtf := vt.Field(i) - if f.fs.Flag('+') || f.fs.Flag('#') { - f.fs.Write([]byte(vtf.Name)) - f.fs.Write(colonBytes) - } - f.format(f.unpackValue(v.Field(i))) - } - } - f.depth-- - f.fs.Write(closeBraceBytes) - - case reflect.Uintptr: - printHexPtr(f.fs, uintptr(v.Uint())) - - case reflect.UnsafePointer, reflect.Chan, reflect.Func: - printHexPtr(f.fs, v.Pointer()) - - // There were not any other types at the time this code was written, but - // fall back to letting the default fmt package handle it if any get added. - default: - format := f.buildDefaultFormat() - if v.CanInterface() { - fmt.Fprintf(f.fs, format, v.Interface()) - } else { - fmt.Fprintf(f.fs, format, v.String()) - } - } -} - -// Format satisfies the fmt.Formatter interface. See NewFormatter for usage -// details. -func (f *formatState) Format(fs fmt.State, verb rune) { - f.fs = fs - - // Use standard formatting for verbs that are not v. - if verb != 'v' { - format := f.constructOrigFormat(verb) - fmt.Fprintf(fs, format, f.value) - return - } - - if f.value == nil { - if fs.Flag('#') { - fs.Write(interfaceBytes) - } - fs.Write(nilAngleBytes) - return - } - - f.format(reflect.ValueOf(f.value)) -} - -// newFormatter is a helper function to consolidate the logic from the various -// public methods which take varying config states. -func newFormatter(cs *ConfigState, v interface{}) fmt.Formatter { - fs := &formatState{value: v, cs: cs} - fs.pointers = make(map[uintptr]int) - return fs -} - -/* -NewFormatter returns a custom formatter that satisfies the fmt.Formatter -interface. As a result, it integrates cleanly with standard fmt package -printing functions. The formatter is useful for inline printing of smaller data -types similar to the standard %v format specifier. - -The custom formatter only responds to the %v (most compact), %+v (adds pointer -addresses), %#v (adds types), or %#+v (adds types and pointer addresses) verb -combinations. Any other verbs such as %x and %q will be sent to the the -standard fmt package for formatting. In addition, the custom formatter ignores -the width and precision arguments (however they will still work on the format -specifiers not handled by the custom formatter). - -Typically this function shouldn't be called directly. It is much easier to make -use of the custom formatter by calling one of the convenience functions such as -Printf, Println, or Fprintf. -*/ -func NewFormatter(v interface{}) fmt.Formatter { - return newFormatter(&Config, v) -} diff --git a/vendor/github.com/stretchr/testify/internal/spew/spew.go b/vendor/github.com/stretchr/testify/internal/spew/spew.go deleted file mode 100644 index 32c0e3388..000000000 --- a/vendor/github.com/stretchr/testify/internal/spew/spew.go +++ /dev/null @@ -1,148 +0,0 @@ -/* - * Copyright (c) 2013-2016 Dave Collins - * - * Permission to use, copy, modify, and distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - */ - -package spew - -import ( - "fmt" - "io" -) - -// Errorf is a wrapper for fmt.Errorf that treats each argument as if it were -// passed with a default Formatter interface returned by NewFormatter. It -// returns the formatted string as a value that satisfies error. See -// NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Errorf(format, spew.NewFormatter(a), spew.NewFormatter(b)) -func Errorf(format string, a ...interface{}) (err error) { - return fmt.Errorf(format, convertArgs(a)...) -} - -// Fprint is a wrapper for fmt.Fprint that treats each argument as if it were -// passed with a default Formatter interface returned by NewFormatter. It -// returns the number of bytes written and any write error encountered. See -// NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Fprint(w, spew.NewFormatter(a), spew.NewFormatter(b)) -func Fprint(w io.Writer, a ...interface{}) (n int, err error) { - return fmt.Fprint(w, convertArgs(a)...) -} - -// Fprintf is a wrapper for fmt.Fprintf that treats each argument as if it were -// passed with a default Formatter interface returned by NewFormatter. It -// returns the number of bytes written and any write error encountered. See -// NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Fprintf(w, format, spew.NewFormatter(a), spew.NewFormatter(b)) -func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) { - return fmt.Fprintf(w, format, convertArgs(a)...) -} - -// Fprintln is a wrapper for fmt.Fprintln that treats each argument as if it -// passed with a default Formatter interface returned by NewFormatter. See -// NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Fprintln(w, spew.NewFormatter(a), spew.NewFormatter(b)) -func Fprintln(w io.Writer, a ...interface{}) (n int, err error) { - return fmt.Fprintln(w, convertArgs(a)...) -} - -// Print is a wrapper for fmt.Print that treats each argument as if it were -// passed with a default Formatter interface returned by NewFormatter. It -// returns the number of bytes written and any write error encountered. See -// NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Print(spew.NewFormatter(a), spew.NewFormatter(b)) -func Print(a ...interface{}) (n int, err error) { - return fmt.Print(convertArgs(a)...) -} - -// Printf is a wrapper for fmt.Printf that treats each argument as if it were -// passed with a default Formatter interface returned by NewFormatter. It -// returns the number of bytes written and any write error encountered. See -// NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Printf(format, spew.NewFormatter(a), spew.NewFormatter(b)) -func Printf(format string, a ...interface{}) (n int, err error) { - return fmt.Printf(format, convertArgs(a)...) -} - -// Println is a wrapper for fmt.Println that treats each argument as if it were -// passed with a default Formatter interface returned by NewFormatter. It -// returns the number of bytes written and any write error encountered. See -// NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Println(spew.NewFormatter(a), spew.NewFormatter(b)) -func Println(a ...interface{}) (n int, err error) { - return fmt.Println(convertArgs(a)...) -} - -// Sprint is a wrapper for fmt.Sprint that treats each argument as if it were -// passed with a default Formatter interface returned by NewFormatter. It -// returns the resulting string. See NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Sprint(spew.NewFormatter(a), spew.NewFormatter(b)) -func Sprint(a ...interface{}) string { - return fmt.Sprint(convertArgs(a)...) -} - -// Sprintf is a wrapper for fmt.Sprintf that treats each argument as if it were -// passed with a default Formatter interface returned by NewFormatter. It -// returns the resulting string. See NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Sprintf(format, spew.NewFormatter(a), spew.NewFormatter(b)) -func Sprintf(format string, a ...interface{}) string { - return fmt.Sprintf(format, convertArgs(a)...) -} - -// Sprintln is a wrapper for fmt.Sprintln that treats each argument as if it -// were passed with a default Formatter interface returned by NewFormatter. It -// returns the resulting string. See NewFormatter for formatting details. -// -// This function is shorthand for the following syntax: -// -// fmt.Sprintln(spew.NewFormatter(a), spew.NewFormatter(b)) -func Sprintln(a ...interface{}) string { - return fmt.Sprintln(convertArgs(a)...) -} - -// convertArgs accepts a slice of arguments and returns a slice of the same -// length with each argument converted to a default spew Formatter interface. -func convertArgs(args []interface{}) (formatters []interface{}) { - formatters = make([]interface{}, len(args)) - for index, arg := range args { - formatters[index] = NewFormatter(arg) - } - return formatters -} diff --git a/vendor/github.com/stretchr/testify/require/doc.go b/vendor/github.com/stretchr/testify/require/doc.go deleted file mode 100644 index ee84cc479..000000000 --- a/vendor/github.com/stretchr/testify/require/doc.go +++ /dev/null @@ -1,31 +0,0 @@ -// Package require implements the same assertions as the assert package but -// stops test execution when a test fails. -// -// # Example Usage -// -// The following is a complete example using require in a standard test function: -// -// import ( -// "testing" -// "github.com/stretchr/testify/require" -// ) -// -// func TestSomething(t *testing.T) { -// -// var a string = "Hello" -// var b string = "Hello" -// -// require.Equal(t, a, b, "The two words should be the same.") -// -// } -// -// # Assertions -// -// The require package have same global functions as in the assert package, -// but instead of returning a boolean result they call [testing.T.FailNow]. -// A consequence of this is that it must be called from the goroutine running -// the test function, not from other goroutines created during the test. -// -// Every assertion function also takes an optional string message as the final argument, -// allowing custom error messages to be appended to the message the assertion method outputs. -package require diff --git a/vendor/github.com/stretchr/testify/require/forward_requirements.go b/vendor/github.com/stretchr/testify/require/forward_requirements.go deleted file mode 100644 index 1dcb2338c..000000000 --- a/vendor/github.com/stretchr/testify/require/forward_requirements.go +++ /dev/null @@ -1,16 +0,0 @@ -package require - -// Assertions provides assertion methods around the -// TestingT interface. -type Assertions struct { - t TestingT -} - -// New makes a new Assertions object for the specified TestingT. -func New(t TestingT) *Assertions { - return &Assertions{ - t: t, - } -} - -//go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=require -template=require_forward.go.tmpl -include-format-funcs" diff --git a/vendor/github.com/stretchr/testify/require/require.go b/vendor/github.com/stretchr/testify/require/require.go deleted file mode 100644 index 652871f2e..000000000 --- a/vendor/github.com/stretchr/testify/require/require.go +++ /dev/null @@ -1,2176 +0,0 @@ -// Code generated with github.com/stretchr/testify/_codegen; DO NOT EDIT. - -package require - -import ( - assert "github.com/stretchr/testify/assert" - http "net/http" - url "net/url" - time "time" -) - -// Condition uses a Comparison to assert a complex condition. -func Condition(t TestingT, comp assert.Comparison, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Condition(t, comp, msgAndArgs...) { - return - } - t.FailNow() -} - -// Conditionf uses a Comparison to assert a complex condition. -func Conditionf(t TestingT, comp assert.Comparison, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Conditionf(t, comp, msg, args...) { - return - } - t.FailNow() -} - -// Contains asserts that the specified string, list(array, slice...) or map contains the -// specified substring or element. -// -// require.Contains(t, "Hello World", "World") -// require.Contains(t, ["Hello", "World"], "World") -// require.Contains(t, {"Hello": "World"}, "Hello") -func Contains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Contains(t, s, contains, msgAndArgs...) { - return - } - t.FailNow() -} - -// Containsf asserts that the specified string, list(array, slice...) or map contains the -// specified substring or element. -// -// require.Containsf(t, "Hello World", "World", "error message %s", "formatted") -// require.Containsf(t, ["Hello", "World"], "World", "error message %s", "formatted") -// require.Containsf(t, {"Hello": "World"}, "Hello", "error message %s", "formatted") -func Containsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Containsf(t, s, contains, msg, args...) { - return - } - t.FailNow() -} - -// DirExists checks whether a directory exists in the given path. It also fails -// if the path is a file rather a directory or there is an error checking whether it exists. -func DirExists(t TestingT, path string, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.DirExists(t, path, msgAndArgs...) { - return - } - t.FailNow() -} - -// DirExistsf checks whether a directory exists in the given path. It also fails -// if the path is a file rather a directory or there is an error checking whether it exists. -func DirExistsf(t TestingT, path string, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.DirExistsf(t, path, msg, args...) { - return - } - t.FailNow() -} - -// ElementsMatch asserts that the specified listA(array, slice...) is equal to specified -// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, -// the number of appearances of each of them in both lists should match. -// -// require.ElementsMatch(t, [1, 3, 2, 3], [1, 3, 3, 2]) -func ElementsMatch(t TestingT, listA interface{}, listB interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.ElementsMatch(t, listA, listB, msgAndArgs...) { - return - } - t.FailNow() -} - -// ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified -// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, -// the number of appearances of each of them in both lists should match. -// -// require.ElementsMatchf(t, [1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted") -func ElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.ElementsMatchf(t, listA, listB, msg, args...) { - return - } - t.FailNow() -} - -// Empty asserts that the given value is "empty". -// -// [Zero values] are "empty". -// -// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). -// -// Slices, maps and channels with zero length are "empty". -// -// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". -// -// require.Empty(t, obj) -// -// [Zero values]: https://go.dev/ref/spec#The_zero_value -func Empty(t TestingT, object interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Empty(t, object, msgAndArgs...) { - return - } - t.FailNow() -} - -// Emptyf asserts that the given value is "empty". -// -// [Zero values] are "empty". -// -// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). -// -// Slices, maps and channels with zero length are "empty". -// -// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". -// -// require.Emptyf(t, obj, "error message %s", "formatted") -// -// [Zero values]: https://go.dev/ref/spec#The_zero_value -func Emptyf(t TestingT, object interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Emptyf(t, object, msg, args...) { - return - } - t.FailNow() -} - -// Equal asserts that two objects are equal. -// -// require.Equal(t, 123, 123) -// -// Pointer variable equality is determined based on the equality of the -// referenced values (as opposed to the memory addresses). Function equality -// cannot be determined and will always fail. -func Equal(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Equal(t, expected, actual, msgAndArgs...) { - return - } - t.FailNow() -} - -// EqualError asserts that a function returned a non-nil error (i.e. an error) -// and that it is equal to the provided error. -// -// actualObj, err := SomeFunction() -// require.EqualError(t, err, expectedErrorString) -func EqualError(t TestingT, theError error, errString string, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.EqualError(t, theError, errString, msgAndArgs...) { - return - } - t.FailNow() -} - -// EqualErrorf asserts that a function returned a non-nil error (i.e. an error) -// and that it is equal to the provided error. -// -// actualObj, err := SomeFunction() -// require.EqualErrorf(t, err, expectedErrorString, "error message %s", "formatted") -func EqualErrorf(t TestingT, theError error, errString string, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.EqualErrorf(t, theError, errString, msg, args...) { - return - } - t.FailNow() -} - -// EqualExportedValues asserts that the types of two objects are equal and their public -// fields are also equal. This is useful for comparing structs that have private fields -// that could potentially differ. -// -// type S struct { -// Exported int -// notExported int -// } -// require.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true -// require.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false -func EqualExportedValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.EqualExportedValues(t, expected, actual, msgAndArgs...) { - return - } - t.FailNow() -} - -// EqualExportedValuesf asserts that the types of two objects are equal and their public -// fields are also equal. This is useful for comparing structs that have private fields -// that could potentially differ. -// -// type S struct { -// Exported int -// notExported int -// } -// require.EqualExportedValuesf(t, S{1, 2}, S{1, 3}, "error message %s", "formatted") => true -// require.EqualExportedValuesf(t, S{1, 2}, S{2, 3}, "error message %s", "formatted") => false -func EqualExportedValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.EqualExportedValuesf(t, expected, actual, msg, args...) { - return - } - t.FailNow() -} - -// EqualValues asserts that two objects are equal or convertible to the larger -// type and equal. -// -// require.EqualValues(t, uint32(123), int32(123)) -func EqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.EqualValues(t, expected, actual, msgAndArgs...) { - return - } - t.FailNow() -} - -// EqualValuesf asserts that two objects are equal or convertible to the larger -// type and equal. -// -// require.EqualValuesf(t, uint32(123), int32(123), "error message %s", "formatted") -func EqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.EqualValuesf(t, expected, actual, msg, args...) { - return - } - t.FailNow() -} - -// Equalf asserts that two objects are equal. -// -// require.Equalf(t, 123, 123, "error message %s", "formatted") -// -// Pointer variable equality is determined based on the equality of the -// referenced values (as opposed to the memory addresses). Function equality -// cannot be determined and will always fail. -func Equalf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Equalf(t, expected, actual, msg, args...) { - return - } - t.FailNow() -} - -// Error asserts that a function returned a non-nil error (ie. an error). -// -// actualObj, err := SomeFunction() -// require.Error(t, err) -func Error(t TestingT, err error, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Error(t, err, msgAndArgs...) { - return - } - t.FailNow() -} - -// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. -// This is a wrapper for errors.As. -func ErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.ErrorAs(t, err, target, msgAndArgs...) { - return - } - t.FailNow() -} - -// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. -// This is a wrapper for errors.As. -func ErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.ErrorAsf(t, err, target, msg, args...) { - return - } - t.FailNow() -} - -// ErrorContains asserts that a function returned a non-nil error (i.e. an -// error) and that the error contains the specified substring. -// -// actualObj, err := SomeFunction() -// require.ErrorContains(t, err, expectedErrorSubString) -func ErrorContains(t TestingT, theError error, contains string, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.ErrorContains(t, theError, contains, msgAndArgs...) { - return - } - t.FailNow() -} - -// ErrorContainsf asserts that a function returned a non-nil error (i.e. an -// error) and that the error contains the specified substring. -// -// actualObj, err := SomeFunction() -// require.ErrorContainsf(t, err, expectedErrorSubString, "error message %s", "formatted") -func ErrorContainsf(t TestingT, theError error, contains string, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.ErrorContainsf(t, theError, contains, msg, args...) { - return - } - t.FailNow() -} - -// ErrorIs asserts that at least one of the errors in err's chain matches target. -// This is a wrapper for errors.Is. -func ErrorIs(t TestingT, err error, target error, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.ErrorIs(t, err, target, msgAndArgs...) { - return - } - t.FailNow() -} - -// ErrorIsf asserts that at least one of the errors in err's chain matches target. -// This is a wrapper for errors.Is. -func ErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.ErrorIsf(t, err, target, msg, args...) { - return - } - t.FailNow() -} - -// Errorf asserts that a function returned a non-nil error (ie. an error). -// -// actualObj, err := SomeFunction() -// require.Errorf(t, err, "error message %s", "formatted") -func Errorf(t TestingT, err error, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Errorf(t, err, msg, args...) { - return - } - t.FailNow() -} - -// Eventually asserts that given condition will be met in waitFor time, -// periodically checking target function each tick. -// -// require.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) -func Eventually(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Eventually(t, condition, waitFor, tick, msgAndArgs...) { - return - } - t.FailNow() -} - -// EventuallyWithT asserts that given condition will be met in waitFor time, -// periodically checking target function each tick. In contrast to Eventually, -// it supplies a CollectT to the condition function, so that the condition -// function can use the CollectT to call other assertions. -// The condition is considered "met" if no errors are raised in a tick. -// The supplied CollectT collects all errors from one tick (if there are any). -// If the condition is not met before waitFor, the collected errors of -// the last tick are copied to t. -// -// externalValue := false -// go func() { -// time.Sleep(8*time.Second) -// externalValue = true -// }() -// require.EventuallyWithT(t, func(c *assert.CollectT) { -// // add assertions as needed; any assertion failure will fail the current tick -// require.True(c, externalValue, "expected 'externalValue' to be true") -// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") -func EventuallyWithT(t TestingT, condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.EventuallyWithT(t, condition, waitFor, tick, msgAndArgs...) { - return - } - t.FailNow() -} - -// EventuallyWithTf asserts that given condition will be met in waitFor time, -// periodically checking target function each tick. In contrast to Eventually, -// it supplies a CollectT to the condition function, so that the condition -// function can use the CollectT to call other assertions. -// The condition is considered "met" if no errors are raised in a tick. -// The supplied CollectT collects all errors from one tick (if there are any). -// If the condition is not met before waitFor, the collected errors of -// the last tick are copied to t. -// -// externalValue := false -// go func() { -// time.Sleep(8*time.Second) -// externalValue = true -// }() -// require.EventuallyWithTf(t, func(c *assert.CollectT) { -// // add assertions as needed; any assertion failure will fail the current tick -// require.True(c, externalValue, "expected 'externalValue' to be true") -// }, 10*time.Second, 1*time.Second, "error message %s", "formatted") -func EventuallyWithTf(t TestingT, condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.EventuallyWithTf(t, condition, waitFor, tick, msg, args...) { - return - } - t.FailNow() -} - -// Eventuallyf asserts that given condition will be met in waitFor time, -// periodically checking target function each tick. -// -// require.Eventuallyf(t, func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") -func Eventuallyf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Eventuallyf(t, condition, waitFor, tick, msg, args...) { - return - } - t.FailNow() -} - -// Exactly asserts that two objects are equal in value and type. -// -// require.Exactly(t, int32(123), int64(123)) -func Exactly(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Exactly(t, expected, actual, msgAndArgs...) { - return - } - t.FailNow() -} - -// Exactlyf asserts that two objects are equal in value and type. -// -// require.Exactlyf(t, int32(123), int64(123), "error message %s", "formatted") -func Exactlyf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Exactlyf(t, expected, actual, msg, args...) { - return - } - t.FailNow() -} - -// Fail reports a failure through -func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Fail(t, failureMessage, msgAndArgs...) { - return - } - t.FailNow() -} - -// FailNow fails test -func FailNow(t TestingT, failureMessage string, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.FailNow(t, failureMessage, msgAndArgs...) { - return - } - t.FailNow() -} - -// FailNowf fails test -func FailNowf(t TestingT, failureMessage string, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.FailNowf(t, failureMessage, msg, args...) { - return - } - t.FailNow() -} - -// Failf reports a failure through -func Failf(t TestingT, failureMessage string, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Failf(t, failureMessage, msg, args...) { - return - } - t.FailNow() -} - -// False asserts that the specified value is false. -// -// require.False(t, myBool) -func False(t TestingT, value bool, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.False(t, value, msgAndArgs...) { - return - } - t.FailNow() -} - -// Falsef asserts that the specified value is false. -// -// require.Falsef(t, myBool, "error message %s", "formatted") -func Falsef(t TestingT, value bool, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Falsef(t, value, msg, args...) { - return - } - t.FailNow() -} - -// FileExists checks whether a file exists in the given path. It also fails if -// the path points to a directory or there is an error when trying to check the file. -func FileExists(t TestingT, path string, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.FileExists(t, path, msgAndArgs...) { - return - } - t.FailNow() -} - -// FileExistsf checks whether a file exists in the given path. It also fails if -// the path points to a directory or there is an error when trying to check the file. -func FileExistsf(t TestingT, path string, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.FileExistsf(t, path, msg, args...) { - return - } - t.FailNow() -} - -// Greater asserts that the first element is greater than the second -// -// require.Greater(t, 2, 1) -// require.Greater(t, float64(2), float64(1)) -// require.Greater(t, "b", "a") -func Greater(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Greater(t, e1, e2, msgAndArgs...) { - return - } - t.FailNow() -} - -// GreaterOrEqual asserts that the first element is greater than or equal to the second -// -// require.GreaterOrEqual(t, 2, 1) -// require.GreaterOrEqual(t, 2, 2) -// require.GreaterOrEqual(t, "b", "a") -// require.GreaterOrEqual(t, "b", "b") -func GreaterOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.GreaterOrEqual(t, e1, e2, msgAndArgs...) { - return - } - t.FailNow() -} - -// GreaterOrEqualf asserts that the first element is greater than or equal to the second -// -// require.GreaterOrEqualf(t, 2, 1, "error message %s", "formatted") -// require.GreaterOrEqualf(t, 2, 2, "error message %s", "formatted") -// require.GreaterOrEqualf(t, "b", "a", "error message %s", "formatted") -// require.GreaterOrEqualf(t, "b", "b", "error message %s", "formatted") -func GreaterOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.GreaterOrEqualf(t, e1, e2, msg, args...) { - return - } - t.FailNow() -} - -// Greaterf asserts that the first element is greater than the second -// -// require.Greaterf(t, 2, 1, "error message %s", "formatted") -// require.Greaterf(t, float64(2), float64(1), "error message %s", "formatted") -// require.Greaterf(t, "b", "a", "error message %s", "formatted") -func Greaterf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Greaterf(t, e1, e2, msg, args...) { - return - } - t.FailNow() -} - -// HTTPBodyContains asserts that a specified handler returns a -// body that contains a string. -// -// require.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") -func HTTPBodyContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.HTTPBodyContains(t, handler, method, url, values, str, msgAndArgs...) { - return - } - t.FailNow() -} - -// HTTPBodyContainsf asserts that a specified handler returns a -// body that contains a string. -// -// require.HTTPBodyContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") -func HTTPBodyContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.HTTPBodyContainsf(t, handler, method, url, values, str, msg, args...) { - return - } - t.FailNow() -} - -// HTTPBodyNotContains asserts that a specified handler returns a -// body that does not contain a string. -// -// require.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") -func HTTPBodyNotContains(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.HTTPBodyNotContains(t, handler, method, url, values, str, msgAndArgs...) { - return - } - t.FailNow() -} - -// HTTPBodyNotContainsf asserts that a specified handler returns a -// body that does not contain a string. -// -// require.HTTPBodyNotContainsf(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") -func HTTPBodyNotContainsf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.HTTPBodyNotContainsf(t, handler, method, url, values, str, msg, args...) { - return - } - t.FailNow() -} - -// HTTPError asserts that a specified handler returns an error status code. -// -// require.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} -func HTTPError(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.HTTPError(t, handler, method, url, values, msgAndArgs...) { - return - } - t.FailNow() -} - -// HTTPErrorf asserts that a specified handler returns an error status code. -// -// require.HTTPErrorf(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} -func HTTPErrorf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.HTTPErrorf(t, handler, method, url, values, msg, args...) { - return - } - t.FailNow() -} - -// HTTPRedirect asserts that a specified handler returns a redirect status code. -// -// require.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} -func HTTPRedirect(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.HTTPRedirect(t, handler, method, url, values, msgAndArgs...) { - return - } - t.FailNow() -} - -// HTTPRedirectf asserts that a specified handler returns a redirect status code. -// -// require.HTTPRedirectf(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} -func HTTPRedirectf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.HTTPRedirectf(t, handler, method, url, values, msg, args...) { - return - } - t.FailNow() -} - -// HTTPStatusCode asserts that a specified handler returns a specified status code. -// -// require.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) -func HTTPStatusCode(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.HTTPStatusCode(t, handler, method, url, values, statuscode, msgAndArgs...) { - return - } - t.FailNow() -} - -// HTTPStatusCodef asserts that a specified handler returns a specified status code. -// -// require.HTTPStatusCodef(t, myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") -func HTTPStatusCodef(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.HTTPStatusCodef(t, handler, method, url, values, statuscode, msg, args...) { - return - } - t.FailNow() -} - -// HTTPSuccess asserts that a specified handler returns a success status code. -// -// require.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) -func HTTPSuccess(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.HTTPSuccess(t, handler, method, url, values, msgAndArgs...) { - return - } - t.FailNow() -} - -// HTTPSuccessf asserts that a specified handler returns a success status code. -// -// require.HTTPSuccessf(t, myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") -func HTTPSuccessf(t TestingT, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.HTTPSuccessf(t, handler, method, url, values, msg, args...) { - return - } - t.FailNow() -} - -// Implements asserts that an object is implemented by the specified interface. -// -// require.Implements(t, (*MyInterface)(nil), new(MyObject)) -func Implements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Implements(t, interfaceObject, object, msgAndArgs...) { - return - } - t.FailNow() -} - -// Implementsf asserts that an object is implemented by the specified interface. -// -// require.Implementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") -func Implementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Implementsf(t, interfaceObject, object, msg, args...) { - return - } - t.FailNow() -} - -// InDelta asserts that the two numerals are within delta of each other. -// -// require.InDelta(t, math.Pi, 22/7.0, 0.01) -func InDelta(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.InDelta(t, expected, actual, delta, msgAndArgs...) { - return - } - t.FailNow() -} - -// InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. -func InDeltaMapValues(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.InDeltaMapValues(t, expected, actual, delta, msgAndArgs...) { - return - } - t.FailNow() -} - -// InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. -func InDeltaMapValuesf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.InDeltaMapValuesf(t, expected, actual, delta, msg, args...) { - return - } - t.FailNow() -} - -// InDeltaSlice is the same as InDelta, except it compares two slices. -func InDeltaSlice(t TestingT, expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.InDeltaSlice(t, expected, actual, delta, msgAndArgs...) { - return - } - t.FailNow() -} - -// InDeltaSlicef is the same as InDelta, except it compares two slices. -func InDeltaSlicef(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.InDeltaSlicef(t, expected, actual, delta, msg, args...) { - return - } - t.FailNow() -} - -// InDeltaf asserts that the two numerals are within delta of each other. -// -// require.InDeltaf(t, math.Pi, 22/7.0, 0.01, "error message %s", "formatted") -func InDeltaf(t TestingT, expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.InDeltaf(t, expected, actual, delta, msg, args...) { - return - } - t.FailNow() -} - -// InEpsilon asserts that expected and actual have a relative error less than epsilon -func InEpsilon(t TestingT, expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.InEpsilon(t, expected, actual, epsilon, msgAndArgs...) { - return - } - t.FailNow() -} - -// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. -func InEpsilonSlice(t TestingT, expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.InEpsilonSlice(t, expected, actual, epsilon, msgAndArgs...) { - return - } - t.FailNow() -} - -// InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices. -func InEpsilonSlicef(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.InEpsilonSlicef(t, expected, actual, epsilon, msg, args...) { - return - } - t.FailNow() -} - -// InEpsilonf asserts that expected and actual have a relative error less than epsilon -func InEpsilonf(t TestingT, expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.InEpsilonf(t, expected, actual, epsilon, msg, args...) { - return - } - t.FailNow() -} - -// IsDecreasing asserts that the collection is decreasing -// -// require.IsDecreasing(t, []int{2, 1, 0}) -// require.IsDecreasing(t, []float{2, 1}) -// require.IsDecreasing(t, []string{"b", "a"}) -func IsDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.IsDecreasing(t, object, msgAndArgs...) { - return - } - t.FailNow() -} - -// IsDecreasingf asserts that the collection is decreasing -// -// require.IsDecreasingf(t, []int{2, 1, 0}, "error message %s", "formatted") -// require.IsDecreasingf(t, []float{2, 1}, "error message %s", "formatted") -// require.IsDecreasingf(t, []string{"b", "a"}, "error message %s", "formatted") -func IsDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.IsDecreasingf(t, object, msg, args...) { - return - } - t.FailNow() -} - -// IsIncreasing asserts that the collection is increasing -// -// require.IsIncreasing(t, []int{1, 2, 3}) -// require.IsIncreasing(t, []float{1, 2}) -// require.IsIncreasing(t, []string{"a", "b"}) -func IsIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.IsIncreasing(t, object, msgAndArgs...) { - return - } - t.FailNow() -} - -// IsIncreasingf asserts that the collection is increasing -// -// require.IsIncreasingf(t, []int{1, 2, 3}, "error message %s", "formatted") -// require.IsIncreasingf(t, []float{1, 2}, "error message %s", "formatted") -// require.IsIncreasingf(t, []string{"a", "b"}, "error message %s", "formatted") -func IsIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.IsIncreasingf(t, object, msg, args...) { - return - } - t.FailNow() -} - -// IsNonDecreasing asserts that the collection is not decreasing -// -// require.IsNonDecreasing(t, []int{1, 1, 2}) -// require.IsNonDecreasing(t, []float{1, 2}) -// require.IsNonDecreasing(t, []string{"a", "b"}) -func IsNonDecreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.IsNonDecreasing(t, object, msgAndArgs...) { - return - } - t.FailNow() -} - -// IsNonDecreasingf asserts that the collection is not decreasing -// -// require.IsNonDecreasingf(t, []int{1, 1, 2}, "error message %s", "formatted") -// require.IsNonDecreasingf(t, []float{1, 2}, "error message %s", "formatted") -// require.IsNonDecreasingf(t, []string{"a", "b"}, "error message %s", "formatted") -func IsNonDecreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.IsNonDecreasingf(t, object, msg, args...) { - return - } - t.FailNow() -} - -// IsNonIncreasing asserts that the collection is not increasing -// -// require.IsNonIncreasing(t, []int{2, 1, 1}) -// require.IsNonIncreasing(t, []float{2, 1}) -// require.IsNonIncreasing(t, []string{"b", "a"}) -func IsNonIncreasing(t TestingT, object interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.IsNonIncreasing(t, object, msgAndArgs...) { - return - } - t.FailNow() -} - -// IsNonIncreasingf asserts that the collection is not increasing -// -// require.IsNonIncreasingf(t, []int{2, 1, 1}, "error message %s", "formatted") -// require.IsNonIncreasingf(t, []float{2, 1}, "error message %s", "formatted") -// require.IsNonIncreasingf(t, []string{"b", "a"}, "error message %s", "formatted") -func IsNonIncreasingf(t TestingT, object interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.IsNonIncreasingf(t, object, msg, args...) { - return - } - t.FailNow() -} - -// IsNotType asserts that the specified objects are not of the same type. -// -// require.IsNotType(t, &NotMyStruct{}, &MyStruct{}) -func IsNotType(t TestingT, theType interface{}, object interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.IsNotType(t, theType, object, msgAndArgs...) { - return - } - t.FailNow() -} - -// IsNotTypef asserts that the specified objects are not of the same type. -// -// require.IsNotTypef(t, &NotMyStruct{}, &MyStruct{}, "error message %s", "formatted") -func IsNotTypef(t TestingT, theType interface{}, object interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.IsNotTypef(t, theType, object, msg, args...) { - return - } - t.FailNow() -} - -// IsType asserts that the specified objects are of the same type. -// -// require.IsType(t, &MyStruct{}, &MyStruct{}) -func IsType(t TestingT, expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.IsType(t, expectedType, object, msgAndArgs...) { - return - } - t.FailNow() -} - -// IsTypef asserts that the specified objects are of the same type. -// -// require.IsTypef(t, &MyStruct{}, &MyStruct{}, "error message %s", "formatted") -func IsTypef(t TestingT, expectedType interface{}, object interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.IsTypef(t, expectedType, object, msg, args...) { - return - } - t.FailNow() -} - -// JSONEq asserts that two JSON strings are equivalent. -// -// require.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) -func JSONEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.JSONEq(t, expected, actual, msgAndArgs...) { - return - } - t.FailNow() -} - -// JSONEqf asserts that two JSON strings are equivalent. -// -// require.JSONEqf(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") -func JSONEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.JSONEqf(t, expected, actual, msg, args...) { - return - } - t.FailNow() -} - -// Len asserts that the specified object has specific length. -// Len also fails if the object has a type that len() not accept. -// -// require.Len(t, mySlice, 3) -func Len(t TestingT, object interface{}, length int, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Len(t, object, length, msgAndArgs...) { - return - } - t.FailNow() -} - -// Lenf asserts that the specified object has specific length. -// Lenf also fails if the object has a type that len() not accept. -// -// require.Lenf(t, mySlice, 3, "error message %s", "formatted") -func Lenf(t TestingT, object interface{}, length int, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Lenf(t, object, length, msg, args...) { - return - } - t.FailNow() -} - -// Less asserts that the first element is less than the second -// -// require.Less(t, 1, 2) -// require.Less(t, float64(1), float64(2)) -// require.Less(t, "a", "b") -func Less(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Less(t, e1, e2, msgAndArgs...) { - return - } - t.FailNow() -} - -// LessOrEqual asserts that the first element is less than or equal to the second -// -// require.LessOrEqual(t, 1, 2) -// require.LessOrEqual(t, 2, 2) -// require.LessOrEqual(t, "a", "b") -// require.LessOrEqual(t, "b", "b") -func LessOrEqual(t TestingT, e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.LessOrEqual(t, e1, e2, msgAndArgs...) { - return - } - t.FailNow() -} - -// LessOrEqualf asserts that the first element is less than or equal to the second -// -// require.LessOrEqualf(t, 1, 2, "error message %s", "formatted") -// require.LessOrEqualf(t, 2, 2, "error message %s", "formatted") -// require.LessOrEqualf(t, "a", "b", "error message %s", "formatted") -// require.LessOrEqualf(t, "b", "b", "error message %s", "formatted") -func LessOrEqualf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.LessOrEqualf(t, e1, e2, msg, args...) { - return - } - t.FailNow() -} - -// Lessf asserts that the first element is less than the second -// -// require.Lessf(t, 1, 2, "error message %s", "formatted") -// require.Lessf(t, float64(1), float64(2), "error message %s", "formatted") -// require.Lessf(t, "a", "b", "error message %s", "formatted") -func Lessf(t TestingT, e1 interface{}, e2 interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Lessf(t, e1, e2, msg, args...) { - return - } - t.FailNow() -} - -// Negative asserts that the specified element is negative -// -// require.Negative(t, -1) -// require.Negative(t, -1.23) -func Negative(t TestingT, e interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Negative(t, e, msgAndArgs...) { - return - } - t.FailNow() -} - -// Negativef asserts that the specified element is negative -// -// require.Negativef(t, -1, "error message %s", "formatted") -// require.Negativef(t, -1.23, "error message %s", "formatted") -func Negativef(t TestingT, e interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Negativef(t, e, msg, args...) { - return - } - t.FailNow() -} - -// Never asserts that the given condition doesn't satisfy in waitFor time, -// periodically checking the target function each tick. -// -// require.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) -func Never(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Never(t, condition, waitFor, tick, msgAndArgs...) { - return - } - t.FailNow() -} - -// Neverf asserts that the given condition doesn't satisfy in waitFor time, -// periodically checking the target function each tick. -// -// require.Neverf(t, func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") -func Neverf(t TestingT, condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Neverf(t, condition, waitFor, tick, msg, args...) { - return - } - t.FailNow() -} - -// Nil asserts that the specified object is nil. -// -// require.Nil(t, err) -func Nil(t TestingT, object interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Nil(t, object, msgAndArgs...) { - return - } - t.FailNow() -} - -// Nilf asserts that the specified object is nil. -// -// require.Nilf(t, err, "error message %s", "formatted") -func Nilf(t TestingT, object interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Nilf(t, object, msg, args...) { - return - } - t.FailNow() -} - -// NoDirExists checks whether a directory does not exist in the given path. -// It fails if the path points to an existing _directory_ only. -func NoDirExists(t TestingT, path string, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NoDirExists(t, path, msgAndArgs...) { - return - } - t.FailNow() -} - -// NoDirExistsf checks whether a directory does not exist in the given path. -// It fails if the path points to an existing _directory_ only. -func NoDirExistsf(t TestingT, path string, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NoDirExistsf(t, path, msg, args...) { - return - } - t.FailNow() -} - -// NoError asserts that a function returned a nil error (ie. no error). -// -// actualObj, err := SomeFunction() -// require.NoError(t, err) -// require.Equal(t, expectedObj, actualObj) -func NoError(t TestingT, err error, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NoError(t, err, msgAndArgs...) { - return - } - t.FailNow() -} - -// NoErrorf asserts that a function returned a nil error (ie. no error). -// -// actualObj, err := SomeFunction() -// require.NoErrorf(t, err, "error message %s", "formatted") -// require.Equal(t, expectedObj, actualObj) -func NoErrorf(t TestingT, err error, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NoErrorf(t, err, msg, args...) { - return - } - t.FailNow() -} - -// NoFileExists checks whether a file does not exist in a given path. It fails -// if the path points to an existing _file_ only. -func NoFileExists(t TestingT, path string, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NoFileExists(t, path, msgAndArgs...) { - return - } - t.FailNow() -} - -// NoFileExistsf checks whether a file does not exist in a given path. It fails -// if the path points to an existing _file_ only. -func NoFileExistsf(t TestingT, path string, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NoFileExistsf(t, path, msg, args...) { - return - } - t.FailNow() -} - -// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the -// specified substring or element. -// -// require.NotContains(t, "Hello World", "Earth") -// require.NotContains(t, ["Hello", "World"], "Earth") -// require.NotContains(t, {"Hello": "World"}, "Earth") -func NotContains(t TestingT, s interface{}, contains interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NotContains(t, s, contains, msgAndArgs...) { - return - } - t.FailNow() -} - -// NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the -// specified substring or element. -// -// require.NotContainsf(t, "Hello World", "Earth", "error message %s", "formatted") -// require.NotContainsf(t, ["Hello", "World"], "Earth", "error message %s", "formatted") -// require.NotContainsf(t, {"Hello": "World"}, "Earth", "error message %s", "formatted") -func NotContainsf(t TestingT, s interface{}, contains interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NotContainsf(t, s, contains, msg, args...) { - return - } - t.FailNow() -} - -// NotElementsMatch asserts that the specified listA(array, slice...) is NOT equal to specified -// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, -// the number of appearances of each of them in both lists should not match. -// This is an inverse of ElementsMatch. -// -// require.NotElementsMatch(t, [1, 1, 2, 3], [1, 1, 2, 3]) -> false -// -// require.NotElementsMatch(t, [1, 1, 2, 3], [1, 2, 3]) -> true -// -// require.NotElementsMatch(t, [1, 2, 3], [1, 2, 4]) -> true -func NotElementsMatch(t TestingT, listA interface{}, listB interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NotElementsMatch(t, listA, listB, msgAndArgs...) { - return - } - t.FailNow() -} - -// NotElementsMatchf asserts that the specified listA(array, slice...) is NOT equal to specified -// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, -// the number of appearances of each of them in both lists should not match. -// This is an inverse of ElementsMatch. -// -// require.NotElementsMatchf(t, [1, 1, 2, 3], [1, 1, 2, 3], "error message %s", "formatted") -> false -// -// require.NotElementsMatchf(t, [1, 1, 2, 3], [1, 2, 3], "error message %s", "formatted") -> true -// -// require.NotElementsMatchf(t, [1, 2, 3], [1, 2, 4], "error message %s", "formatted") -> true -func NotElementsMatchf(t TestingT, listA interface{}, listB interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NotElementsMatchf(t, listA, listB, msg, args...) { - return - } - t.FailNow() -} - -// NotEmpty asserts that the specified object is NOT [Empty]. -// -// require.NotEmpty(t, obj) -// require.Equal(t, "two", obj[1]) -func NotEmpty(t TestingT, object interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NotEmpty(t, object, msgAndArgs...) { - return - } - t.FailNow() -} - -// NotEmptyf asserts that the specified object is NOT [Empty]. -// -// require.NotEmptyf(t, obj, "error message %s", "formatted") -// require.Equal(t, "two", obj[1]) -func NotEmptyf(t TestingT, object interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NotEmptyf(t, object, msg, args...) { - return - } - t.FailNow() -} - -// NotEqual asserts that the specified values are NOT equal. -// -// require.NotEqual(t, obj1, obj2) -// -// Pointer variable equality is determined based on the equality of the -// referenced values (as opposed to the memory addresses). -func NotEqual(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NotEqual(t, expected, actual, msgAndArgs...) { - return - } - t.FailNow() -} - -// NotEqualValues asserts that two objects are not equal even when converted to the same type -// -// require.NotEqualValues(t, obj1, obj2) -func NotEqualValues(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NotEqualValues(t, expected, actual, msgAndArgs...) { - return - } - t.FailNow() -} - -// NotEqualValuesf asserts that two objects are not equal even when converted to the same type -// -// require.NotEqualValuesf(t, obj1, obj2, "error message %s", "formatted") -func NotEqualValuesf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NotEqualValuesf(t, expected, actual, msg, args...) { - return - } - t.FailNow() -} - -// NotEqualf asserts that the specified values are NOT equal. -// -// require.NotEqualf(t, obj1, obj2, "error message %s", "formatted") -// -// Pointer variable equality is determined based on the equality of the -// referenced values (as opposed to the memory addresses). -func NotEqualf(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NotEqualf(t, expected, actual, msg, args...) { - return - } - t.FailNow() -} - -// NotErrorAs asserts that none of the errors in err's chain matches target, -// but if so, sets target to that error value. -func NotErrorAs(t TestingT, err error, target interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NotErrorAs(t, err, target, msgAndArgs...) { - return - } - t.FailNow() -} - -// NotErrorAsf asserts that none of the errors in err's chain matches target, -// but if so, sets target to that error value. -func NotErrorAsf(t TestingT, err error, target interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NotErrorAsf(t, err, target, msg, args...) { - return - } - t.FailNow() -} - -// NotErrorIs asserts that none of the errors in err's chain matches target. -// This is a wrapper for errors.Is. -func NotErrorIs(t TestingT, err error, target error, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NotErrorIs(t, err, target, msgAndArgs...) { - return - } - t.FailNow() -} - -// NotErrorIsf asserts that none of the errors in err's chain matches target. -// This is a wrapper for errors.Is. -func NotErrorIsf(t TestingT, err error, target error, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NotErrorIsf(t, err, target, msg, args...) { - return - } - t.FailNow() -} - -// NotImplements asserts that an object does not implement the specified interface. -// -// require.NotImplements(t, (*MyInterface)(nil), new(MyObject)) -func NotImplements(t TestingT, interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NotImplements(t, interfaceObject, object, msgAndArgs...) { - return - } - t.FailNow() -} - -// NotImplementsf asserts that an object does not implement the specified interface. -// -// require.NotImplementsf(t, (*MyInterface)(nil), new(MyObject), "error message %s", "formatted") -func NotImplementsf(t TestingT, interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NotImplementsf(t, interfaceObject, object, msg, args...) { - return - } - t.FailNow() -} - -// NotNil asserts that the specified object is not nil. -// -// require.NotNil(t, err) -func NotNil(t TestingT, object interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NotNil(t, object, msgAndArgs...) { - return - } - t.FailNow() -} - -// NotNilf asserts that the specified object is not nil. -// -// require.NotNilf(t, err, "error message %s", "formatted") -func NotNilf(t TestingT, object interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NotNilf(t, object, msg, args...) { - return - } - t.FailNow() -} - -// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. -// -// require.NotPanics(t, func(){ RemainCalm() }) -func NotPanics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NotPanics(t, f, msgAndArgs...) { - return - } - t.FailNow() -} - -// NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. -// -// require.NotPanicsf(t, func(){ RemainCalm() }, "error message %s", "formatted") -func NotPanicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NotPanicsf(t, f, msg, args...) { - return - } - t.FailNow() -} - -// NotRegexp asserts that a specified regexp does not match a string. -// -// require.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") -// require.NotRegexp(t, "^start", "it's not starting") -func NotRegexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NotRegexp(t, rx, str, msgAndArgs...) { - return - } - t.FailNow() -} - -// NotRegexpf asserts that a specified regexp does not match a string. -// -// require.NotRegexpf(t, regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") -// require.NotRegexpf(t, "^start", "it's not starting", "error message %s", "formatted") -func NotRegexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NotRegexpf(t, rx, str, msg, args...) { - return - } - t.FailNow() -} - -// NotSame asserts that two pointers do not reference the same object. -// -// require.NotSame(t, ptr1, ptr2) -// -// Both arguments must be pointer variables. Pointer variable sameness is -// determined based on the equality of both type and value. -func NotSame(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NotSame(t, expected, actual, msgAndArgs...) { - return - } - t.FailNow() -} - -// NotSamef asserts that two pointers do not reference the same object. -// -// require.NotSamef(t, ptr1, ptr2, "error message %s", "formatted") -// -// Both arguments must be pointer variables. Pointer variable sameness is -// determined based on the equality of both type and value. -func NotSamef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NotSamef(t, expected, actual, msg, args...) { - return - } - t.FailNow() -} - -// NotSubset asserts that the list (array, slice, or map) does NOT contain all -// elements given in the subset (array, slice, or map). -// Map elements are key-value pairs unless compared with an array or slice where -// only the map key is evaluated. -// -// require.NotSubset(t, [1, 3, 4], [1, 2]) -// require.NotSubset(t, {"x": 1, "y": 2}, {"z": 3}) -// require.NotSubset(t, [1, 3, 4], {1: "one", 2: "two"}) -// require.NotSubset(t, {"x": 1, "y": 2}, ["z"]) -func NotSubset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NotSubset(t, list, subset, msgAndArgs...) { - return - } - t.FailNow() -} - -// NotSubsetf asserts that the list (array, slice, or map) does NOT contain all -// elements given in the subset (array, slice, or map). -// Map elements are key-value pairs unless compared with an array or slice where -// only the map key is evaluated. -// -// require.NotSubsetf(t, [1, 3, 4], [1, 2], "error message %s", "formatted") -// require.NotSubsetf(t, {"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") -// require.NotSubsetf(t, [1, 3, 4], {1: "one", 2: "two"}, "error message %s", "formatted") -// require.NotSubsetf(t, {"x": 1, "y": 2}, ["z"], "error message %s", "formatted") -func NotSubsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NotSubsetf(t, list, subset, msg, args...) { - return - } - t.FailNow() -} - -// NotZero asserts that i is not the zero value for its type. -func NotZero(t TestingT, i interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NotZero(t, i, msgAndArgs...) { - return - } - t.FailNow() -} - -// NotZerof asserts that i is not the zero value for its type. -func NotZerof(t TestingT, i interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.NotZerof(t, i, msg, args...) { - return - } - t.FailNow() -} - -// Panics asserts that the code inside the specified PanicTestFunc panics. -// -// require.Panics(t, func(){ GoCrazy() }) -func Panics(t TestingT, f assert.PanicTestFunc, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Panics(t, f, msgAndArgs...) { - return - } - t.FailNow() -} - -// PanicsWithError asserts that the code inside the specified PanicTestFunc -// panics, and that the recovered panic value is an error that satisfies the -// EqualError comparison. -// -// require.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) -func PanicsWithError(t TestingT, errString string, f assert.PanicTestFunc, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.PanicsWithError(t, errString, f, msgAndArgs...) { - return - } - t.FailNow() -} - -// PanicsWithErrorf asserts that the code inside the specified PanicTestFunc -// panics, and that the recovered panic value is an error that satisfies the -// EqualError comparison. -// -// require.PanicsWithErrorf(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") -func PanicsWithErrorf(t TestingT, errString string, f assert.PanicTestFunc, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.PanicsWithErrorf(t, errString, f, msg, args...) { - return - } - t.FailNow() -} - -// PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that -// the recovered panic value equals the expected panic value. -// -// require.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) -func PanicsWithValue(t TestingT, expected interface{}, f assert.PanicTestFunc, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.PanicsWithValue(t, expected, f, msgAndArgs...) { - return - } - t.FailNow() -} - -// PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that -// the recovered panic value equals the expected panic value. -// -// require.PanicsWithValuef(t, "crazy error", func(){ GoCrazy() }, "error message %s", "formatted") -func PanicsWithValuef(t TestingT, expected interface{}, f assert.PanicTestFunc, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.PanicsWithValuef(t, expected, f, msg, args...) { - return - } - t.FailNow() -} - -// Panicsf asserts that the code inside the specified PanicTestFunc panics. -// -// require.Panicsf(t, func(){ GoCrazy() }, "error message %s", "formatted") -func Panicsf(t TestingT, f assert.PanicTestFunc, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Panicsf(t, f, msg, args...) { - return - } - t.FailNow() -} - -// Positive asserts that the specified element is positive -// -// require.Positive(t, 1) -// require.Positive(t, 1.23) -func Positive(t TestingT, e interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Positive(t, e, msgAndArgs...) { - return - } - t.FailNow() -} - -// Positivef asserts that the specified element is positive -// -// require.Positivef(t, 1, "error message %s", "formatted") -// require.Positivef(t, 1.23, "error message %s", "formatted") -func Positivef(t TestingT, e interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Positivef(t, e, msg, args...) { - return - } - t.FailNow() -} - -// Regexp asserts that a specified regexp matches a string. -// -// require.Regexp(t, regexp.MustCompile("start"), "it's starting") -// require.Regexp(t, "start...$", "it's not starting") -func Regexp(t TestingT, rx interface{}, str interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Regexp(t, rx, str, msgAndArgs...) { - return - } - t.FailNow() -} - -// Regexpf asserts that a specified regexp matches a string. -// -// require.Regexpf(t, regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") -// require.Regexpf(t, "start...$", "it's not starting", "error message %s", "formatted") -func Regexpf(t TestingT, rx interface{}, str interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Regexpf(t, rx, str, msg, args...) { - return - } - t.FailNow() -} - -// Same asserts that two pointers reference the same object. -// -// require.Same(t, ptr1, ptr2) -// -// Both arguments must be pointer variables. Pointer variable sameness is -// determined based on the equality of both type and value. -func Same(t TestingT, expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Same(t, expected, actual, msgAndArgs...) { - return - } - t.FailNow() -} - -// Samef asserts that two pointers reference the same object. -// -// require.Samef(t, ptr1, ptr2, "error message %s", "formatted") -// -// Both arguments must be pointer variables. Pointer variable sameness is -// determined based on the equality of both type and value. -func Samef(t TestingT, expected interface{}, actual interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Samef(t, expected, actual, msg, args...) { - return - } - t.FailNow() -} - -// Subset asserts that the list (array, slice, or map) contains all elements -// given in the subset (array, slice, or map). -// Map elements are key-value pairs unless compared with an array or slice where -// only the map key is evaluated. -// -// require.Subset(t, [1, 2, 3], [1, 2]) -// require.Subset(t, {"x": 1, "y": 2}, {"x": 1}) -// require.Subset(t, [1, 2, 3], {1: "one", 2: "two"}) -// require.Subset(t, {"x": 1, "y": 2}, ["x"]) -func Subset(t TestingT, list interface{}, subset interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Subset(t, list, subset, msgAndArgs...) { - return - } - t.FailNow() -} - -// Subsetf asserts that the list (array, slice, or map) contains all elements -// given in the subset (array, slice, or map). -// Map elements are key-value pairs unless compared with an array or slice where -// only the map key is evaluated. -// -// require.Subsetf(t, [1, 2, 3], [1, 2], "error message %s", "formatted") -// require.Subsetf(t, {"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") -// require.Subsetf(t, [1, 2, 3], {1: "one", 2: "two"}, "error message %s", "formatted") -// require.Subsetf(t, {"x": 1, "y": 2}, ["x"], "error message %s", "formatted") -func Subsetf(t TestingT, list interface{}, subset interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Subsetf(t, list, subset, msg, args...) { - return - } - t.FailNow() -} - -// True asserts that the specified value is true. -// -// require.True(t, myBool) -func True(t TestingT, value bool, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.True(t, value, msgAndArgs...) { - return - } - t.FailNow() -} - -// Truef asserts that the specified value is true. -// -// require.Truef(t, myBool, "error message %s", "formatted") -func Truef(t TestingT, value bool, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Truef(t, value, msg, args...) { - return - } - t.FailNow() -} - -// WithinDuration asserts that the two times are within duration delta of each other. -// -// require.WithinDuration(t, time.Now(), time.Now(), 10*time.Second) -func WithinDuration(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.WithinDuration(t, expected, actual, delta, msgAndArgs...) { - return - } - t.FailNow() -} - -// WithinDurationf asserts that the two times are within duration delta of each other. -// -// require.WithinDurationf(t, time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") -func WithinDurationf(t TestingT, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.WithinDurationf(t, expected, actual, delta, msg, args...) { - return - } - t.FailNow() -} - -// WithinRange asserts that a time is within a time range (inclusive). -// -// require.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) -func WithinRange(t TestingT, actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.WithinRange(t, actual, start, end, msgAndArgs...) { - return - } - t.FailNow() -} - -// WithinRangef asserts that a time is within a time range (inclusive). -// -// require.WithinRangef(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") -func WithinRangef(t TestingT, actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.WithinRangef(t, actual, start, end, msg, args...) { - return - } - t.FailNow() -} - -// YAMLEq asserts that the first documents in the two YAML strings are equivalent. -// -// expected := `--- -// key: value -// --- -// key: this is a second document, it is not evaluated -// ` -// actual := `--- -// key: value -// --- -// key: this is a subsequent document, it is not evaluated -// ` -// require.YAMLEq(t, expected, actual) -func YAMLEq(t TestingT, expected string, actual string, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.YAMLEq(t, expected, actual, msgAndArgs...) { - return - } - t.FailNow() -} - -// YAMLEqf asserts that the first documents in the two YAML strings are equivalent. -// -// expected := `--- -// key: value -// --- -// key: this is a second document, it is not evaluated -// ` -// actual := `--- -// key: value -// --- -// key: this is a subsequent document, it is not evaluated -// ` -// require.YAMLEqf(t, expected, actual, "error message %s", "formatted") -func YAMLEqf(t TestingT, expected string, actual string, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.YAMLEqf(t, expected, actual, msg, args...) { - return - } - t.FailNow() -} - -// Zero asserts that i is the zero value for its type. -func Zero(t TestingT, i interface{}, msgAndArgs ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Zero(t, i, msgAndArgs...) { - return - } - t.FailNow() -} - -// Zerof asserts that i is the zero value for its type. -func Zerof(t TestingT, i interface{}, msg string, args ...interface{}) { - if h, ok := t.(tHelper); ok { - h.Helper() - } - if assert.Zerof(t, i, msg, args...) { - return - } - t.FailNow() -} diff --git a/vendor/github.com/stretchr/testify/require/require.go.tmpl b/vendor/github.com/stretchr/testify/require/require.go.tmpl deleted file mode 100644 index 6a975501e..000000000 --- a/vendor/github.com/stretchr/testify/require/require.go.tmpl +++ /dev/null @@ -1,6 +0,0 @@ -{{.CommentRequire}} -func {{.DocInfo.Name}}(t TestingT, {{.Params}}) { - if h, ok := t.(tHelper); ok { h.Helper() } - if assert.{{.DocInfo.Name}}(t, {{.ForwardedParams}}) { return } - t.FailNow() -} diff --git a/vendor/github.com/stretchr/testify/require/require_forward.go b/vendor/github.com/stretchr/testify/require/require_forward.go deleted file mode 100644 index edac147ef..000000000 --- a/vendor/github.com/stretchr/testify/require/require_forward.go +++ /dev/null @@ -1,1720 +0,0 @@ -// Code generated with github.com/stretchr/testify/_codegen; DO NOT EDIT. - -package require - -import ( - assert "github.com/stretchr/testify/assert" - http "net/http" - url "net/url" - time "time" -) - -// Condition uses a Comparison to assert a complex condition. -func (a *Assertions) Condition(comp assert.Comparison, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Condition(a.t, comp, msgAndArgs...) -} - -// Conditionf uses a Comparison to assert a complex condition. -func (a *Assertions) Conditionf(comp assert.Comparison, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Conditionf(a.t, comp, msg, args...) -} - -// Contains asserts that the specified string, list(array, slice...) or map contains the -// specified substring or element. -// -// a.Contains("Hello World", "World") -// a.Contains(["Hello", "World"], "World") -// a.Contains({"Hello": "World"}, "Hello") -func (a *Assertions) Contains(s interface{}, contains interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Contains(a.t, s, contains, msgAndArgs...) -} - -// Containsf asserts that the specified string, list(array, slice...) or map contains the -// specified substring or element. -// -// a.Containsf("Hello World", "World", "error message %s", "formatted") -// a.Containsf(["Hello", "World"], "World", "error message %s", "formatted") -// a.Containsf({"Hello": "World"}, "Hello", "error message %s", "formatted") -func (a *Assertions) Containsf(s interface{}, contains interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Containsf(a.t, s, contains, msg, args...) -} - -// DirExists checks whether a directory exists in the given path. It also fails -// if the path is a file rather a directory or there is an error checking whether it exists. -func (a *Assertions) DirExists(path string, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - DirExists(a.t, path, msgAndArgs...) -} - -// DirExistsf checks whether a directory exists in the given path. It also fails -// if the path is a file rather a directory or there is an error checking whether it exists. -func (a *Assertions) DirExistsf(path string, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - DirExistsf(a.t, path, msg, args...) -} - -// ElementsMatch asserts that the specified listA(array, slice...) is equal to specified -// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, -// the number of appearances of each of them in both lists should match. -// -// a.ElementsMatch([1, 3, 2, 3], [1, 3, 3, 2]) -func (a *Assertions) ElementsMatch(listA interface{}, listB interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - ElementsMatch(a.t, listA, listB, msgAndArgs...) -} - -// ElementsMatchf asserts that the specified listA(array, slice...) is equal to specified -// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, -// the number of appearances of each of them in both lists should match. -// -// a.ElementsMatchf([1, 3, 2, 3], [1, 3, 3, 2], "error message %s", "formatted") -func (a *Assertions) ElementsMatchf(listA interface{}, listB interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - ElementsMatchf(a.t, listA, listB, msg, args...) -} - -// Empty asserts that the given value is "empty". -// -// [Zero values] are "empty". -// -// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). -// -// Slices, maps and channels with zero length are "empty". -// -// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". -// -// a.Empty(obj) -// -// [Zero values]: https://go.dev/ref/spec#The_zero_value -func (a *Assertions) Empty(object interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Empty(a.t, object, msgAndArgs...) -} - -// Emptyf asserts that the given value is "empty". -// -// [Zero values] are "empty". -// -// Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). -// -// Slices, maps and channels with zero length are "empty". -// -// Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". -// -// a.Emptyf(obj, "error message %s", "formatted") -// -// [Zero values]: https://go.dev/ref/spec#The_zero_value -func (a *Assertions) Emptyf(object interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Emptyf(a.t, object, msg, args...) -} - -// Equal asserts that two objects are equal. -// -// a.Equal(123, 123) -// -// Pointer variable equality is determined based on the equality of the -// referenced values (as opposed to the memory addresses). Function equality -// cannot be determined and will always fail. -func (a *Assertions) Equal(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Equal(a.t, expected, actual, msgAndArgs...) -} - -// EqualError asserts that a function returned a non-nil error (i.e. an error) -// and that it is equal to the provided error. -// -// actualObj, err := SomeFunction() -// a.EqualError(err, expectedErrorString) -func (a *Assertions) EqualError(theError error, errString string, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - EqualError(a.t, theError, errString, msgAndArgs...) -} - -// EqualErrorf asserts that a function returned a non-nil error (i.e. an error) -// and that it is equal to the provided error. -// -// actualObj, err := SomeFunction() -// a.EqualErrorf(err, expectedErrorString, "error message %s", "formatted") -func (a *Assertions) EqualErrorf(theError error, errString string, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - EqualErrorf(a.t, theError, errString, msg, args...) -} - -// EqualExportedValues asserts that the types of two objects are equal and their public -// fields are also equal. This is useful for comparing structs that have private fields -// that could potentially differ. -// -// type S struct { -// Exported int -// notExported int -// } -// a.EqualExportedValues(S{1, 2}, S{1, 3}) => true -// a.EqualExportedValues(S{1, 2}, S{2, 3}) => false -func (a *Assertions) EqualExportedValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - EqualExportedValues(a.t, expected, actual, msgAndArgs...) -} - -// EqualExportedValuesf asserts that the types of two objects are equal and their public -// fields are also equal. This is useful for comparing structs that have private fields -// that could potentially differ. -// -// type S struct { -// Exported int -// notExported int -// } -// a.EqualExportedValuesf(S{1, 2}, S{1, 3}, "error message %s", "formatted") => true -// a.EqualExportedValuesf(S{1, 2}, S{2, 3}, "error message %s", "formatted") => false -func (a *Assertions) EqualExportedValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - EqualExportedValuesf(a.t, expected, actual, msg, args...) -} - -// EqualValues asserts that two objects are equal or convertible to the larger -// type and equal. -// -// a.EqualValues(uint32(123), int32(123)) -func (a *Assertions) EqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - EqualValues(a.t, expected, actual, msgAndArgs...) -} - -// EqualValuesf asserts that two objects are equal or convertible to the larger -// type and equal. -// -// a.EqualValuesf(uint32(123), int32(123), "error message %s", "formatted") -func (a *Assertions) EqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - EqualValuesf(a.t, expected, actual, msg, args...) -} - -// Equalf asserts that two objects are equal. -// -// a.Equalf(123, 123, "error message %s", "formatted") -// -// Pointer variable equality is determined based on the equality of the -// referenced values (as opposed to the memory addresses). Function equality -// cannot be determined and will always fail. -func (a *Assertions) Equalf(expected interface{}, actual interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Equalf(a.t, expected, actual, msg, args...) -} - -// Error asserts that a function returned a non-nil error (ie. an error). -// -// actualObj, err := SomeFunction() -// a.Error(err) -func (a *Assertions) Error(err error, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Error(a.t, err, msgAndArgs...) -} - -// ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. -// This is a wrapper for errors.As. -func (a *Assertions) ErrorAs(err error, target interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - ErrorAs(a.t, err, target, msgAndArgs...) -} - -// ErrorAsf asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. -// This is a wrapper for errors.As. -func (a *Assertions) ErrorAsf(err error, target interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - ErrorAsf(a.t, err, target, msg, args...) -} - -// ErrorContains asserts that a function returned a non-nil error (i.e. an -// error) and that the error contains the specified substring. -// -// actualObj, err := SomeFunction() -// a.ErrorContains(err, expectedErrorSubString) -func (a *Assertions) ErrorContains(theError error, contains string, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - ErrorContains(a.t, theError, contains, msgAndArgs...) -} - -// ErrorContainsf asserts that a function returned a non-nil error (i.e. an -// error) and that the error contains the specified substring. -// -// actualObj, err := SomeFunction() -// a.ErrorContainsf(err, expectedErrorSubString, "error message %s", "formatted") -func (a *Assertions) ErrorContainsf(theError error, contains string, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - ErrorContainsf(a.t, theError, contains, msg, args...) -} - -// ErrorIs asserts that at least one of the errors in err's chain matches target. -// This is a wrapper for errors.Is. -func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - ErrorIs(a.t, err, target, msgAndArgs...) -} - -// ErrorIsf asserts that at least one of the errors in err's chain matches target. -// This is a wrapper for errors.Is. -func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - ErrorIsf(a.t, err, target, msg, args...) -} - -// Errorf asserts that a function returned a non-nil error (ie. an error). -// -// actualObj, err := SomeFunction() -// a.Errorf(err, "error message %s", "formatted") -func (a *Assertions) Errorf(err error, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Errorf(a.t, err, msg, args...) -} - -// Eventually asserts that given condition will be met in waitFor time, -// periodically checking target function each tick. -// -// a.Eventually(func() bool { return true; }, time.Second, 10*time.Millisecond) -func (a *Assertions) Eventually(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Eventually(a.t, condition, waitFor, tick, msgAndArgs...) -} - -// EventuallyWithT asserts that given condition will be met in waitFor time, -// periodically checking target function each tick. In contrast to Eventually, -// it supplies a CollectT to the condition function, so that the condition -// function can use the CollectT to call other assertions. -// The condition is considered "met" if no errors are raised in a tick. -// The supplied CollectT collects all errors from one tick (if there are any). -// If the condition is not met before waitFor, the collected errors of -// the last tick are copied to t. -// -// externalValue := false -// go func() { -// time.Sleep(8*time.Second) -// externalValue = true -// }() -// a.EventuallyWithT(func(c *assert.CollectT) { -// // add assertions as needed; any assertion failure will fail the current tick -// assert.True(c, externalValue, "expected 'externalValue' to be true") -// }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") -func (a *Assertions) EventuallyWithT(condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - EventuallyWithT(a.t, condition, waitFor, tick, msgAndArgs...) -} - -// EventuallyWithTf asserts that given condition will be met in waitFor time, -// periodically checking target function each tick. In contrast to Eventually, -// it supplies a CollectT to the condition function, so that the condition -// function can use the CollectT to call other assertions. -// The condition is considered "met" if no errors are raised in a tick. -// The supplied CollectT collects all errors from one tick (if there are any). -// If the condition is not met before waitFor, the collected errors of -// the last tick are copied to t. -// -// externalValue := false -// go func() { -// time.Sleep(8*time.Second) -// externalValue = true -// }() -// a.EventuallyWithTf(func(c *assert.CollectT) { -// // add assertions as needed; any assertion failure will fail the current tick -// assert.True(c, externalValue, "expected 'externalValue' to be true") -// }, 10*time.Second, 1*time.Second, "error message %s", "formatted") -func (a *Assertions) EventuallyWithTf(condition func(collect *assert.CollectT), waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - EventuallyWithTf(a.t, condition, waitFor, tick, msg, args...) -} - -// Eventuallyf asserts that given condition will be met in waitFor time, -// periodically checking target function each tick. -// -// a.Eventuallyf(func() bool { return true; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") -func (a *Assertions) Eventuallyf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Eventuallyf(a.t, condition, waitFor, tick, msg, args...) -} - -// Exactly asserts that two objects are equal in value and type. -// -// a.Exactly(int32(123), int64(123)) -func (a *Assertions) Exactly(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Exactly(a.t, expected, actual, msgAndArgs...) -} - -// Exactlyf asserts that two objects are equal in value and type. -// -// a.Exactlyf(int32(123), int64(123), "error message %s", "formatted") -func (a *Assertions) Exactlyf(expected interface{}, actual interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Exactlyf(a.t, expected, actual, msg, args...) -} - -// Fail reports a failure through -func (a *Assertions) Fail(failureMessage string, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Fail(a.t, failureMessage, msgAndArgs...) -} - -// FailNow fails test -func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - FailNow(a.t, failureMessage, msgAndArgs...) -} - -// FailNowf fails test -func (a *Assertions) FailNowf(failureMessage string, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - FailNowf(a.t, failureMessage, msg, args...) -} - -// Failf reports a failure through -func (a *Assertions) Failf(failureMessage string, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Failf(a.t, failureMessage, msg, args...) -} - -// False asserts that the specified value is false. -// -// a.False(myBool) -func (a *Assertions) False(value bool, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - False(a.t, value, msgAndArgs...) -} - -// Falsef asserts that the specified value is false. -// -// a.Falsef(myBool, "error message %s", "formatted") -func (a *Assertions) Falsef(value bool, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Falsef(a.t, value, msg, args...) -} - -// FileExists checks whether a file exists in the given path. It also fails if -// the path points to a directory or there is an error when trying to check the file. -func (a *Assertions) FileExists(path string, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - FileExists(a.t, path, msgAndArgs...) -} - -// FileExistsf checks whether a file exists in the given path. It also fails if -// the path points to a directory or there is an error when trying to check the file. -func (a *Assertions) FileExistsf(path string, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - FileExistsf(a.t, path, msg, args...) -} - -// Greater asserts that the first element is greater than the second -// -// a.Greater(2, 1) -// a.Greater(float64(2), float64(1)) -// a.Greater("b", "a") -func (a *Assertions) Greater(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Greater(a.t, e1, e2, msgAndArgs...) -} - -// GreaterOrEqual asserts that the first element is greater than or equal to the second -// -// a.GreaterOrEqual(2, 1) -// a.GreaterOrEqual(2, 2) -// a.GreaterOrEqual("b", "a") -// a.GreaterOrEqual("b", "b") -func (a *Assertions) GreaterOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - GreaterOrEqual(a.t, e1, e2, msgAndArgs...) -} - -// GreaterOrEqualf asserts that the first element is greater than or equal to the second -// -// a.GreaterOrEqualf(2, 1, "error message %s", "formatted") -// a.GreaterOrEqualf(2, 2, "error message %s", "formatted") -// a.GreaterOrEqualf("b", "a", "error message %s", "formatted") -// a.GreaterOrEqualf("b", "b", "error message %s", "formatted") -func (a *Assertions) GreaterOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - GreaterOrEqualf(a.t, e1, e2, msg, args...) -} - -// Greaterf asserts that the first element is greater than the second -// -// a.Greaterf(2, 1, "error message %s", "formatted") -// a.Greaterf(float64(2), float64(1), "error message %s", "formatted") -// a.Greaterf("b", "a", "error message %s", "formatted") -func (a *Assertions) Greaterf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Greaterf(a.t, e1, e2, msg, args...) -} - -// HTTPBodyContains asserts that a specified handler returns a -// body that contains a string. -// -// a.HTTPBodyContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") -func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - HTTPBodyContains(a.t, handler, method, url, values, str, msgAndArgs...) -} - -// HTTPBodyContainsf asserts that a specified handler returns a -// body that contains a string. -// -// a.HTTPBodyContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") -func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - HTTPBodyContainsf(a.t, handler, method, url, values, str, msg, args...) -} - -// HTTPBodyNotContains asserts that a specified handler returns a -// body that does not contain a string. -// -// a.HTTPBodyNotContains(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") -func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - HTTPBodyNotContains(a.t, handler, method, url, values, str, msgAndArgs...) -} - -// HTTPBodyNotContainsf asserts that a specified handler returns a -// body that does not contain a string. -// -// a.HTTPBodyNotContainsf(myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky", "error message %s", "formatted") -func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - HTTPBodyNotContainsf(a.t, handler, method, url, values, str, msg, args...) -} - -// HTTPError asserts that a specified handler returns an error status code. -// -// a.HTTPError(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} -func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - HTTPError(a.t, handler, method, url, values, msgAndArgs...) -} - -// HTTPErrorf asserts that a specified handler returns an error status code. -// -// a.HTTPErrorf(myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} -func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - HTTPErrorf(a.t, handler, method, url, values, msg, args...) -} - -// HTTPRedirect asserts that a specified handler returns a redirect status code. -// -// a.HTTPRedirect(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} -func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - HTTPRedirect(a.t, handler, method, url, values, msgAndArgs...) -} - -// HTTPRedirectf asserts that a specified handler returns a redirect status code. -// -// a.HTTPRedirectf(myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} -func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - HTTPRedirectf(a.t, handler, method, url, values, msg, args...) -} - -// HTTPStatusCode asserts that a specified handler returns a specified status code. -// -// a.HTTPStatusCode(myHandler, "GET", "/notImplemented", nil, 501) -func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - HTTPStatusCode(a.t, handler, method, url, values, statuscode, msgAndArgs...) -} - -// HTTPStatusCodef asserts that a specified handler returns a specified status code. -// -// a.HTTPStatusCodef(myHandler, "GET", "/notImplemented", nil, 501, "error message %s", "formatted") -func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - HTTPStatusCodef(a.t, handler, method, url, values, statuscode, msg, args...) -} - -// HTTPSuccess asserts that a specified handler returns a success status code. -// -// a.HTTPSuccess(myHandler, "POST", "http://www.google.com", nil) -func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - HTTPSuccess(a.t, handler, method, url, values, msgAndArgs...) -} - -// HTTPSuccessf asserts that a specified handler returns a success status code. -// -// a.HTTPSuccessf(myHandler, "POST", "http://www.google.com", nil, "error message %s", "formatted") -func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - HTTPSuccessf(a.t, handler, method, url, values, msg, args...) -} - -// Implements asserts that an object is implemented by the specified interface. -// -// a.Implements((*MyInterface)(nil), new(MyObject)) -func (a *Assertions) Implements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Implements(a.t, interfaceObject, object, msgAndArgs...) -} - -// Implementsf asserts that an object is implemented by the specified interface. -// -// a.Implementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") -func (a *Assertions) Implementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Implementsf(a.t, interfaceObject, object, msg, args...) -} - -// InDelta asserts that the two numerals are within delta of each other. -// -// a.InDelta(math.Pi, 22/7.0, 0.01) -func (a *Assertions) InDelta(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - InDelta(a.t, expected, actual, delta, msgAndArgs...) -} - -// InDeltaMapValues is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. -func (a *Assertions) InDeltaMapValues(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - InDeltaMapValues(a.t, expected, actual, delta, msgAndArgs...) -} - -// InDeltaMapValuesf is the same as InDelta, but it compares all values between two maps. Both maps must have exactly the same keys. -func (a *Assertions) InDeltaMapValuesf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - InDeltaMapValuesf(a.t, expected, actual, delta, msg, args...) -} - -// InDeltaSlice is the same as InDelta, except it compares two slices. -func (a *Assertions) InDeltaSlice(expected interface{}, actual interface{}, delta float64, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - InDeltaSlice(a.t, expected, actual, delta, msgAndArgs...) -} - -// InDeltaSlicef is the same as InDelta, except it compares two slices. -func (a *Assertions) InDeltaSlicef(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - InDeltaSlicef(a.t, expected, actual, delta, msg, args...) -} - -// InDeltaf asserts that the two numerals are within delta of each other. -// -// a.InDeltaf(math.Pi, 22/7.0, 0.01, "error message %s", "formatted") -func (a *Assertions) InDeltaf(expected interface{}, actual interface{}, delta float64, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - InDeltaf(a.t, expected, actual, delta, msg, args...) -} - -// InEpsilon asserts that expected and actual have a relative error less than epsilon -func (a *Assertions) InEpsilon(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - InEpsilon(a.t, expected, actual, epsilon, msgAndArgs...) -} - -// InEpsilonSlice is the same as InEpsilon, except it compares each value from two slices. -func (a *Assertions) InEpsilonSlice(expected interface{}, actual interface{}, epsilon float64, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - InEpsilonSlice(a.t, expected, actual, epsilon, msgAndArgs...) -} - -// InEpsilonSlicef is the same as InEpsilon, except it compares each value from two slices. -func (a *Assertions) InEpsilonSlicef(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - InEpsilonSlicef(a.t, expected, actual, epsilon, msg, args...) -} - -// InEpsilonf asserts that expected and actual have a relative error less than epsilon -func (a *Assertions) InEpsilonf(expected interface{}, actual interface{}, epsilon float64, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - InEpsilonf(a.t, expected, actual, epsilon, msg, args...) -} - -// IsDecreasing asserts that the collection is decreasing -// -// a.IsDecreasing([]int{2, 1, 0}) -// a.IsDecreasing([]float{2, 1}) -// a.IsDecreasing([]string{"b", "a"}) -func (a *Assertions) IsDecreasing(object interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - IsDecreasing(a.t, object, msgAndArgs...) -} - -// IsDecreasingf asserts that the collection is decreasing -// -// a.IsDecreasingf([]int{2, 1, 0}, "error message %s", "formatted") -// a.IsDecreasingf([]float{2, 1}, "error message %s", "formatted") -// a.IsDecreasingf([]string{"b", "a"}, "error message %s", "formatted") -func (a *Assertions) IsDecreasingf(object interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - IsDecreasingf(a.t, object, msg, args...) -} - -// IsIncreasing asserts that the collection is increasing -// -// a.IsIncreasing([]int{1, 2, 3}) -// a.IsIncreasing([]float{1, 2}) -// a.IsIncreasing([]string{"a", "b"}) -func (a *Assertions) IsIncreasing(object interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - IsIncreasing(a.t, object, msgAndArgs...) -} - -// IsIncreasingf asserts that the collection is increasing -// -// a.IsIncreasingf([]int{1, 2, 3}, "error message %s", "formatted") -// a.IsIncreasingf([]float{1, 2}, "error message %s", "formatted") -// a.IsIncreasingf([]string{"a", "b"}, "error message %s", "formatted") -func (a *Assertions) IsIncreasingf(object interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - IsIncreasingf(a.t, object, msg, args...) -} - -// IsNonDecreasing asserts that the collection is not decreasing -// -// a.IsNonDecreasing([]int{1, 1, 2}) -// a.IsNonDecreasing([]float{1, 2}) -// a.IsNonDecreasing([]string{"a", "b"}) -func (a *Assertions) IsNonDecreasing(object interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - IsNonDecreasing(a.t, object, msgAndArgs...) -} - -// IsNonDecreasingf asserts that the collection is not decreasing -// -// a.IsNonDecreasingf([]int{1, 1, 2}, "error message %s", "formatted") -// a.IsNonDecreasingf([]float{1, 2}, "error message %s", "formatted") -// a.IsNonDecreasingf([]string{"a", "b"}, "error message %s", "formatted") -func (a *Assertions) IsNonDecreasingf(object interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - IsNonDecreasingf(a.t, object, msg, args...) -} - -// IsNonIncreasing asserts that the collection is not increasing -// -// a.IsNonIncreasing([]int{2, 1, 1}) -// a.IsNonIncreasing([]float{2, 1}) -// a.IsNonIncreasing([]string{"b", "a"}) -func (a *Assertions) IsNonIncreasing(object interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - IsNonIncreasing(a.t, object, msgAndArgs...) -} - -// IsNonIncreasingf asserts that the collection is not increasing -// -// a.IsNonIncreasingf([]int{2, 1, 1}, "error message %s", "formatted") -// a.IsNonIncreasingf([]float{2, 1}, "error message %s", "formatted") -// a.IsNonIncreasingf([]string{"b", "a"}, "error message %s", "formatted") -func (a *Assertions) IsNonIncreasingf(object interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - IsNonIncreasingf(a.t, object, msg, args...) -} - -// IsNotType asserts that the specified objects are not of the same type. -// -// a.IsNotType(&NotMyStruct{}, &MyStruct{}) -func (a *Assertions) IsNotType(theType interface{}, object interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - IsNotType(a.t, theType, object, msgAndArgs...) -} - -// IsNotTypef asserts that the specified objects are not of the same type. -// -// a.IsNotTypef(&NotMyStruct{}, &MyStruct{}, "error message %s", "formatted") -func (a *Assertions) IsNotTypef(theType interface{}, object interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - IsNotTypef(a.t, theType, object, msg, args...) -} - -// IsType asserts that the specified objects are of the same type. -// -// a.IsType(&MyStruct{}, &MyStruct{}) -func (a *Assertions) IsType(expectedType interface{}, object interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - IsType(a.t, expectedType, object, msgAndArgs...) -} - -// IsTypef asserts that the specified objects are of the same type. -// -// a.IsTypef(&MyStruct{}, &MyStruct{}, "error message %s", "formatted") -func (a *Assertions) IsTypef(expectedType interface{}, object interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - IsTypef(a.t, expectedType, object, msg, args...) -} - -// JSONEq asserts that two JSON strings are equivalent. -// -// a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) -func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - JSONEq(a.t, expected, actual, msgAndArgs...) -} - -// JSONEqf asserts that two JSON strings are equivalent. -// -// a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "error message %s", "formatted") -func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - JSONEqf(a.t, expected, actual, msg, args...) -} - -// Len asserts that the specified object has specific length. -// Len also fails if the object has a type that len() not accept. -// -// a.Len(mySlice, 3) -func (a *Assertions) Len(object interface{}, length int, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Len(a.t, object, length, msgAndArgs...) -} - -// Lenf asserts that the specified object has specific length. -// Lenf also fails if the object has a type that len() not accept. -// -// a.Lenf(mySlice, 3, "error message %s", "formatted") -func (a *Assertions) Lenf(object interface{}, length int, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Lenf(a.t, object, length, msg, args...) -} - -// Less asserts that the first element is less than the second -// -// a.Less(1, 2) -// a.Less(float64(1), float64(2)) -// a.Less("a", "b") -func (a *Assertions) Less(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Less(a.t, e1, e2, msgAndArgs...) -} - -// LessOrEqual asserts that the first element is less than or equal to the second -// -// a.LessOrEqual(1, 2) -// a.LessOrEqual(2, 2) -// a.LessOrEqual("a", "b") -// a.LessOrEqual("b", "b") -func (a *Assertions) LessOrEqual(e1 interface{}, e2 interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - LessOrEqual(a.t, e1, e2, msgAndArgs...) -} - -// LessOrEqualf asserts that the first element is less than or equal to the second -// -// a.LessOrEqualf(1, 2, "error message %s", "formatted") -// a.LessOrEqualf(2, 2, "error message %s", "formatted") -// a.LessOrEqualf("a", "b", "error message %s", "formatted") -// a.LessOrEqualf("b", "b", "error message %s", "formatted") -func (a *Assertions) LessOrEqualf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - LessOrEqualf(a.t, e1, e2, msg, args...) -} - -// Lessf asserts that the first element is less than the second -// -// a.Lessf(1, 2, "error message %s", "formatted") -// a.Lessf(float64(1), float64(2), "error message %s", "formatted") -// a.Lessf("a", "b", "error message %s", "formatted") -func (a *Assertions) Lessf(e1 interface{}, e2 interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Lessf(a.t, e1, e2, msg, args...) -} - -// Negative asserts that the specified element is negative -// -// a.Negative(-1) -// a.Negative(-1.23) -func (a *Assertions) Negative(e interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Negative(a.t, e, msgAndArgs...) -} - -// Negativef asserts that the specified element is negative -// -// a.Negativef(-1, "error message %s", "formatted") -// a.Negativef(-1.23, "error message %s", "formatted") -func (a *Assertions) Negativef(e interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Negativef(a.t, e, msg, args...) -} - -// Never asserts that the given condition doesn't satisfy in waitFor time, -// periodically checking the target function each tick. -// -// a.Never(func() bool { return false; }, time.Second, 10*time.Millisecond) -func (a *Assertions) Never(condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Never(a.t, condition, waitFor, tick, msgAndArgs...) -} - -// Neverf asserts that the given condition doesn't satisfy in waitFor time, -// periodically checking the target function each tick. -// -// a.Neverf(func() bool { return false; }, time.Second, 10*time.Millisecond, "error message %s", "formatted") -func (a *Assertions) Neverf(condition func() bool, waitFor time.Duration, tick time.Duration, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Neverf(a.t, condition, waitFor, tick, msg, args...) -} - -// Nil asserts that the specified object is nil. -// -// a.Nil(err) -func (a *Assertions) Nil(object interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Nil(a.t, object, msgAndArgs...) -} - -// Nilf asserts that the specified object is nil. -// -// a.Nilf(err, "error message %s", "formatted") -func (a *Assertions) Nilf(object interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Nilf(a.t, object, msg, args...) -} - -// NoDirExists checks whether a directory does not exist in the given path. -// It fails if the path points to an existing _directory_ only. -func (a *Assertions) NoDirExists(path string, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NoDirExists(a.t, path, msgAndArgs...) -} - -// NoDirExistsf checks whether a directory does not exist in the given path. -// It fails if the path points to an existing _directory_ only. -func (a *Assertions) NoDirExistsf(path string, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NoDirExistsf(a.t, path, msg, args...) -} - -// NoError asserts that a function returned a nil error (ie. no error). -// -// actualObj, err := SomeFunction() -// a.NoError(err) -// a.Equal(expectedObj, actualObj) -func (a *Assertions) NoError(err error, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NoError(a.t, err, msgAndArgs...) -} - -// NoErrorf asserts that a function returned a nil error (ie. no error). -// -// actualObj, err := SomeFunction() -// a.NoErrorf(err, "error message %s", "formatted") -// a.Equal(expectedObj, actualObj) -func (a *Assertions) NoErrorf(err error, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NoErrorf(a.t, err, msg, args...) -} - -// NoFileExists checks whether a file does not exist in a given path. It fails -// if the path points to an existing _file_ only. -func (a *Assertions) NoFileExists(path string, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NoFileExists(a.t, path, msgAndArgs...) -} - -// NoFileExistsf checks whether a file does not exist in a given path. It fails -// if the path points to an existing _file_ only. -func (a *Assertions) NoFileExistsf(path string, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NoFileExistsf(a.t, path, msg, args...) -} - -// NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the -// specified substring or element. -// -// a.NotContains("Hello World", "Earth") -// a.NotContains(["Hello", "World"], "Earth") -// a.NotContains({"Hello": "World"}, "Earth") -func (a *Assertions) NotContains(s interface{}, contains interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NotContains(a.t, s, contains, msgAndArgs...) -} - -// NotContainsf asserts that the specified string, list(array, slice...) or map does NOT contain the -// specified substring or element. -// -// a.NotContainsf("Hello World", "Earth", "error message %s", "formatted") -// a.NotContainsf(["Hello", "World"], "Earth", "error message %s", "formatted") -// a.NotContainsf({"Hello": "World"}, "Earth", "error message %s", "formatted") -func (a *Assertions) NotContainsf(s interface{}, contains interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NotContainsf(a.t, s, contains, msg, args...) -} - -// NotElementsMatch asserts that the specified listA(array, slice...) is NOT equal to specified -// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, -// the number of appearances of each of them in both lists should not match. -// This is an inverse of ElementsMatch. -// -// a.NotElementsMatch([1, 1, 2, 3], [1, 1, 2, 3]) -> false -// -// a.NotElementsMatch([1, 1, 2, 3], [1, 2, 3]) -> true -// -// a.NotElementsMatch([1, 2, 3], [1, 2, 4]) -> true -func (a *Assertions) NotElementsMatch(listA interface{}, listB interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NotElementsMatch(a.t, listA, listB, msgAndArgs...) -} - -// NotElementsMatchf asserts that the specified listA(array, slice...) is NOT equal to specified -// listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, -// the number of appearances of each of them in both lists should not match. -// This is an inverse of ElementsMatch. -// -// a.NotElementsMatchf([1, 1, 2, 3], [1, 1, 2, 3], "error message %s", "formatted") -> false -// -// a.NotElementsMatchf([1, 1, 2, 3], [1, 2, 3], "error message %s", "formatted") -> true -// -// a.NotElementsMatchf([1, 2, 3], [1, 2, 4], "error message %s", "formatted") -> true -func (a *Assertions) NotElementsMatchf(listA interface{}, listB interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NotElementsMatchf(a.t, listA, listB, msg, args...) -} - -// NotEmpty asserts that the specified object is NOT [Empty]. -// -// a.NotEmpty(obj) -// a.Equal("two", obj[1]) -func (a *Assertions) NotEmpty(object interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NotEmpty(a.t, object, msgAndArgs...) -} - -// NotEmptyf asserts that the specified object is NOT [Empty]. -// -// a.NotEmptyf(obj, "error message %s", "formatted") -// a.Equal("two", obj[1]) -func (a *Assertions) NotEmptyf(object interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NotEmptyf(a.t, object, msg, args...) -} - -// NotEqual asserts that the specified values are NOT equal. -// -// a.NotEqual(obj1, obj2) -// -// Pointer variable equality is determined based on the equality of the -// referenced values (as opposed to the memory addresses). -func (a *Assertions) NotEqual(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NotEqual(a.t, expected, actual, msgAndArgs...) -} - -// NotEqualValues asserts that two objects are not equal even when converted to the same type -// -// a.NotEqualValues(obj1, obj2) -func (a *Assertions) NotEqualValues(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NotEqualValues(a.t, expected, actual, msgAndArgs...) -} - -// NotEqualValuesf asserts that two objects are not equal even when converted to the same type -// -// a.NotEqualValuesf(obj1, obj2, "error message %s", "formatted") -func (a *Assertions) NotEqualValuesf(expected interface{}, actual interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NotEqualValuesf(a.t, expected, actual, msg, args...) -} - -// NotEqualf asserts that the specified values are NOT equal. -// -// a.NotEqualf(obj1, obj2, "error message %s", "formatted") -// -// Pointer variable equality is determined based on the equality of the -// referenced values (as opposed to the memory addresses). -func (a *Assertions) NotEqualf(expected interface{}, actual interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NotEqualf(a.t, expected, actual, msg, args...) -} - -// NotErrorAs asserts that none of the errors in err's chain matches target, -// but if so, sets target to that error value. -func (a *Assertions) NotErrorAs(err error, target interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NotErrorAs(a.t, err, target, msgAndArgs...) -} - -// NotErrorAsf asserts that none of the errors in err's chain matches target, -// but if so, sets target to that error value. -func (a *Assertions) NotErrorAsf(err error, target interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NotErrorAsf(a.t, err, target, msg, args...) -} - -// NotErrorIs asserts that none of the errors in err's chain matches target. -// This is a wrapper for errors.Is. -func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NotErrorIs(a.t, err, target, msgAndArgs...) -} - -// NotErrorIsf asserts that none of the errors in err's chain matches target. -// This is a wrapper for errors.Is. -func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NotErrorIsf(a.t, err, target, msg, args...) -} - -// NotImplements asserts that an object does not implement the specified interface. -// -// a.NotImplements((*MyInterface)(nil), new(MyObject)) -func (a *Assertions) NotImplements(interfaceObject interface{}, object interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NotImplements(a.t, interfaceObject, object, msgAndArgs...) -} - -// NotImplementsf asserts that an object does not implement the specified interface. -// -// a.NotImplementsf((*MyInterface)(nil), new(MyObject), "error message %s", "formatted") -func (a *Assertions) NotImplementsf(interfaceObject interface{}, object interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NotImplementsf(a.t, interfaceObject, object, msg, args...) -} - -// NotNil asserts that the specified object is not nil. -// -// a.NotNil(err) -func (a *Assertions) NotNil(object interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NotNil(a.t, object, msgAndArgs...) -} - -// NotNilf asserts that the specified object is not nil. -// -// a.NotNilf(err, "error message %s", "formatted") -func (a *Assertions) NotNilf(object interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NotNilf(a.t, object, msg, args...) -} - -// NotPanics asserts that the code inside the specified PanicTestFunc does NOT panic. -// -// a.NotPanics(func(){ RemainCalm() }) -func (a *Assertions) NotPanics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NotPanics(a.t, f, msgAndArgs...) -} - -// NotPanicsf asserts that the code inside the specified PanicTestFunc does NOT panic. -// -// a.NotPanicsf(func(){ RemainCalm() }, "error message %s", "formatted") -func (a *Assertions) NotPanicsf(f assert.PanicTestFunc, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NotPanicsf(a.t, f, msg, args...) -} - -// NotRegexp asserts that a specified regexp does not match a string. -// -// a.NotRegexp(regexp.MustCompile("starts"), "it's starting") -// a.NotRegexp("^start", "it's not starting") -func (a *Assertions) NotRegexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NotRegexp(a.t, rx, str, msgAndArgs...) -} - -// NotRegexpf asserts that a specified regexp does not match a string. -// -// a.NotRegexpf(regexp.MustCompile("starts"), "it's starting", "error message %s", "formatted") -// a.NotRegexpf("^start", "it's not starting", "error message %s", "formatted") -func (a *Assertions) NotRegexpf(rx interface{}, str interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NotRegexpf(a.t, rx, str, msg, args...) -} - -// NotSame asserts that two pointers do not reference the same object. -// -// a.NotSame(ptr1, ptr2) -// -// Both arguments must be pointer variables. Pointer variable sameness is -// determined based on the equality of both type and value. -func (a *Assertions) NotSame(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NotSame(a.t, expected, actual, msgAndArgs...) -} - -// NotSamef asserts that two pointers do not reference the same object. -// -// a.NotSamef(ptr1, ptr2, "error message %s", "formatted") -// -// Both arguments must be pointer variables. Pointer variable sameness is -// determined based on the equality of both type and value. -func (a *Assertions) NotSamef(expected interface{}, actual interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NotSamef(a.t, expected, actual, msg, args...) -} - -// NotSubset asserts that the list (array, slice, or map) does NOT contain all -// elements given in the subset (array, slice, or map). -// Map elements are key-value pairs unless compared with an array or slice where -// only the map key is evaluated. -// -// a.NotSubset([1, 3, 4], [1, 2]) -// a.NotSubset({"x": 1, "y": 2}, {"z": 3}) -// a.NotSubset([1, 3, 4], {1: "one", 2: "two"}) -// a.NotSubset({"x": 1, "y": 2}, ["z"]) -func (a *Assertions) NotSubset(list interface{}, subset interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NotSubset(a.t, list, subset, msgAndArgs...) -} - -// NotSubsetf asserts that the list (array, slice, or map) does NOT contain all -// elements given in the subset (array, slice, or map). -// Map elements are key-value pairs unless compared with an array or slice where -// only the map key is evaluated. -// -// a.NotSubsetf([1, 3, 4], [1, 2], "error message %s", "formatted") -// a.NotSubsetf({"x": 1, "y": 2}, {"z": 3}, "error message %s", "formatted") -// a.NotSubsetf([1, 3, 4], {1: "one", 2: "two"}, "error message %s", "formatted") -// a.NotSubsetf({"x": 1, "y": 2}, ["z"], "error message %s", "formatted") -func (a *Assertions) NotSubsetf(list interface{}, subset interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NotSubsetf(a.t, list, subset, msg, args...) -} - -// NotZero asserts that i is not the zero value for its type. -func (a *Assertions) NotZero(i interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NotZero(a.t, i, msgAndArgs...) -} - -// NotZerof asserts that i is not the zero value for its type. -func (a *Assertions) NotZerof(i interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - NotZerof(a.t, i, msg, args...) -} - -// Panics asserts that the code inside the specified PanicTestFunc panics. -// -// a.Panics(func(){ GoCrazy() }) -func (a *Assertions) Panics(f assert.PanicTestFunc, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Panics(a.t, f, msgAndArgs...) -} - -// PanicsWithError asserts that the code inside the specified PanicTestFunc -// panics, and that the recovered panic value is an error that satisfies the -// EqualError comparison. -// -// a.PanicsWithError("crazy error", func(){ GoCrazy() }) -func (a *Assertions) PanicsWithError(errString string, f assert.PanicTestFunc, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - PanicsWithError(a.t, errString, f, msgAndArgs...) -} - -// PanicsWithErrorf asserts that the code inside the specified PanicTestFunc -// panics, and that the recovered panic value is an error that satisfies the -// EqualError comparison. -// -// a.PanicsWithErrorf("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") -func (a *Assertions) PanicsWithErrorf(errString string, f assert.PanicTestFunc, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - PanicsWithErrorf(a.t, errString, f, msg, args...) -} - -// PanicsWithValue asserts that the code inside the specified PanicTestFunc panics, and that -// the recovered panic value equals the expected panic value. -// -// a.PanicsWithValue("crazy error", func(){ GoCrazy() }) -func (a *Assertions) PanicsWithValue(expected interface{}, f assert.PanicTestFunc, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - PanicsWithValue(a.t, expected, f, msgAndArgs...) -} - -// PanicsWithValuef asserts that the code inside the specified PanicTestFunc panics, and that -// the recovered panic value equals the expected panic value. -// -// a.PanicsWithValuef("crazy error", func(){ GoCrazy() }, "error message %s", "formatted") -func (a *Assertions) PanicsWithValuef(expected interface{}, f assert.PanicTestFunc, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - PanicsWithValuef(a.t, expected, f, msg, args...) -} - -// Panicsf asserts that the code inside the specified PanicTestFunc panics. -// -// a.Panicsf(func(){ GoCrazy() }, "error message %s", "formatted") -func (a *Assertions) Panicsf(f assert.PanicTestFunc, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Panicsf(a.t, f, msg, args...) -} - -// Positive asserts that the specified element is positive -// -// a.Positive(1) -// a.Positive(1.23) -func (a *Assertions) Positive(e interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Positive(a.t, e, msgAndArgs...) -} - -// Positivef asserts that the specified element is positive -// -// a.Positivef(1, "error message %s", "formatted") -// a.Positivef(1.23, "error message %s", "formatted") -func (a *Assertions) Positivef(e interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Positivef(a.t, e, msg, args...) -} - -// Regexp asserts that a specified regexp matches a string. -// -// a.Regexp(regexp.MustCompile("start"), "it's starting") -// a.Regexp("start...$", "it's not starting") -func (a *Assertions) Regexp(rx interface{}, str interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Regexp(a.t, rx, str, msgAndArgs...) -} - -// Regexpf asserts that a specified regexp matches a string. -// -// a.Regexpf(regexp.MustCompile("start"), "it's starting", "error message %s", "formatted") -// a.Regexpf("start...$", "it's not starting", "error message %s", "formatted") -func (a *Assertions) Regexpf(rx interface{}, str interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Regexpf(a.t, rx, str, msg, args...) -} - -// Same asserts that two pointers reference the same object. -// -// a.Same(ptr1, ptr2) -// -// Both arguments must be pointer variables. Pointer variable sameness is -// determined based on the equality of both type and value. -func (a *Assertions) Same(expected interface{}, actual interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Same(a.t, expected, actual, msgAndArgs...) -} - -// Samef asserts that two pointers reference the same object. -// -// a.Samef(ptr1, ptr2, "error message %s", "formatted") -// -// Both arguments must be pointer variables. Pointer variable sameness is -// determined based on the equality of both type and value. -func (a *Assertions) Samef(expected interface{}, actual interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Samef(a.t, expected, actual, msg, args...) -} - -// Subset asserts that the list (array, slice, or map) contains all elements -// given in the subset (array, slice, or map). -// Map elements are key-value pairs unless compared with an array or slice where -// only the map key is evaluated. -// -// a.Subset([1, 2, 3], [1, 2]) -// a.Subset({"x": 1, "y": 2}, {"x": 1}) -// a.Subset([1, 2, 3], {1: "one", 2: "two"}) -// a.Subset({"x": 1, "y": 2}, ["x"]) -func (a *Assertions) Subset(list interface{}, subset interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Subset(a.t, list, subset, msgAndArgs...) -} - -// Subsetf asserts that the list (array, slice, or map) contains all elements -// given in the subset (array, slice, or map). -// Map elements are key-value pairs unless compared with an array or slice where -// only the map key is evaluated. -// -// a.Subsetf([1, 2, 3], [1, 2], "error message %s", "formatted") -// a.Subsetf({"x": 1, "y": 2}, {"x": 1}, "error message %s", "formatted") -// a.Subsetf([1, 2, 3], {1: "one", 2: "two"}, "error message %s", "formatted") -// a.Subsetf({"x": 1, "y": 2}, ["x"], "error message %s", "formatted") -func (a *Assertions) Subsetf(list interface{}, subset interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Subsetf(a.t, list, subset, msg, args...) -} - -// True asserts that the specified value is true. -// -// a.True(myBool) -func (a *Assertions) True(value bool, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - True(a.t, value, msgAndArgs...) -} - -// Truef asserts that the specified value is true. -// -// a.Truef(myBool, "error message %s", "formatted") -func (a *Assertions) Truef(value bool, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Truef(a.t, value, msg, args...) -} - -// WithinDuration asserts that the two times are within duration delta of each other. -// -// a.WithinDuration(time.Now(), time.Now(), 10*time.Second) -func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - WithinDuration(a.t, expected, actual, delta, msgAndArgs...) -} - -// WithinDurationf asserts that the two times are within duration delta of each other. -// -// a.WithinDurationf(time.Now(), time.Now(), 10*time.Second, "error message %s", "formatted") -func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - WithinDurationf(a.t, expected, actual, delta, msg, args...) -} - -// WithinRange asserts that a time is within a time range (inclusive). -// -// a.WithinRange(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) -func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - WithinRange(a.t, actual, start, end, msgAndArgs...) -} - -// WithinRangef asserts that a time is within a time range (inclusive). -// -// a.WithinRangef(time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second), "error message %s", "formatted") -func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - WithinRangef(a.t, actual, start, end, msg, args...) -} - -// YAMLEq asserts that the first documents in the two YAML strings are equivalent. -// -// expected := `--- -// key: value -// --- -// key: this is a second document, it is not evaluated -// ` -// actual := `--- -// key: value -// --- -// key: this is a subsequent document, it is not evaluated -// ` -// a.YAMLEq(expected, actual) -func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - YAMLEq(a.t, expected, actual, msgAndArgs...) -} - -// YAMLEqf asserts that the first documents in the two YAML strings are equivalent. -// -// expected := `--- -// key: value -// --- -// key: this is a second document, it is not evaluated -// ` -// actual := `--- -// key: value -// --- -// key: this is a subsequent document, it is not evaluated -// ` -// a.YAMLEqf(expected, actual, "error message %s", "formatted") -func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - YAMLEqf(a.t, expected, actual, msg, args...) -} - -// Zero asserts that i is the zero value for its type. -func (a *Assertions) Zero(i interface{}, msgAndArgs ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Zero(a.t, i, msgAndArgs...) -} - -// Zerof asserts that i is the zero value for its type. -func (a *Assertions) Zerof(i interface{}, msg string, args ...interface{}) { - if h, ok := a.t.(tHelper); ok { - h.Helper() - } - Zerof(a.t, i, msg, args...) -} diff --git a/vendor/github.com/stretchr/testify/require/require_forward.go.tmpl b/vendor/github.com/stretchr/testify/require/require_forward.go.tmpl deleted file mode 100644 index b3b751de4..000000000 --- a/vendor/github.com/stretchr/testify/require/require_forward.go.tmpl +++ /dev/null @@ -1,5 +0,0 @@ -{{.CommentRequireWithoutT "a"}} -func (a *Assertions) {{.DocInfo.Name}}({{.Params}}) { - if h, ok := a.t.(tHelper); ok { h.Helper() } - {{.DocInfo.Name}}(a.t, {{.ForwardedParams}}) -} diff --git a/vendor/github.com/stretchr/testify/require/requirements.go b/vendor/github.com/stretchr/testify/require/requirements.go deleted file mode 100644 index 375adb0a6..000000000 --- a/vendor/github.com/stretchr/testify/require/requirements.go +++ /dev/null @@ -1,29 +0,0 @@ -package require - -// TestingT is an interface wrapper around *testing.T -type TestingT interface { - Errorf(format string, args ...interface{}) - FailNow() -} - -type tHelper = interface { - Helper() -} - -// ComparisonAssertionFunc is a common function prototype when comparing two values. Can be useful -// for table driven tests. -type ComparisonAssertionFunc = func(TestingT, interface{}, interface{}, ...interface{}) - -// ValueAssertionFunc is a common function prototype when validating a single value. Can be useful -// for table driven tests. -type ValueAssertionFunc = func(TestingT, interface{}, ...interface{}) - -// BoolAssertionFunc is a common function prototype when validating a bool value. Can be useful -// for table driven tests. -type BoolAssertionFunc = func(TestingT, bool, ...interface{}) - -// ErrorAssertionFunc is a common function prototype when validating an error value. Can be useful -// for table driven tests. -type ErrorAssertionFunc = func(TestingT, error, ...interface{}) - -//go:generate sh -c "cd ../_codegen && go build && cd - && ../_codegen/_codegen -output-package=require -template=require.go.tmpl -include-format-funcs" diff --git a/vendor/go.yaml.in/yaml/v3/LICENSE b/vendor/go.yaml.in/yaml/v3/LICENSE deleted file mode 100644 index 2683e4bb1..000000000 --- a/vendor/go.yaml.in/yaml/v3/LICENSE +++ /dev/null @@ -1,50 +0,0 @@ - -This project is covered by two different licenses: MIT and Apache. - -#### MIT License #### - -The following files were ported to Go from C files of libyaml, and thus -are still covered by their original MIT license, with the additional -copyright staring in 2011 when the project was ported over: - - apic.go emitterc.go parserc.go readerc.go scannerc.go - writerc.go yamlh.go yamlprivateh.go - -Copyright (c) 2006-2010 Kirill Simonov -Copyright (c) 2006-2011 Kirill Simonov - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -of the Software, and to permit persons to whom the Software is furnished to do -so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - -### Apache License ### - -All the remaining project files are covered by the Apache license: - -Copyright (c) 2011-2019 Canonical Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/vendor/go.yaml.in/yaml/v3/NOTICE b/vendor/go.yaml.in/yaml/v3/NOTICE deleted file mode 100644 index 866d74a7a..000000000 --- a/vendor/go.yaml.in/yaml/v3/NOTICE +++ /dev/null @@ -1,13 +0,0 @@ -Copyright 2011-2016 Canonical Ltd. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/vendor/go.yaml.in/yaml/v3/README.md b/vendor/go.yaml.in/yaml/v3/README.md deleted file mode 100644 index 15a85a635..000000000 --- a/vendor/go.yaml.in/yaml/v3/README.md +++ /dev/null @@ -1,171 +0,0 @@ -go.yaml.in/yaml -=============== - -YAML Support for the Go Language - - -## Introduction - -The `yaml` package enables [Go](https://go.dev/) programs to comfortably encode -and decode [YAML](https://yaml.org/) values. - -It was originally developed within [Canonical](https://www.canonical.com) as -part of the [juju](https://juju.ubuntu.com) project, and is based on a pure Go -port of the well-known [libyaml](http://pyyaml.org/wiki/LibYAML) C library to -parse and generate YAML data quickly and reliably. - - -## Project Status - -This project started as a fork of the extremely popular [go-yaml]( -https://github.com/go-yaml/yaml/) -project, and is being maintained by the official [YAML organization]( -https://github.com/yaml/). - -The YAML team took over ongoing maintenance and development of the project after -discussion with go-yaml's author, @niemeyer, following his decision to -[label the project repository as "unmaintained"]( -https://github.com/go-yaml/yaml/blob/944c86a7d2/README.md) in April 2025. - -We have put together a team of dedicated maintainers including representatives -of go-yaml's most important downstream projects. - -We will strive to earn the trust of the various go-yaml forks to switch back to -this repository as their upstream. - -Please [contact us](https://cloud-native.slack.com/archives/C08PPAT8PS7) if you -would like to contribute or be involved. - - -## Compatibility - -The `yaml` package supports most of YAML 1.2, but preserves some behavior from -1.1 for backwards compatibility. - -Specifically, v3 of the `yaml` package: - -* Supports YAML 1.1 bools (`yes`/`no`, `on`/`off`) as long as they are being - decoded into a typed bool value. - Otherwise they behave as a string. - Booleans in YAML 1.2 are `true`/`false` only. -* Supports octals encoded and decoded as `0777` per YAML 1.1, rather than - `0o777` as specified in YAML 1.2, because most parsers still use the old - format. - Octals in the `0o777` format are supported though, so new files work. -* Does not support base-60 floats. - These are gone from YAML 1.2, and were actually never supported by this - package as it's clearly a poor choice. - - -## Installation and Usage - -The import path for the package is *go.yaml.in/yaml/v3*. - -To install it, run: - -```bash -go get go.yaml.in/yaml/v3 -``` - - -## API Documentation - -See: - - -## API Stability - -The package API for yaml v3 will remain stable as described in [gopkg.in]( -https://gopkg.in). - - -## Example - -```go -package main - -import ( - "fmt" - "log" - - "go.yaml.in/yaml/v3" -) - -var data = ` -a: Easy! -b: - c: 2 - d: [3, 4] -` - -// Note: struct fields must be public in order for unmarshal to -// correctly populate the data. -type T struct { - A string - B struct { - RenamedC int `yaml:"c"` - D []int `yaml:",flow"` - } -} - -func main() { - t := T{} - - err := yaml.Unmarshal([]byte(data), &t) - if err != nil { - log.Fatalf("error: %v", err) - } - fmt.Printf("--- t:\n%v\n\n", t) - - d, err := yaml.Marshal(&t) - if err != nil { - log.Fatalf("error: %v", err) - } - fmt.Printf("--- t dump:\n%s\n\n", string(d)) - - m := make(map[interface{}]interface{}) - - err = yaml.Unmarshal([]byte(data), &m) - if err != nil { - log.Fatalf("error: %v", err) - } - fmt.Printf("--- m:\n%v\n\n", m) - - d, err = yaml.Marshal(&m) - if err != nil { - log.Fatalf("error: %v", err) - } - fmt.Printf("--- m dump:\n%s\n\n", string(d)) -} -``` - -This example will generate the following output: - -``` ---- t: -{Easy! {2 [3 4]}} - ---- t dump: -a: Easy! -b: - c: 2 - d: [3, 4] - - ---- m: -map[a:Easy! b:map[c:2 d:[3 4]]] - ---- m dump: -a: Easy! -b: - c: 2 - d: - - 3 - - 4 -``` - - -## License - -The yaml package is licensed under the MIT and Apache License 2.0 licenses. -Please see the LICENSE file for details. diff --git a/vendor/go.yaml.in/yaml/v3/apic.go b/vendor/go.yaml.in/yaml/v3/apic.go deleted file mode 100644 index 05fd305da..000000000 --- a/vendor/go.yaml.in/yaml/v3/apic.go +++ /dev/null @@ -1,747 +0,0 @@ -// -// Copyright (c) 2011-2019 Canonical Ltd -// Copyright (c) 2006-2010 Kirill Simonov -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package yaml - -import ( - "io" -) - -func yaml_insert_token(parser *yaml_parser_t, pos int, token *yaml_token_t) { - //fmt.Println("yaml_insert_token", "pos:", pos, "typ:", token.typ, "head:", parser.tokens_head, "len:", len(parser.tokens)) - - // Check if we can move the queue at the beginning of the buffer. - if parser.tokens_head > 0 && len(parser.tokens) == cap(parser.tokens) { - if parser.tokens_head != len(parser.tokens) { - copy(parser.tokens, parser.tokens[parser.tokens_head:]) - } - parser.tokens = parser.tokens[:len(parser.tokens)-parser.tokens_head] - parser.tokens_head = 0 - } - parser.tokens = append(parser.tokens, *token) - if pos < 0 { - return - } - copy(parser.tokens[parser.tokens_head+pos+1:], parser.tokens[parser.tokens_head+pos:]) - parser.tokens[parser.tokens_head+pos] = *token -} - -// Create a new parser object. -func yaml_parser_initialize(parser *yaml_parser_t) bool { - *parser = yaml_parser_t{ - raw_buffer: make([]byte, 0, input_raw_buffer_size), - buffer: make([]byte, 0, input_buffer_size), - } - return true -} - -// Destroy a parser object. -func yaml_parser_delete(parser *yaml_parser_t) { - *parser = yaml_parser_t{} -} - -// String read handler. -func yaml_string_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) { - if parser.input_pos == len(parser.input) { - return 0, io.EOF - } - n = copy(buffer, parser.input[parser.input_pos:]) - parser.input_pos += n - return n, nil -} - -// Reader read handler. -func yaml_reader_read_handler(parser *yaml_parser_t, buffer []byte) (n int, err error) { - return parser.input_reader.Read(buffer) -} - -// Set a string input. -func yaml_parser_set_input_string(parser *yaml_parser_t, input []byte) { - if parser.read_handler != nil { - panic("must set the input source only once") - } - parser.read_handler = yaml_string_read_handler - parser.input = input - parser.input_pos = 0 -} - -// Set a file input. -func yaml_parser_set_input_reader(parser *yaml_parser_t, r io.Reader) { - if parser.read_handler != nil { - panic("must set the input source only once") - } - parser.read_handler = yaml_reader_read_handler - parser.input_reader = r -} - -// Set the source encoding. -func yaml_parser_set_encoding(parser *yaml_parser_t, encoding yaml_encoding_t) { - if parser.encoding != yaml_ANY_ENCODING { - panic("must set the encoding only once") - } - parser.encoding = encoding -} - -// Create a new emitter object. -func yaml_emitter_initialize(emitter *yaml_emitter_t) { - *emitter = yaml_emitter_t{ - buffer: make([]byte, output_buffer_size), - raw_buffer: make([]byte, 0, output_raw_buffer_size), - states: make([]yaml_emitter_state_t, 0, initial_stack_size), - events: make([]yaml_event_t, 0, initial_queue_size), - best_width: -1, - } -} - -// Destroy an emitter object. -func yaml_emitter_delete(emitter *yaml_emitter_t) { - *emitter = yaml_emitter_t{} -} - -// String write handler. -func yaml_string_write_handler(emitter *yaml_emitter_t, buffer []byte) error { - *emitter.output_buffer = append(*emitter.output_buffer, buffer...) - return nil -} - -// yaml_writer_write_handler uses emitter.output_writer to write the -// emitted text. -func yaml_writer_write_handler(emitter *yaml_emitter_t, buffer []byte) error { - _, err := emitter.output_writer.Write(buffer) - return err -} - -// Set a string output. -func yaml_emitter_set_output_string(emitter *yaml_emitter_t, output_buffer *[]byte) { - if emitter.write_handler != nil { - panic("must set the output target only once") - } - emitter.write_handler = yaml_string_write_handler - emitter.output_buffer = output_buffer -} - -// Set a file output. -func yaml_emitter_set_output_writer(emitter *yaml_emitter_t, w io.Writer) { - if emitter.write_handler != nil { - panic("must set the output target only once") - } - emitter.write_handler = yaml_writer_write_handler - emitter.output_writer = w -} - -// Set the output encoding. -func yaml_emitter_set_encoding(emitter *yaml_emitter_t, encoding yaml_encoding_t) { - if emitter.encoding != yaml_ANY_ENCODING { - panic("must set the output encoding only once") - } - emitter.encoding = encoding -} - -// Set the canonical output style. -func yaml_emitter_set_canonical(emitter *yaml_emitter_t, canonical bool) { - emitter.canonical = canonical -} - -// Set the indentation increment. -func yaml_emitter_set_indent(emitter *yaml_emitter_t, indent int) { - if indent < 2 || indent > 9 { - indent = 2 - } - emitter.best_indent = indent -} - -// Set the preferred line width. -func yaml_emitter_set_width(emitter *yaml_emitter_t, width int) { - if width < 0 { - width = -1 - } - emitter.best_width = width -} - -// Set if unescaped non-ASCII characters are allowed. -func yaml_emitter_set_unicode(emitter *yaml_emitter_t, unicode bool) { - emitter.unicode = unicode -} - -// Set the preferred line break character. -func yaml_emitter_set_break(emitter *yaml_emitter_t, line_break yaml_break_t) { - emitter.line_break = line_break -} - -///* -// * Destroy a token object. -// */ -// -//YAML_DECLARE(void) -//yaml_token_delete(yaml_token_t *token) -//{ -// assert(token); // Non-NULL token object expected. -// -// switch (token.type) -// { -// case YAML_TAG_DIRECTIVE_TOKEN: -// yaml_free(token.data.tag_directive.handle); -// yaml_free(token.data.tag_directive.prefix); -// break; -// -// case YAML_ALIAS_TOKEN: -// yaml_free(token.data.alias.value); -// break; -// -// case YAML_ANCHOR_TOKEN: -// yaml_free(token.data.anchor.value); -// break; -// -// case YAML_TAG_TOKEN: -// yaml_free(token.data.tag.handle); -// yaml_free(token.data.tag.suffix); -// break; -// -// case YAML_SCALAR_TOKEN: -// yaml_free(token.data.scalar.value); -// break; -// -// default: -// break; -// } -// -// memset(token, 0, sizeof(yaml_token_t)); -//} -// -///* -// * Check if a string is a valid UTF-8 sequence. -// * -// * Check 'reader.c' for more details on UTF-8 encoding. -// */ -// -//static int -//yaml_check_utf8(yaml_char_t *start, size_t length) -//{ -// yaml_char_t *end = start+length; -// yaml_char_t *pointer = start; -// -// while (pointer < end) { -// unsigned char octet; -// unsigned int width; -// unsigned int value; -// size_t k; -// -// octet = pointer[0]; -// width = (octet & 0x80) == 0x00 ? 1 : -// (octet & 0xE0) == 0xC0 ? 2 : -// (octet & 0xF0) == 0xE0 ? 3 : -// (octet & 0xF8) == 0xF0 ? 4 : 0; -// value = (octet & 0x80) == 0x00 ? octet & 0x7F : -// (octet & 0xE0) == 0xC0 ? octet & 0x1F : -// (octet & 0xF0) == 0xE0 ? octet & 0x0F : -// (octet & 0xF8) == 0xF0 ? octet & 0x07 : 0; -// if (!width) return 0; -// if (pointer+width > end) return 0; -// for (k = 1; k < width; k ++) { -// octet = pointer[k]; -// if ((octet & 0xC0) != 0x80) return 0; -// value = (value << 6) + (octet & 0x3F); -// } -// if (!((width == 1) || -// (width == 2 && value >= 0x80) || -// (width == 3 && value >= 0x800) || -// (width == 4 && value >= 0x10000))) return 0; -// -// pointer += width; -// } -// -// return 1; -//} -// - -// Create STREAM-START. -func yaml_stream_start_event_initialize(event *yaml_event_t, encoding yaml_encoding_t) { - *event = yaml_event_t{ - typ: yaml_STREAM_START_EVENT, - encoding: encoding, - } -} - -// Create STREAM-END. -func yaml_stream_end_event_initialize(event *yaml_event_t) { - *event = yaml_event_t{ - typ: yaml_STREAM_END_EVENT, - } -} - -// Create DOCUMENT-START. -func yaml_document_start_event_initialize( - event *yaml_event_t, - version_directive *yaml_version_directive_t, - tag_directives []yaml_tag_directive_t, - implicit bool, -) { - *event = yaml_event_t{ - typ: yaml_DOCUMENT_START_EVENT, - version_directive: version_directive, - tag_directives: tag_directives, - implicit: implicit, - } -} - -// Create DOCUMENT-END. -func yaml_document_end_event_initialize(event *yaml_event_t, implicit bool) { - *event = yaml_event_t{ - typ: yaml_DOCUMENT_END_EVENT, - implicit: implicit, - } -} - -// Create ALIAS. -func yaml_alias_event_initialize(event *yaml_event_t, anchor []byte) bool { - *event = yaml_event_t{ - typ: yaml_ALIAS_EVENT, - anchor: anchor, - } - return true -} - -// Create SCALAR. -func yaml_scalar_event_initialize(event *yaml_event_t, anchor, tag, value []byte, plain_implicit, quoted_implicit bool, style yaml_scalar_style_t) bool { - *event = yaml_event_t{ - typ: yaml_SCALAR_EVENT, - anchor: anchor, - tag: tag, - value: value, - implicit: plain_implicit, - quoted_implicit: quoted_implicit, - style: yaml_style_t(style), - } - return true -} - -// Create SEQUENCE-START. -func yaml_sequence_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_sequence_style_t) bool { - *event = yaml_event_t{ - typ: yaml_SEQUENCE_START_EVENT, - anchor: anchor, - tag: tag, - implicit: implicit, - style: yaml_style_t(style), - } - return true -} - -// Create SEQUENCE-END. -func yaml_sequence_end_event_initialize(event *yaml_event_t) bool { - *event = yaml_event_t{ - typ: yaml_SEQUENCE_END_EVENT, - } - return true -} - -// Create MAPPING-START. -func yaml_mapping_start_event_initialize(event *yaml_event_t, anchor, tag []byte, implicit bool, style yaml_mapping_style_t) { - *event = yaml_event_t{ - typ: yaml_MAPPING_START_EVENT, - anchor: anchor, - tag: tag, - implicit: implicit, - style: yaml_style_t(style), - } -} - -// Create MAPPING-END. -func yaml_mapping_end_event_initialize(event *yaml_event_t) { - *event = yaml_event_t{ - typ: yaml_MAPPING_END_EVENT, - } -} - -// Destroy an event object. -func yaml_event_delete(event *yaml_event_t) { - *event = yaml_event_t{} -} - -///* -// * Create a document object. -// */ -// -//YAML_DECLARE(int) -//yaml_document_initialize(document *yaml_document_t, -// version_directive *yaml_version_directive_t, -// tag_directives_start *yaml_tag_directive_t, -// tag_directives_end *yaml_tag_directive_t, -// start_implicit int, end_implicit int) -//{ -// struct { -// error yaml_error_type_t -// } context -// struct { -// start *yaml_node_t -// end *yaml_node_t -// top *yaml_node_t -// } nodes = { NULL, NULL, NULL } -// version_directive_copy *yaml_version_directive_t = NULL -// struct { -// start *yaml_tag_directive_t -// end *yaml_tag_directive_t -// top *yaml_tag_directive_t -// } tag_directives_copy = { NULL, NULL, NULL } -// value yaml_tag_directive_t = { NULL, NULL } -// mark yaml_mark_t = { 0, 0, 0 } -// -// assert(document) // Non-NULL document object is expected. -// assert((tag_directives_start && tag_directives_end) || -// (tag_directives_start == tag_directives_end)) -// // Valid tag directives are expected. -// -// if (!STACK_INIT(&context, nodes, INITIAL_STACK_SIZE)) goto error -// -// if (version_directive) { -// version_directive_copy = yaml_malloc(sizeof(yaml_version_directive_t)) -// if (!version_directive_copy) goto error -// version_directive_copy.major = version_directive.major -// version_directive_copy.minor = version_directive.minor -// } -// -// if (tag_directives_start != tag_directives_end) { -// tag_directive *yaml_tag_directive_t -// if (!STACK_INIT(&context, tag_directives_copy, INITIAL_STACK_SIZE)) -// goto error -// for (tag_directive = tag_directives_start -// tag_directive != tag_directives_end; tag_directive ++) { -// assert(tag_directive.handle) -// assert(tag_directive.prefix) -// if (!yaml_check_utf8(tag_directive.handle, -// strlen((char *)tag_directive.handle))) -// goto error -// if (!yaml_check_utf8(tag_directive.prefix, -// strlen((char *)tag_directive.prefix))) -// goto error -// value.handle = yaml_strdup(tag_directive.handle) -// value.prefix = yaml_strdup(tag_directive.prefix) -// if (!value.handle || !value.prefix) goto error -// if (!PUSH(&context, tag_directives_copy, value)) -// goto error -// value.handle = NULL -// value.prefix = NULL -// } -// } -// -// DOCUMENT_INIT(*document, nodes.start, nodes.end, version_directive_copy, -// tag_directives_copy.start, tag_directives_copy.top, -// start_implicit, end_implicit, mark, mark) -// -// return 1 -// -//error: -// STACK_DEL(&context, nodes) -// yaml_free(version_directive_copy) -// while (!STACK_EMPTY(&context, tag_directives_copy)) { -// value yaml_tag_directive_t = POP(&context, tag_directives_copy) -// yaml_free(value.handle) -// yaml_free(value.prefix) -// } -// STACK_DEL(&context, tag_directives_copy) -// yaml_free(value.handle) -// yaml_free(value.prefix) -// -// return 0 -//} -// -///* -// * Destroy a document object. -// */ -// -//YAML_DECLARE(void) -//yaml_document_delete(document *yaml_document_t) -//{ -// struct { -// error yaml_error_type_t -// } context -// tag_directive *yaml_tag_directive_t -// -// context.error = YAML_NO_ERROR // Eliminate a compiler warning. -// -// assert(document) // Non-NULL document object is expected. -// -// while (!STACK_EMPTY(&context, document.nodes)) { -// node yaml_node_t = POP(&context, document.nodes) -// yaml_free(node.tag) -// switch (node.type) { -// case YAML_SCALAR_NODE: -// yaml_free(node.data.scalar.value) -// break -// case YAML_SEQUENCE_NODE: -// STACK_DEL(&context, node.data.sequence.items) -// break -// case YAML_MAPPING_NODE: -// STACK_DEL(&context, node.data.mapping.pairs) -// break -// default: -// assert(0) // Should not happen. -// } -// } -// STACK_DEL(&context, document.nodes) -// -// yaml_free(document.version_directive) -// for (tag_directive = document.tag_directives.start -// tag_directive != document.tag_directives.end -// tag_directive++) { -// yaml_free(tag_directive.handle) -// yaml_free(tag_directive.prefix) -// } -// yaml_free(document.tag_directives.start) -// -// memset(document, 0, sizeof(yaml_document_t)) -//} -// -///** -// * Get a document node. -// */ -// -//YAML_DECLARE(yaml_node_t *) -//yaml_document_get_node(document *yaml_document_t, index int) -//{ -// assert(document) // Non-NULL document object is expected. -// -// if (index > 0 && document.nodes.start + index <= document.nodes.top) { -// return document.nodes.start + index - 1 -// } -// return NULL -//} -// -///** -// * Get the root object. -// */ -// -//YAML_DECLARE(yaml_node_t *) -//yaml_document_get_root_node(document *yaml_document_t) -//{ -// assert(document) // Non-NULL document object is expected. -// -// if (document.nodes.top != document.nodes.start) { -// return document.nodes.start -// } -// return NULL -//} -// -///* -// * Add a scalar node to a document. -// */ -// -//YAML_DECLARE(int) -//yaml_document_add_scalar(document *yaml_document_t, -// tag *yaml_char_t, value *yaml_char_t, length int, -// style yaml_scalar_style_t) -//{ -// struct { -// error yaml_error_type_t -// } context -// mark yaml_mark_t = { 0, 0, 0 } -// tag_copy *yaml_char_t = NULL -// value_copy *yaml_char_t = NULL -// node yaml_node_t -// -// assert(document) // Non-NULL document object is expected. -// assert(value) // Non-NULL value is expected. -// -// if (!tag) { -// tag = (yaml_char_t *)YAML_DEFAULT_SCALAR_TAG -// } -// -// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error -// tag_copy = yaml_strdup(tag) -// if (!tag_copy) goto error -// -// if (length < 0) { -// length = strlen((char *)value) -// } -// -// if (!yaml_check_utf8(value, length)) goto error -// value_copy = yaml_malloc(length+1) -// if (!value_copy) goto error -// memcpy(value_copy, value, length) -// value_copy[length] = '\0' -// -// SCALAR_NODE_INIT(node, tag_copy, value_copy, length, style, mark, mark) -// if (!PUSH(&context, document.nodes, node)) goto error -// -// return document.nodes.top - document.nodes.start -// -//error: -// yaml_free(tag_copy) -// yaml_free(value_copy) -// -// return 0 -//} -// -///* -// * Add a sequence node to a document. -// */ -// -//YAML_DECLARE(int) -//yaml_document_add_sequence(document *yaml_document_t, -// tag *yaml_char_t, style yaml_sequence_style_t) -//{ -// struct { -// error yaml_error_type_t -// } context -// mark yaml_mark_t = { 0, 0, 0 } -// tag_copy *yaml_char_t = NULL -// struct { -// start *yaml_node_item_t -// end *yaml_node_item_t -// top *yaml_node_item_t -// } items = { NULL, NULL, NULL } -// node yaml_node_t -// -// assert(document) // Non-NULL document object is expected. -// -// if (!tag) { -// tag = (yaml_char_t *)YAML_DEFAULT_SEQUENCE_TAG -// } -// -// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error -// tag_copy = yaml_strdup(tag) -// if (!tag_copy) goto error -// -// if (!STACK_INIT(&context, items, INITIAL_STACK_SIZE)) goto error -// -// SEQUENCE_NODE_INIT(node, tag_copy, items.start, items.end, -// style, mark, mark) -// if (!PUSH(&context, document.nodes, node)) goto error -// -// return document.nodes.top - document.nodes.start -// -//error: -// STACK_DEL(&context, items) -// yaml_free(tag_copy) -// -// return 0 -//} -// -///* -// * Add a mapping node to a document. -// */ -// -//YAML_DECLARE(int) -//yaml_document_add_mapping(document *yaml_document_t, -// tag *yaml_char_t, style yaml_mapping_style_t) -//{ -// struct { -// error yaml_error_type_t -// } context -// mark yaml_mark_t = { 0, 0, 0 } -// tag_copy *yaml_char_t = NULL -// struct { -// start *yaml_node_pair_t -// end *yaml_node_pair_t -// top *yaml_node_pair_t -// } pairs = { NULL, NULL, NULL } -// node yaml_node_t -// -// assert(document) // Non-NULL document object is expected. -// -// if (!tag) { -// tag = (yaml_char_t *)YAML_DEFAULT_MAPPING_TAG -// } -// -// if (!yaml_check_utf8(tag, strlen((char *)tag))) goto error -// tag_copy = yaml_strdup(tag) -// if (!tag_copy) goto error -// -// if (!STACK_INIT(&context, pairs, INITIAL_STACK_SIZE)) goto error -// -// MAPPING_NODE_INIT(node, tag_copy, pairs.start, pairs.end, -// style, mark, mark) -// if (!PUSH(&context, document.nodes, node)) goto error -// -// return document.nodes.top - document.nodes.start -// -//error: -// STACK_DEL(&context, pairs) -// yaml_free(tag_copy) -// -// return 0 -//} -// -///* -// * Append an item to a sequence node. -// */ -// -//YAML_DECLARE(int) -//yaml_document_append_sequence_item(document *yaml_document_t, -// sequence int, item int) -//{ -// struct { -// error yaml_error_type_t -// } context -// -// assert(document) // Non-NULL document is required. -// assert(sequence > 0 -// && document.nodes.start + sequence <= document.nodes.top) -// // Valid sequence id is required. -// assert(document.nodes.start[sequence-1].type == YAML_SEQUENCE_NODE) -// // A sequence node is required. -// assert(item > 0 && document.nodes.start + item <= document.nodes.top) -// // Valid item id is required. -// -// if (!PUSH(&context, -// document.nodes.start[sequence-1].data.sequence.items, item)) -// return 0 -// -// return 1 -//} -// -///* -// * Append a pair of a key and a value to a mapping node. -// */ -// -//YAML_DECLARE(int) -//yaml_document_append_mapping_pair(document *yaml_document_t, -// mapping int, key int, value int) -//{ -// struct { -// error yaml_error_type_t -// } context -// -// pair yaml_node_pair_t -// -// assert(document) // Non-NULL document is required. -// assert(mapping > 0 -// && document.nodes.start + mapping <= document.nodes.top) -// // Valid mapping id is required. -// assert(document.nodes.start[mapping-1].type == YAML_MAPPING_NODE) -// // A mapping node is required. -// assert(key > 0 && document.nodes.start + key <= document.nodes.top) -// // Valid key id is required. -// assert(value > 0 && document.nodes.start + value <= document.nodes.top) -// // Valid value id is required. -// -// pair.key = key -// pair.value = value -// -// if (!PUSH(&context, -// document.nodes.start[mapping-1].data.mapping.pairs, pair)) -// return 0 -// -// return 1 -//} -// -// diff --git a/vendor/go.yaml.in/yaml/v3/decode.go b/vendor/go.yaml.in/yaml/v3/decode.go deleted file mode 100644 index 02e2b17bf..000000000 --- a/vendor/go.yaml.in/yaml/v3/decode.go +++ /dev/null @@ -1,1018 +0,0 @@ -// -// Copyright (c) 2011-2019 Canonical Ltd -// -// 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 yaml - -import ( - "encoding" - "encoding/base64" - "fmt" - "io" - "math" - "reflect" - "strconv" - "time" -) - -// ---------------------------------------------------------------------------- -// Parser, produces a node tree out of a libyaml event stream. - -type parser struct { - parser yaml_parser_t - event yaml_event_t - doc *Node - anchors map[string]*Node - doneInit bool - textless bool -} - -func newParser(b []byte) *parser { - p := parser{} - if !yaml_parser_initialize(&p.parser) { - panic("failed to initialize YAML emitter") - } - if len(b) == 0 { - b = []byte{'\n'} - } - yaml_parser_set_input_string(&p.parser, b) - return &p -} - -func newParserFromReader(r io.Reader) *parser { - p := parser{} - if !yaml_parser_initialize(&p.parser) { - panic("failed to initialize YAML emitter") - } - yaml_parser_set_input_reader(&p.parser, r) - return &p -} - -func (p *parser) init() { - if p.doneInit { - return - } - p.anchors = make(map[string]*Node) - p.expect(yaml_STREAM_START_EVENT) - p.doneInit = true -} - -func (p *parser) destroy() { - if p.event.typ != yaml_NO_EVENT { - yaml_event_delete(&p.event) - } - yaml_parser_delete(&p.parser) -} - -// expect consumes an event from the event stream and -// checks that it's of the expected type. -func (p *parser) expect(e yaml_event_type_t) { - if p.event.typ == yaml_NO_EVENT { - if !yaml_parser_parse(&p.parser, &p.event) { - p.fail() - } - } - if p.event.typ == yaml_STREAM_END_EVENT { - failf("attempted to go past the end of stream; corrupted value?") - } - if p.event.typ != e { - p.parser.problem = fmt.Sprintf("expected %s event but got %s", e, p.event.typ) - p.fail() - } - yaml_event_delete(&p.event) - p.event.typ = yaml_NO_EVENT -} - -// peek peeks at the next event in the event stream, -// puts the results into p.event and returns the event type. -func (p *parser) peek() yaml_event_type_t { - if p.event.typ != yaml_NO_EVENT { - return p.event.typ - } - // It's curious choice from the underlying API to generally return a - // positive result on success, but on this case return true in an error - // scenario. This was the source of bugs in the past (issue #666). - if !yaml_parser_parse(&p.parser, &p.event) || p.parser.error != yaml_NO_ERROR { - p.fail() - } - return p.event.typ -} - -func (p *parser) fail() { - var where string - var line int - if p.parser.context_mark.line != 0 { - line = p.parser.context_mark.line - // Scanner errors don't iterate line before returning error - if p.parser.error == yaml_SCANNER_ERROR { - line++ - } - } else if p.parser.problem_mark.line != 0 { - line = p.parser.problem_mark.line - // Scanner errors don't iterate line before returning error - if p.parser.error == yaml_SCANNER_ERROR { - line++ - } - } - if line != 0 { - where = "line " + strconv.Itoa(line) + ": " - } - var msg string - if len(p.parser.problem) > 0 { - msg = p.parser.problem - } else { - msg = "unknown problem parsing YAML content" - } - failf("%s%s", where, msg) -} - -func (p *parser) anchor(n *Node, anchor []byte) { - if anchor != nil { - n.Anchor = string(anchor) - p.anchors[n.Anchor] = n - } -} - -func (p *parser) parse() *Node { - p.init() - switch p.peek() { - case yaml_SCALAR_EVENT: - return p.scalar() - case yaml_ALIAS_EVENT: - return p.alias() - case yaml_MAPPING_START_EVENT: - return p.mapping() - case yaml_SEQUENCE_START_EVENT: - return p.sequence() - case yaml_DOCUMENT_START_EVENT: - return p.document() - case yaml_STREAM_END_EVENT: - // Happens when attempting to decode an empty buffer. - return nil - case yaml_TAIL_COMMENT_EVENT: - panic("internal error: unexpected tail comment event (please report)") - default: - panic("internal error: attempted to parse unknown event (please report): " + p.event.typ.String()) - } -} - -func (p *parser) node(kind Kind, defaultTag, tag, value string) *Node { - var style Style - if tag != "" && tag != "!" { - tag = shortTag(tag) - style = TaggedStyle - } else if defaultTag != "" { - tag = defaultTag - } else if kind == ScalarNode { - tag, _ = resolve("", value) - } - n := &Node{ - Kind: kind, - Tag: tag, - Value: value, - Style: style, - } - if !p.textless { - n.Line = p.event.start_mark.line + 1 - n.Column = p.event.start_mark.column + 1 - n.HeadComment = string(p.event.head_comment) - n.LineComment = string(p.event.line_comment) - n.FootComment = string(p.event.foot_comment) - } - return n -} - -func (p *parser) parseChild(parent *Node) *Node { - child := p.parse() - parent.Content = append(parent.Content, child) - return child -} - -func (p *parser) document() *Node { - n := p.node(DocumentNode, "", "", "") - p.doc = n - p.expect(yaml_DOCUMENT_START_EVENT) - p.parseChild(n) - if p.peek() == yaml_DOCUMENT_END_EVENT { - n.FootComment = string(p.event.foot_comment) - } - p.expect(yaml_DOCUMENT_END_EVENT) - return n -} - -func (p *parser) alias() *Node { - n := p.node(AliasNode, "", "", string(p.event.anchor)) - n.Alias = p.anchors[n.Value] - if n.Alias == nil { - failf("unknown anchor '%s' referenced", n.Value) - } - p.expect(yaml_ALIAS_EVENT) - return n -} - -func (p *parser) scalar() *Node { - var parsedStyle = p.event.scalar_style() - var nodeStyle Style - switch { - case parsedStyle&yaml_DOUBLE_QUOTED_SCALAR_STYLE != 0: - nodeStyle = DoubleQuotedStyle - case parsedStyle&yaml_SINGLE_QUOTED_SCALAR_STYLE != 0: - nodeStyle = SingleQuotedStyle - case parsedStyle&yaml_LITERAL_SCALAR_STYLE != 0: - nodeStyle = LiteralStyle - case parsedStyle&yaml_FOLDED_SCALAR_STYLE != 0: - nodeStyle = FoldedStyle - } - var nodeValue = string(p.event.value) - var nodeTag = string(p.event.tag) - var defaultTag string - if nodeStyle == 0 { - if nodeValue == "<<" { - defaultTag = mergeTag - } - } else { - defaultTag = strTag - } - n := p.node(ScalarNode, defaultTag, nodeTag, nodeValue) - n.Style |= nodeStyle - p.anchor(n, p.event.anchor) - p.expect(yaml_SCALAR_EVENT) - return n -} - -func (p *parser) sequence() *Node { - n := p.node(SequenceNode, seqTag, string(p.event.tag), "") - if p.event.sequence_style()&yaml_FLOW_SEQUENCE_STYLE != 0 { - n.Style |= FlowStyle - } - p.anchor(n, p.event.anchor) - p.expect(yaml_SEQUENCE_START_EVENT) - for p.peek() != yaml_SEQUENCE_END_EVENT { - p.parseChild(n) - } - n.LineComment = string(p.event.line_comment) - n.FootComment = string(p.event.foot_comment) - p.expect(yaml_SEQUENCE_END_EVENT) - return n -} - -func (p *parser) mapping() *Node { - n := p.node(MappingNode, mapTag, string(p.event.tag), "") - block := true - if p.event.mapping_style()&yaml_FLOW_MAPPING_STYLE != 0 { - block = false - n.Style |= FlowStyle - } - p.anchor(n, p.event.anchor) - p.expect(yaml_MAPPING_START_EVENT) - for p.peek() != yaml_MAPPING_END_EVENT { - k := p.parseChild(n) - if block && k.FootComment != "" { - // Must be a foot comment for the prior value when being dedented. - if len(n.Content) > 2 { - n.Content[len(n.Content)-3].FootComment = k.FootComment - k.FootComment = "" - } - } - v := p.parseChild(n) - if k.FootComment == "" && v.FootComment != "" { - k.FootComment = v.FootComment - v.FootComment = "" - } - if p.peek() == yaml_TAIL_COMMENT_EVENT { - if k.FootComment == "" { - k.FootComment = string(p.event.foot_comment) - } - p.expect(yaml_TAIL_COMMENT_EVENT) - } - } - n.LineComment = string(p.event.line_comment) - n.FootComment = string(p.event.foot_comment) - if n.Style&FlowStyle == 0 && n.FootComment != "" && len(n.Content) > 1 { - n.Content[len(n.Content)-2].FootComment = n.FootComment - n.FootComment = "" - } - p.expect(yaml_MAPPING_END_EVENT) - return n -} - -// ---------------------------------------------------------------------------- -// Decoder, unmarshals a node into a provided value. - -type decoder struct { - doc *Node - aliases map[*Node]bool - terrors []string - - stringMapType reflect.Type - generalMapType reflect.Type - - knownFields bool - uniqueKeys bool - decodeCount int - aliasCount int - aliasDepth int - - mergedFields map[interface{}]bool -} - -var ( - nodeType = reflect.TypeOf(Node{}) - durationType = reflect.TypeOf(time.Duration(0)) - stringMapType = reflect.TypeOf(map[string]interface{}{}) - generalMapType = reflect.TypeOf(map[interface{}]interface{}{}) - ifaceType = generalMapType.Elem() - timeType = reflect.TypeOf(time.Time{}) - ptrTimeType = reflect.TypeOf(&time.Time{}) -) - -func newDecoder() *decoder { - d := &decoder{ - stringMapType: stringMapType, - generalMapType: generalMapType, - uniqueKeys: true, - } - d.aliases = make(map[*Node]bool) - return d -} - -func (d *decoder) terror(n *Node, tag string, out reflect.Value) { - if n.Tag != "" { - tag = n.Tag - } - value := n.Value - if tag != seqTag && tag != mapTag { - if len(value) > 10 { - value = " `" + value[:7] + "...`" - } else { - value = " `" + value + "`" - } - } - d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.Line, shortTag(tag), value, out.Type())) -} - -func (d *decoder) callUnmarshaler(n *Node, u Unmarshaler) (good bool) { - err := u.UnmarshalYAML(n) - if e, ok := err.(*TypeError); ok { - d.terrors = append(d.terrors, e.Errors...) - return false - } - if err != nil { - fail(err) - } - return true -} - -func (d *decoder) callObsoleteUnmarshaler(n *Node, u obsoleteUnmarshaler) (good bool) { - terrlen := len(d.terrors) - err := u.UnmarshalYAML(func(v interface{}) (err error) { - defer handleErr(&err) - d.unmarshal(n, reflect.ValueOf(v)) - if len(d.terrors) > terrlen { - issues := d.terrors[terrlen:] - d.terrors = d.terrors[:terrlen] - return &TypeError{issues} - } - return nil - }) - if e, ok := err.(*TypeError); ok { - d.terrors = append(d.terrors, e.Errors...) - return false - } - if err != nil { - fail(err) - } - return true -} - -// d.prepare initializes and dereferences pointers and calls UnmarshalYAML -// if a value is found to implement it. -// It returns the initialized and dereferenced out value, whether -// unmarshalling was already done by UnmarshalYAML, and if so whether -// its types unmarshalled appropriately. -// -// If n holds a null value, prepare returns before doing anything. -func (d *decoder) prepare(n *Node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) { - if n.ShortTag() == nullTag { - return out, false, false - } - again := true - for again { - again = false - if out.Kind() == reflect.Ptr { - if out.IsNil() { - out.Set(reflect.New(out.Type().Elem())) - } - out = out.Elem() - again = true - } - if out.CanAddr() { - outi := out.Addr().Interface() - if u, ok := outi.(Unmarshaler); ok { - good = d.callUnmarshaler(n, u) - return out, true, good - } - if u, ok := outi.(obsoleteUnmarshaler); ok { - good = d.callObsoleteUnmarshaler(n, u) - return out, true, good - } - } - } - return out, false, false -} - -func (d *decoder) fieldByIndex(n *Node, v reflect.Value, index []int) (field reflect.Value) { - if n.ShortTag() == nullTag { - return reflect.Value{} - } - for _, num := range index { - for { - if v.Kind() == reflect.Ptr { - if v.IsNil() { - v.Set(reflect.New(v.Type().Elem())) - } - v = v.Elem() - continue - } - break - } - v = v.Field(num) - } - return v -} - -const ( - // 400,000 decode operations is ~500kb of dense object declarations, or - // ~5kb of dense object declarations with 10000% alias expansion - alias_ratio_range_low = 400000 - - // 4,000,000 decode operations is ~5MB of dense object declarations, or - // ~4.5MB of dense object declarations with 10% alias expansion - alias_ratio_range_high = 4000000 - - // alias_ratio_range is the range over which we scale allowed alias ratios - alias_ratio_range = float64(alias_ratio_range_high - alias_ratio_range_low) -) - -func allowedAliasRatio(decodeCount int) float64 { - switch { - case decodeCount <= alias_ratio_range_low: - // allow 99% to come from alias expansion for small-to-medium documents - return 0.99 - case decodeCount >= alias_ratio_range_high: - // allow 10% to come from alias expansion for very large documents - return 0.10 - default: - // scale smoothly from 99% down to 10% over the range. - // this maps to 396,000 - 400,000 allowed alias-driven decodes over the range. - // 400,000 decode operations is ~100MB of allocations in worst-case scenarios (single-item maps). - return 0.99 - 0.89*(float64(decodeCount-alias_ratio_range_low)/alias_ratio_range) - } -} - -func (d *decoder) unmarshal(n *Node, out reflect.Value) (good bool) { - d.decodeCount++ - if d.aliasDepth > 0 { - d.aliasCount++ - } - if d.aliasCount > 100 && d.decodeCount > 1000 && float64(d.aliasCount)/float64(d.decodeCount) > allowedAliasRatio(d.decodeCount) { - failf("document contains excessive aliasing") - } - if out.Type() == nodeType { - out.Set(reflect.ValueOf(n).Elem()) - return true - } - switch n.Kind { - case DocumentNode: - return d.document(n, out) - case AliasNode: - return d.alias(n, out) - } - out, unmarshaled, good := d.prepare(n, out) - if unmarshaled { - return good - } - switch n.Kind { - case ScalarNode: - good = d.scalar(n, out) - case MappingNode: - good = d.mapping(n, out) - case SequenceNode: - good = d.sequence(n, out) - case 0: - if n.IsZero() { - return d.null(out) - } - fallthrough - default: - failf("cannot decode node with unknown kind %d", n.Kind) - } - return good -} - -func (d *decoder) document(n *Node, out reflect.Value) (good bool) { - if len(n.Content) == 1 { - d.doc = n - d.unmarshal(n.Content[0], out) - return true - } - return false -} - -func (d *decoder) alias(n *Node, out reflect.Value) (good bool) { - if d.aliases[n] { - // TODO this could actually be allowed in some circumstances. - failf("anchor '%s' value contains itself", n.Value) - } - d.aliases[n] = true - d.aliasDepth++ - good = d.unmarshal(n.Alias, out) - d.aliasDepth-- - delete(d.aliases, n) - return good -} - -var zeroValue reflect.Value - -func resetMap(out reflect.Value) { - for _, k := range out.MapKeys() { - out.SetMapIndex(k, zeroValue) - } -} - -func (d *decoder) null(out reflect.Value) bool { - if out.CanAddr() { - switch out.Kind() { - case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice: - out.Set(reflect.Zero(out.Type())) - return true - } - } - return false -} - -func (d *decoder) scalar(n *Node, out reflect.Value) bool { - var tag string - var resolved interface{} - if n.indicatedString() { - tag = strTag - resolved = n.Value - } else { - tag, resolved = resolve(n.Tag, n.Value) - if tag == binaryTag { - data, err := base64.StdEncoding.DecodeString(resolved.(string)) - if err != nil { - failf("!!binary value contains invalid base64 data") - } - resolved = string(data) - } - } - if resolved == nil { - return d.null(out) - } - if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() { - // We've resolved to exactly the type we want, so use that. - out.Set(resolvedv) - return true - } - // Perhaps we can use the value as a TextUnmarshaler to - // set its value. - if out.CanAddr() { - u, ok := out.Addr().Interface().(encoding.TextUnmarshaler) - if ok { - var text []byte - if tag == binaryTag { - text = []byte(resolved.(string)) - } else { - // We let any value be unmarshaled into TextUnmarshaler. - // That might be more lax than we'd like, but the - // TextUnmarshaler itself should bowl out any dubious values. - text = []byte(n.Value) - } - err := u.UnmarshalText(text) - if err != nil { - fail(err) - } - return true - } - } - switch out.Kind() { - case reflect.String: - if tag == binaryTag { - out.SetString(resolved.(string)) - return true - } - out.SetString(n.Value) - return true - case reflect.Interface: - out.Set(reflect.ValueOf(resolved)) - return true - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - // This used to work in v2, but it's very unfriendly. - isDuration := out.Type() == durationType - - switch resolved := resolved.(type) { - case int: - if !isDuration && !out.OverflowInt(int64(resolved)) { - out.SetInt(int64(resolved)) - return true - } - case int64: - if !isDuration && !out.OverflowInt(resolved) { - out.SetInt(resolved) - return true - } - case uint64: - if !isDuration && resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { - out.SetInt(int64(resolved)) - return true - } - case float64: - if !isDuration && resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) { - out.SetInt(int64(resolved)) - return true - } - case string: - if out.Type() == durationType { - d, err := time.ParseDuration(resolved) - if err == nil { - out.SetInt(int64(d)) - return true - } - } - } - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - switch resolved := resolved.(type) { - case int: - if resolved >= 0 && !out.OverflowUint(uint64(resolved)) { - out.SetUint(uint64(resolved)) - return true - } - case int64: - if resolved >= 0 && !out.OverflowUint(uint64(resolved)) { - out.SetUint(uint64(resolved)) - return true - } - case uint64: - if !out.OverflowUint(uint64(resolved)) { - out.SetUint(uint64(resolved)) - return true - } - case float64: - if resolved <= math.MaxUint64 && !out.OverflowUint(uint64(resolved)) { - out.SetUint(uint64(resolved)) - return true - } - } - case reflect.Bool: - switch resolved := resolved.(type) { - case bool: - out.SetBool(resolved) - return true - case string: - // This offers some compatibility with the 1.1 spec (https://yaml.org/type/bool.html). - // It only works if explicitly attempting to unmarshal into a typed bool value. - switch resolved { - case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON": - out.SetBool(true) - return true - case "n", "N", "no", "No", "NO", "off", "Off", "OFF": - out.SetBool(false) - return true - } - } - case reflect.Float32, reflect.Float64: - switch resolved := resolved.(type) { - case int: - out.SetFloat(float64(resolved)) - return true - case int64: - out.SetFloat(float64(resolved)) - return true - case uint64: - out.SetFloat(float64(resolved)) - return true - case float64: - out.SetFloat(resolved) - return true - } - case reflect.Struct: - if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() { - out.Set(resolvedv) - return true - } - case reflect.Ptr: - panic("yaml internal error: please report the issue") - } - d.terror(n, tag, out) - return false -} - -func settableValueOf(i interface{}) reflect.Value { - v := reflect.ValueOf(i) - sv := reflect.New(v.Type()).Elem() - sv.Set(v) - return sv -} - -func (d *decoder) sequence(n *Node, out reflect.Value) (good bool) { - l := len(n.Content) - - var iface reflect.Value - switch out.Kind() { - case reflect.Slice: - out.Set(reflect.MakeSlice(out.Type(), l, l)) - case reflect.Array: - if l != out.Len() { - failf("invalid array: want %d elements but got %d", out.Len(), l) - } - case reflect.Interface: - // No type hints. Will have to use a generic sequence. - iface = out - out = settableValueOf(make([]interface{}, l)) - default: - d.terror(n, seqTag, out) - return false - } - et := out.Type().Elem() - - j := 0 - for i := 0; i < l; i++ { - e := reflect.New(et).Elem() - if ok := d.unmarshal(n.Content[i], e); ok { - out.Index(j).Set(e) - j++ - } - } - if out.Kind() != reflect.Array { - out.Set(out.Slice(0, j)) - } - if iface.IsValid() { - iface.Set(out) - } - return true -} - -func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) { - l := len(n.Content) - if d.uniqueKeys { - nerrs := len(d.terrors) - for i := 0; i < l; i += 2 { - ni := n.Content[i] - for j := i + 2; j < l; j += 2 { - nj := n.Content[j] - if ni.Kind == nj.Kind && ni.Value == nj.Value { - d.terrors = append(d.terrors, fmt.Sprintf("line %d: mapping key %#v already defined at line %d", nj.Line, nj.Value, ni.Line)) - } - } - } - if len(d.terrors) > nerrs { - return false - } - } - switch out.Kind() { - case reflect.Struct: - return d.mappingStruct(n, out) - case reflect.Map: - // okay - case reflect.Interface: - iface := out - if isStringMap(n) { - out = reflect.MakeMap(d.stringMapType) - } else { - out = reflect.MakeMap(d.generalMapType) - } - iface.Set(out) - default: - d.terror(n, mapTag, out) - return false - } - - outt := out.Type() - kt := outt.Key() - et := outt.Elem() - - stringMapType := d.stringMapType - generalMapType := d.generalMapType - if outt.Elem() == ifaceType { - if outt.Key().Kind() == reflect.String { - d.stringMapType = outt - } else if outt.Key() == ifaceType { - d.generalMapType = outt - } - } - - mergedFields := d.mergedFields - d.mergedFields = nil - - var mergeNode *Node - - mapIsNew := false - if out.IsNil() { - out.Set(reflect.MakeMap(outt)) - mapIsNew = true - } - for i := 0; i < l; i += 2 { - if isMerge(n.Content[i]) { - mergeNode = n.Content[i+1] - continue - } - k := reflect.New(kt).Elem() - if d.unmarshal(n.Content[i], k) { - if mergedFields != nil { - ki := k.Interface() - if d.getPossiblyUnhashableKey(mergedFields, ki) { - continue - } - d.setPossiblyUnhashableKey(mergedFields, ki, true) - } - kkind := k.Kind() - if kkind == reflect.Interface { - kkind = k.Elem().Kind() - } - if kkind == reflect.Map || kkind == reflect.Slice { - failf("invalid map key: %#v", k.Interface()) - } - e := reflect.New(et).Elem() - if d.unmarshal(n.Content[i+1], e) || n.Content[i+1].ShortTag() == nullTag && (mapIsNew || !out.MapIndex(k).IsValid()) { - out.SetMapIndex(k, e) - } - } - } - - d.mergedFields = mergedFields - if mergeNode != nil { - d.merge(n, mergeNode, out) - } - - d.stringMapType = stringMapType - d.generalMapType = generalMapType - return true -} - -func isStringMap(n *Node) bool { - if n.Kind != MappingNode { - return false - } - l := len(n.Content) - for i := 0; i < l; i += 2 { - shortTag := n.Content[i].ShortTag() - if shortTag != strTag && shortTag != mergeTag { - return false - } - } - return true -} - -func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) { - sinfo, err := getStructInfo(out.Type()) - if err != nil { - panic(err) - } - - var inlineMap reflect.Value - var elemType reflect.Type - if sinfo.InlineMap != -1 { - inlineMap = out.Field(sinfo.InlineMap) - elemType = inlineMap.Type().Elem() - } - - for _, index := range sinfo.InlineUnmarshalers { - field := d.fieldByIndex(n, out, index) - d.prepare(n, field) - } - - mergedFields := d.mergedFields - d.mergedFields = nil - var mergeNode *Node - var doneFields []bool - if d.uniqueKeys { - doneFields = make([]bool, len(sinfo.FieldsList)) - } - name := settableValueOf("") - l := len(n.Content) - for i := 0; i < l; i += 2 { - ni := n.Content[i] - if isMerge(ni) { - mergeNode = n.Content[i+1] - continue - } - if !d.unmarshal(ni, name) { - continue - } - sname := name.String() - if mergedFields != nil { - if mergedFields[sname] { - continue - } - mergedFields[sname] = true - } - if info, ok := sinfo.FieldsMap[sname]; ok { - if d.uniqueKeys { - if doneFields[info.Id] { - d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.Line, name.String(), out.Type())) - continue - } - doneFields[info.Id] = true - } - var field reflect.Value - if info.Inline == nil { - field = out.Field(info.Num) - } else { - field = d.fieldByIndex(n, out, info.Inline) - } - d.unmarshal(n.Content[i+1], field) - } else if sinfo.InlineMap != -1 { - if inlineMap.IsNil() { - inlineMap.Set(reflect.MakeMap(inlineMap.Type())) - } - value := reflect.New(elemType).Elem() - d.unmarshal(n.Content[i+1], value) - inlineMap.SetMapIndex(name, value) - } else if d.knownFields { - d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.Line, name.String(), out.Type())) - } - } - - d.mergedFields = mergedFields - if mergeNode != nil { - d.merge(n, mergeNode, out) - } - return true -} - -func failWantMap() { - failf("map merge requires map or sequence of maps as the value") -} - -func (d *decoder) setPossiblyUnhashableKey(m map[interface{}]bool, key interface{}, value bool) { - defer func() { - if err := recover(); err != nil { - failf("%v", err) - } - }() - m[key] = value -} - -func (d *decoder) getPossiblyUnhashableKey(m map[interface{}]bool, key interface{}) bool { - defer func() { - if err := recover(); err != nil { - failf("%v", err) - } - }() - return m[key] -} - -func (d *decoder) merge(parent *Node, merge *Node, out reflect.Value) { - mergedFields := d.mergedFields - if mergedFields == nil { - d.mergedFields = make(map[interface{}]bool) - for i := 0; i < len(parent.Content); i += 2 { - k := reflect.New(ifaceType).Elem() - if d.unmarshal(parent.Content[i], k) { - d.setPossiblyUnhashableKey(d.mergedFields, k.Interface(), true) - } - } - } - - switch merge.Kind { - case MappingNode: - d.unmarshal(merge, out) - case AliasNode: - if merge.Alias != nil && merge.Alias.Kind != MappingNode { - failWantMap() - } - d.unmarshal(merge, out) - case SequenceNode: - for i := 0; i < len(merge.Content); i++ { - ni := merge.Content[i] - if ni.Kind == AliasNode { - if ni.Alias != nil && ni.Alias.Kind != MappingNode { - failWantMap() - } - } else if ni.Kind != MappingNode { - failWantMap() - } - d.unmarshal(ni, out) - } - default: - failWantMap() - } - - d.mergedFields = mergedFields -} - -func isMerge(n *Node) bool { - return n.Kind == ScalarNode && n.Value == "<<" && (n.Tag == "" || n.Tag == "!" || shortTag(n.Tag) == mergeTag) -} diff --git a/vendor/go.yaml.in/yaml/v3/emitterc.go b/vendor/go.yaml.in/yaml/v3/emitterc.go deleted file mode 100644 index ab4e03ba7..000000000 --- a/vendor/go.yaml.in/yaml/v3/emitterc.go +++ /dev/null @@ -1,2054 +0,0 @@ -// -// Copyright (c) 2011-2019 Canonical Ltd -// Copyright (c) 2006-2010 Kirill Simonov -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package yaml - -import ( - "bytes" - "fmt" -) - -// Flush the buffer if needed. -func flush(emitter *yaml_emitter_t) bool { - if emitter.buffer_pos+5 >= len(emitter.buffer) { - return yaml_emitter_flush(emitter) - } - return true -} - -// Put a character to the output buffer. -func put(emitter *yaml_emitter_t, value byte) bool { - if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) { - return false - } - emitter.buffer[emitter.buffer_pos] = value - emitter.buffer_pos++ - emitter.column++ - return true -} - -// Put a line break to the output buffer. -func put_break(emitter *yaml_emitter_t) bool { - if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) { - return false - } - switch emitter.line_break { - case yaml_CR_BREAK: - emitter.buffer[emitter.buffer_pos] = '\r' - emitter.buffer_pos += 1 - case yaml_LN_BREAK: - emitter.buffer[emitter.buffer_pos] = '\n' - emitter.buffer_pos += 1 - case yaml_CRLN_BREAK: - emitter.buffer[emitter.buffer_pos+0] = '\r' - emitter.buffer[emitter.buffer_pos+1] = '\n' - emitter.buffer_pos += 2 - default: - panic("unknown line break setting") - } - if emitter.column == 0 { - emitter.space_above = true - } - emitter.column = 0 - emitter.line++ - // [Go] Do this here and below and drop from everywhere else (see commented lines). - emitter.indention = true - return true -} - -// Copy a character from a string into buffer. -func write(emitter *yaml_emitter_t, s []byte, i *int) bool { - if emitter.buffer_pos+5 >= len(emitter.buffer) && !yaml_emitter_flush(emitter) { - return false - } - p := emitter.buffer_pos - w := width(s[*i]) - switch w { - case 4: - emitter.buffer[p+3] = s[*i+3] - fallthrough - case 3: - emitter.buffer[p+2] = s[*i+2] - fallthrough - case 2: - emitter.buffer[p+1] = s[*i+1] - fallthrough - case 1: - emitter.buffer[p+0] = s[*i+0] - default: - panic("unknown character width") - } - emitter.column++ - emitter.buffer_pos += w - *i += w - return true -} - -// Write a whole string into buffer. -func write_all(emitter *yaml_emitter_t, s []byte) bool { - for i := 0; i < len(s); { - if !write(emitter, s, &i) { - return false - } - } - return true -} - -// Copy a line break character from a string into buffer. -func write_break(emitter *yaml_emitter_t, s []byte, i *int) bool { - if s[*i] == '\n' { - if !put_break(emitter) { - return false - } - *i++ - } else { - if !write(emitter, s, i) { - return false - } - if emitter.column == 0 { - emitter.space_above = true - } - emitter.column = 0 - emitter.line++ - // [Go] Do this here and above and drop from everywhere else (see commented lines). - emitter.indention = true - } - return true -} - -// Set an emitter error and return false. -func yaml_emitter_set_emitter_error(emitter *yaml_emitter_t, problem string) bool { - emitter.error = yaml_EMITTER_ERROR - emitter.problem = problem - return false -} - -// Emit an event. -func yaml_emitter_emit(emitter *yaml_emitter_t, event *yaml_event_t) bool { - emitter.events = append(emitter.events, *event) - for !yaml_emitter_need_more_events(emitter) { - event := &emitter.events[emitter.events_head] - if !yaml_emitter_analyze_event(emitter, event) { - return false - } - if !yaml_emitter_state_machine(emitter, event) { - return false - } - yaml_event_delete(event) - emitter.events_head++ - } - return true -} - -// Check if we need to accumulate more events before emitting. -// -// We accumulate extra -// - 1 event for DOCUMENT-START -// - 2 events for SEQUENCE-START -// - 3 events for MAPPING-START -func yaml_emitter_need_more_events(emitter *yaml_emitter_t) bool { - if emitter.events_head == len(emitter.events) { - return true - } - var accumulate int - switch emitter.events[emitter.events_head].typ { - case yaml_DOCUMENT_START_EVENT: - accumulate = 1 - break - case yaml_SEQUENCE_START_EVENT: - accumulate = 2 - break - case yaml_MAPPING_START_EVENT: - accumulate = 3 - break - default: - return false - } - if len(emitter.events)-emitter.events_head > accumulate { - return false - } - var level int - for i := emitter.events_head; i < len(emitter.events); i++ { - switch emitter.events[i].typ { - case yaml_STREAM_START_EVENT, yaml_DOCUMENT_START_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT: - level++ - case yaml_STREAM_END_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_END_EVENT, yaml_MAPPING_END_EVENT: - level-- - } - if level == 0 { - return false - } - } - return true -} - -// Append a directive to the directives stack. -func yaml_emitter_append_tag_directive(emitter *yaml_emitter_t, value *yaml_tag_directive_t, allow_duplicates bool) bool { - for i := 0; i < len(emitter.tag_directives); i++ { - if bytes.Equal(value.handle, emitter.tag_directives[i].handle) { - if allow_duplicates { - return true - } - return yaml_emitter_set_emitter_error(emitter, "duplicate %TAG directive") - } - } - - // [Go] Do we actually need to copy this given garbage collection - // and the lack of deallocating destructors? - tag_copy := yaml_tag_directive_t{ - handle: make([]byte, len(value.handle)), - prefix: make([]byte, len(value.prefix)), - } - copy(tag_copy.handle, value.handle) - copy(tag_copy.prefix, value.prefix) - emitter.tag_directives = append(emitter.tag_directives, tag_copy) - return true -} - -// Increase the indentation level. -func yaml_emitter_increase_indent_compact(emitter *yaml_emitter_t, flow, indentless bool, compact_seq bool) bool { - emitter.indents = append(emitter.indents, emitter.indent) - if emitter.indent < 0 { - if flow { - emitter.indent = emitter.best_indent - } else { - emitter.indent = 0 - } - } else if !indentless { - // [Go] This was changed so that indentations are more regular. - if emitter.states[len(emitter.states)-1] == yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE { - // The first indent inside a sequence will just skip the "- " indicator. - emitter.indent += 2 - } else { - // Everything else aligns to the chosen indentation. - emitter.indent = emitter.best_indent * ((emitter.indent + emitter.best_indent) / emitter.best_indent) - if compact_seq { - // The value compact_seq passed in is almost always set to `false` when this function is called, - // except when we are dealing with sequence nodes. So this gets triggered to subtract 2 only when we - // are increasing the indent to account for sequence nodes, which will be correct because we need to - // subtract 2 to account for the - at the beginning of the sequence node. - emitter.indent = emitter.indent - 2 - } - } - } - return true -} - -// State dispatcher. -func yaml_emitter_state_machine(emitter *yaml_emitter_t, event *yaml_event_t) bool { - switch emitter.state { - default: - case yaml_EMIT_STREAM_START_STATE: - return yaml_emitter_emit_stream_start(emitter, event) - - case yaml_EMIT_FIRST_DOCUMENT_START_STATE: - return yaml_emitter_emit_document_start(emitter, event, true) - - case yaml_EMIT_DOCUMENT_START_STATE: - return yaml_emitter_emit_document_start(emitter, event, false) - - case yaml_EMIT_DOCUMENT_CONTENT_STATE: - return yaml_emitter_emit_document_content(emitter, event) - - case yaml_EMIT_DOCUMENT_END_STATE: - return yaml_emitter_emit_document_end(emitter, event) - - case yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE: - return yaml_emitter_emit_flow_sequence_item(emitter, event, true, false) - - case yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE: - return yaml_emitter_emit_flow_sequence_item(emitter, event, false, true) - - case yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE: - return yaml_emitter_emit_flow_sequence_item(emitter, event, false, false) - - case yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE: - return yaml_emitter_emit_flow_mapping_key(emitter, event, true, false) - - case yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE: - return yaml_emitter_emit_flow_mapping_key(emitter, event, false, true) - - case yaml_EMIT_FLOW_MAPPING_KEY_STATE: - return yaml_emitter_emit_flow_mapping_key(emitter, event, false, false) - - case yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE: - return yaml_emitter_emit_flow_mapping_value(emitter, event, true) - - case yaml_EMIT_FLOW_MAPPING_VALUE_STATE: - return yaml_emitter_emit_flow_mapping_value(emitter, event, false) - - case yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE: - return yaml_emitter_emit_block_sequence_item(emitter, event, true) - - case yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE: - return yaml_emitter_emit_block_sequence_item(emitter, event, false) - - case yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE: - return yaml_emitter_emit_block_mapping_key(emitter, event, true) - - case yaml_EMIT_BLOCK_MAPPING_KEY_STATE: - return yaml_emitter_emit_block_mapping_key(emitter, event, false) - - case yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE: - return yaml_emitter_emit_block_mapping_value(emitter, event, true) - - case yaml_EMIT_BLOCK_MAPPING_VALUE_STATE: - return yaml_emitter_emit_block_mapping_value(emitter, event, false) - - case yaml_EMIT_END_STATE: - return yaml_emitter_set_emitter_error(emitter, "expected nothing after STREAM-END") - } - panic("invalid emitter state") -} - -// Expect STREAM-START. -func yaml_emitter_emit_stream_start(emitter *yaml_emitter_t, event *yaml_event_t) bool { - if event.typ != yaml_STREAM_START_EVENT { - return yaml_emitter_set_emitter_error(emitter, "expected STREAM-START") - } - if emitter.encoding == yaml_ANY_ENCODING { - emitter.encoding = event.encoding - if emitter.encoding == yaml_ANY_ENCODING { - emitter.encoding = yaml_UTF8_ENCODING - } - } - if emitter.best_indent < 2 || emitter.best_indent > 9 { - emitter.best_indent = 2 - } - if emitter.best_width >= 0 && emitter.best_width <= emitter.best_indent*2 { - emitter.best_width = 80 - } - if emitter.best_width < 0 { - emitter.best_width = 1<<31 - 1 - } - if emitter.line_break == yaml_ANY_BREAK { - emitter.line_break = yaml_LN_BREAK - } - - emitter.indent = -1 - emitter.line = 0 - emitter.column = 0 - emitter.whitespace = true - emitter.indention = true - emitter.space_above = true - emitter.foot_indent = -1 - - if emitter.encoding != yaml_UTF8_ENCODING { - if !yaml_emitter_write_bom(emitter) { - return false - } - } - emitter.state = yaml_EMIT_FIRST_DOCUMENT_START_STATE - return true -} - -// Expect DOCUMENT-START or STREAM-END. -func yaml_emitter_emit_document_start(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { - - if event.typ == yaml_DOCUMENT_START_EVENT { - - if event.version_directive != nil { - if !yaml_emitter_analyze_version_directive(emitter, event.version_directive) { - return false - } - } - - for i := 0; i < len(event.tag_directives); i++ { - tag_directive := &event.tag_directives[i] - if !yaml_emitter_analyze_tag_directive(emitter, tag_directive) { - return false - } - if !yaml_emitter_append_tag_directive(emitter, tag_directive, false) { - return false - } - } - - for i := 0; i < len(default_tag_directives); i++ { - tag_directive := &default_tag_directives[i] - if !yaml_emitter_append_tag_directive(emitter, tag_directive, true) { - return false - } - } - - implicit := event.implicit - if !first || emitter.canonical { - implicit = false - } - - if emitter.open_ended && (event.version_directive != nil || len(event.tag_directives) > 0) { - if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) { - return false - } - if !yaml_emitter_write_indent(emitter) { - return false - } - } - - if event.version_directive != nil { - implicit = false - if !yaml_emitter_write_indicator(emitter, []byte("%YAML"), true, false, false) { - return false - } - if !yaml_emitter_write_indicator(emitter, []byte("1.1"), true, false, false) { - return false - } - if !yaml_emitter_write_indent(emitter) { - return false - } - } - - if len(event.tag_directives) > 0 { - implicit = false - for i := 0; i < len(event.tag_directives); i++ { - tag_directive := &event.tag_directives[i] - if !yaml_emitter_write_indicator(emitter, []byte("%TAG"), true, false, false) { - return false - } - if !yaml_emitter_write_tag_handle(emitter, tag_directive.handle) { - return false - } - if !yaml_emitter_write_tag_content(emitter, tag_directive.prefix, true) { - return false - } - if !yaml_emitter_write_indent(emitter) { - return false - } - } - } - - if yaml_emitter_check_empty_document(emitter) { - implicit = false - } - if !implicit { - if !yaml_emitter_write_indent(emitter) { - return false - } - if !yaml_emitter_write_indicator(emitter, []byte("---"), true, false, false) { - return false - } - if emitter.canonical || true { - if !yaml_emitter_write_indent(emitter) { - return false - } - } - } - - if len(emitter.head_comment) > 0 { - if !yaml_emitter_process_head_comment(emitter) { - return false - } - if !put_break(emitter) { - return false - } - } - - emitter.state = yaml_EMIT_DOCUMENT_CONTENT_STATE - return true - } - - if event.typ == yaml_STREAM_END_EVENT { - if emitter.open_ended { - if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) { - return false - } - if !yaml_emitter_write_indent(emitter) { - return false - } - } - if !yaml_emitter_flush(emitter) { - return false - } - emitter.state = yaml_EMIT_END_STATE - return true - } - - return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-START or STREAM-END") -} - -// yaml_emitter_increase_indent preserves the original signature and delegates to -// yaml_emitter_increase_indent_compact without compact-sequence indentation -func yaml_emitter_increase_indent(emitter *yaml_emitter_t, flow, indentless bool) bool { - return yaml_emitter_increase_indent_compact(emitter, flow, indentless, false) -} - -// yaml_emitter_process_line_comment preserves the original signature and delegates to -// yaml_emitter_process_line_comment_linebreak passing false for linebreak -func yaml_emitter_process_line_comment(emitter *yaml_emitter_t) bool { - return yaml_emitter_process_line_comment_linebreak(emitter, false) -} - -// Expect the root node. -func yaml_emitter_emit_document_content(emitter *yaml_emitter_t, event *yaml_event_t) bool { - emitter.states = append(emitter.states, yaml_EMIT_DOCUMENT_END_STATE) - - if !yaml_emitter_process_head_comment(emitter) { - return false - } - if !yaml_emitter_emit_node(emitter, event, true, false, false, false) { - return false - } - if !yaml_emitter_process_line_comment(emitter) { - return false - } - if !yaml_emitter_process_foot_comment(emitter) { - return false - } - return true -} - -// Expect DOCUMENT-END. -func yaml_emitter_emit_document_end(emitter *yaml_emitter_t, event *yaml_event_t) bool { - if event.typ != yaml_DOCUMENT_END_EVENT { - return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-END") - } - // [Go] Force document foot separation. - emitter.foot_indent = 0 - if !yaml_emitter_process_foot_comment(emitter) { - return false - } - emitter.foot_indent = -1 - if !yaml_emitter_write_indent(emitter) { - return false - } - if !event.implicit { - // [Go] Allocate the slice elsewhere. - if !yaml_emitter_write_indicator(emitter, []byte("..."), true, false, false) { - return false - } - if !yaml_emitter_write_indent(emitter) { - return false - } - } - if !yaml_emitter_flush(emitter) { - return false - } - emitter.state = yaml_EMIT_DOCUMENT_START_STATE - emitter.tag_directives = emitter.tag_directives[:0] - return true -} - -// Expect a flow item node. -func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first, trail bool) bool { - if first { - if !yaml_emitter_write_indicator(emitter, []byte{'['}, true, true, false) { - return false - } - if !yaml_emitter_increase_indent(emitter, true, false) { - return false - } - emitter.flow_level++ - } - - if event.typ == yaml_SEQUENCE_END_EVENT { - if emitter.canonical && !first && !trail { - if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { - return false - } - } - emitter.flow_level-- - emitter.indent = emitter.indents[len(emitter.indents)-1] - emitter.indents = emitter.indents[:len(emitter.indents)-1] - if emitter.column == 0 || emitter.canonical && !first { - if !yaml_emitter_write_indent(emitter) { - return false - } - } - if !yaml_emitter_write_indicator(emitter, []byte{']'}, false, false, false) { - return false - } - if !yaml_emitter_process_line_comment(emitter) { - return false - } - if !yaml_emitter_process_foot_comment(emitter) { - return false - } - emitter.state = emitter.states[len(emitter.states)-1] - emitter.states = emitter.states[:len(emitter.states)-1] - - return true - } - - if !first && !trail { - if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { - return false - } - } - - if !yaml_emitter_process_head_comment(emitter) { - return false - } - if emitter.column == 0 { - if !yaml_emitter_write_indent(emitter) { - return false - } - } - - if emitter.canonical || emitter.column > emitter.best_width { - if !yaml_emitter_write_indent(emitter) { - return false - } - } - if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 { - emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE) - } else { - emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE) - } - if !yaml_emitter_emit_node(emitter, event, false, true, false, false) { - return false - } - if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 { - if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { - return false - } - } - if !yaml_emitter_process_line_comment(emitter) { - return false - } - if !yaml_emitter_process_foot_comment(emitter) { - return false - } - return true -} - -// Expect a flow key node. -func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first, trail bool) bool { - if first { - if !yaml_emitter_write_indicator(emitter, []byte{'{'}, true, true, false) { - return false - } - if !yaml_emitter_increase_indent(emitter, true, false) { - return false - } - emitter.flow_level++ - } - - if event.typ == yaml_MAPPING_END_EVENT { - if (emitter.canonical || len(emitter.head_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0) && !first && !trail { - if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { - return false - } - } - if !yaml_emitter_process_head_comment(emitter) { - return false - } - emitter.flow_level-- - emitter.indent = emitter.indents[len(emitter.indents)-1] - emitter.indents = emitter.indents[:len(emitter.indents)-1] - if emitter.canonical && !first { - if !yaml_emitter_write_indent(emitter) { - return false - } - } - if !yaml_emitter_write_indicator(emitter, []byte{'}'}, false, false, false) { - return false - } - if !yaml_emitter_process_line_comment(emitter) { - return false - } - if !yaml_emitter_process_foot_comment(emitter) { - return false - } - emitter.state = emitter.states[len(emitter.states)-1] - emitter.states = emitter.states[:len(emitter.states)-1] - return true - } - - if !first && !trail { - if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { - return false - } - } - - if !yaml_emitter_process_head_comment(emitter) { - return false - } - - if emitter.column == 0 { - if !yaml_emitter_write_indent(emitter) { - return false - } - } - - if emitter.canonical || emitter.column > emitter.best_width { - if !yaml_emitter_write_indent(emitter) { - return false - } - } - - if !emitter.canonical && yaml_emitter_check_simple_key(emitter) { - emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE) - return yaml_emitter_emit_node(emitter, event, false, false, true, true) - } - if !yaml_emitter_write_indicator(emitter, []byte{'?'}, true, false, false) { - return false - } - emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_VALUE_STATE) - return yaml_emitter_emit_node(emitter, event, false, false, true, false) -} - -// Expect a flow value node. -func yaml_emitter_emit_flow_mapping_value(emitter *yaml_emitter_t, event *yaml_event_t, simple bool) bool { - if simple { - if !yaml_emitter_write_indicator(emitter, []byte{':'}, false, false, false) { - return false - } - } else { - if emitter.canonical || emitter.column > emitter.best_width { - if !yaml_emitter_write_indent(emitter) { - return false - } - } - if !yaml_emitter_write_indicator(emitter, []byte{':'}, true, false, false) { - return false - } - } - if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 { - emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE) - } else { - emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_KEY_STATE) - } - if !yaml_emitter_emit_node(emitter, event, false, false, true, false) { - return false - } - if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 { - if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) { - return false - } - } - if !yaml_emitter_process_line_comment(emitter) { - return false - } - if !yaml_emitter_process_foot_comment(emitter) { - return false - } - return true -} - -// Expect a block item node. -func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { - if first { - // emitter.mapping context tells us if we are currently in a mapping context. - // emiiter.column tells us which column we are in in the yaml output. 0 is the first char of the column. - // emitter.indentation tells us if the last character was an indentation character. - // emitter.compact_sequence_indent tells us if '- ' is considered part of the indentation for sequence elements. - // So, `seq` means that we are in a mapping context, and we are either at the first char of the column or - // the last character was not an indentation character, and we consider '- ' part of the indentation - // for sequence elements. - seq := emitter.mapping_context && (emitter.column == 0 || !emitter.indention) && - emitter.compact_sequence_indent - if !yaml_emitter_increase_indent_compact(emitter, false, false, seq) { - return false - } - } - if event.typ == yaml_SEQUENCE_END_EVENT { - emitter.indent = emitter.indents[len(emitter.indents)-1] - emitter.indents = emitter.indents[:len(emitter.indents)-1] - emitter.state = emitter.states[len(emitter.states)-1] - emitter.states = emitter.states[:len(emitter.states)-1] - return true - } - if !yaml_emitter_process_head_comment(emitter) { - return false - } - if !yaml_emitter_write_indent(emitter) { - return false - } - if !yaml_emitter_write_indicator(emitter, []byte{'-'}, true, false, true) { - return false - } - emitter.states = append(emitter.states, yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE) - if !yaml_emitter_emit_node(emitter, event, false, true, false, false) { - return false - } - if !yaml_emitter_process_line_comment(emitter) { - return false - } - if !yaml_emitter_process_foot_comment(emitter) { - return false - } - return true -} - -// Expect a block key node. -func yaml_emitter_emit_block_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool { - if first { - if !yaml_emitter_increase_indent(emitter, false, false) { - return false - } - } - if !yaml_emitter_process_head_comment(emitter) { - return false - } - if event.typ == yaml_MAPPING_END_EVENT { - emitter.indent = emitter.indents[len(emitter.indents)-1] - emitter.indents = emitter.indents[:len(emitter.indents)-1] - emitter.state = emitter.states[len(emitter.states)-1] - emitter.states = emitter.states[:len(emitter.states)-1] - return true - } - if !yaml_emitter_write_indent(emitter) { - return false - } - if len(emitter.line_comment) > 0 { - // [Go] A line comment was provided for the key. That's unusual as the - // scanner associates line comments with the value. Either way, - // save the line comment and render it appropriately later. - emitter.key_line_comment = emitter.line_comment - emitter.line_comment = nil - } - if yaml_emitter_check_simple_key(emitter) { - emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE) - return yaml_emitter_emit_node(emitter, event, false, false, true, true) - } - if !yaml_emitter_write_indicator(emitter, []byte{'?'}, true, false, true) { - return false - } - emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_VALUE_STATE) - return yaml_emitter_emit_node(emitter, event, false, false, true, false) -} - -// Expect a block value node. -func yaml_emitter_emit_block_mapping_value(emitter *yaml_emitter_t, event *yaml_event_t, simple bool) bool { - if simple { - if !yaml_emitter_write_indicator(emitter, []byte{':'}, false, false, false) { - return false - } - } else { - if !yaml_emitter_write_indent(emitter) { - return false - } - if !yaml_emitter_write_indicator(emitter, []byte{':'}, true, false, true) { - return false - } - } - if len(emitter.key_line_comment) > 0 { - // [Go] Line comments are generally associated with the value, but when there's - // no value on the same line as a mapping key they end up attached to the - // key itself. - if event.typ == yaml_SCALAR_EVENT { - if len(emitter.line_comment) == 0 { - // A scalar is coming and it has no line comments by itself yet, - // so just let it handle the line comment as usual. If it has a - // line comment, we can't have both so the one from the key is lost. - emitter.line_comment = emitter.key_line_comment - emitter.key_line_comment = nil - } - } else if event.sequence_style() != yaml_FLOW_SEQUENCE_STYLE && (event.typ == yaml_MAPPING_START_EVENT || event.typ == yaml_SEQUENCE_START_EVENT) { - // An indented block follows, so write the comment right now. - emitter.line_comment, emitter.key_line_comment = emitter.key_line_comment, emitter.line_comment - if !yaml_emitter_process_line_comment(emitter) { - return false - } - emitter.line_comment, emitter.key_line_comment = emitter.key_line_comment, emitter.line_comment - } - } - emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_KEY_STATE) - if !yaml_emitter_emit_node(emitter, event, false, false, true, false) { - return false - } - if !yaml_emitter_process_line_comment(emitter) { - return false - } - if !yaml_emitter_process_foot_comment(emitter) { - return false - } - return true -} - -func yaml_emitter_silent_nil_event(emitter *yaml_emitter_t, event *yaml_event_t) bool { - return event.typ == yaml_SCALAR_EVENT && event.implicit && !emitter.canonical && len(emitter.scalar_data.value) == 0 -} - -// Expect a node. -func yaml_emitter_emit_node(emitter *yaml_emitter_t, event *yaml_event_t, - root bool, sequence bool, mapping bool, simple_key bool) bool { - - emitter.root_context = root - emitter.sequence_context = sequence - emitter.mapping_context = mapping - emitter.simple_key_context = simple_key - - switch event.typ { - case yaml_ALIAS_EVENT: - return yaml_emitter_emit_alias(emitter, event) - case yaml_SCALAR_EVENT: - return yaml_emitter_emit_scalar(emitter, event) - case yaml_SEQUENCE_START_EVENT: - return yaml_emitter_emit_sequence_start(emitter, event) - case yaml_MAPPING_START_EVENT: - return yaml_emitter_emit_mapping_start(emitter, event) - default: - return yaml_emitter_set_emitter_error(emitter, - fmt.Sprintf("expected SCALAR, SEQUENCE-START, MAPPING-START, or ALIAS, but got %v", event.typ)) - } -} - -// Expect ALIAS. -func yaml_emitter_emit_alias(emitter *yaml_emitter_t, event *yaml_event_t) bool { - if !yaml_emitter_process_anchor(emitter) { - return false - } - emitter.state = emitter.states[len(emitter.states)-1] - emitter.states = emitter.states[:len(emitter.states)-1] - return true -} - -// Expect SCALAR. -func yaml_emitter_emit_scalar(emitter *yaml_emitter_t, event *yaml_event_t) bool { - if !yaml_emitter_select_scalar_style(emitter, event) { - return false - } - if !yaml_emitter_process_anchor(emitter) { - return false - } - if !yaml_emitter_process_tag(emitter) { - return false - } - if !yaml_emitter_increase_indent(emitter, true, false) { - return false - } - if !yaml_emitter_process_scalar(emitter) { - return false - } - emitter.indent = emitter.indents[len(emitter.indents)-1] - emitter.indents = emitter.indents[:len(emitter.indents)-1] - emitter.state = emitter.states[len(emitter.states)-1] - emitter.states = emitter.states[:len(emitter.states)-1] - return true -} - -// Expect SEQUENCE-START. -func yaml_emitter_emit_sequence_start(emitter *yaml_emitter_t, event *yaml_event_t) bool { - if !yaml_emitter_process_anchor(emitter) { - return false - } - if !yaml_emitter_process_tag(emitter) { - return false - } - if emitter.flow_level > 0 || emitter.canonical || event.sequence_style() == yaml_FLOW_SEQUENCE_STYLE || - yaml_emitter_check_empty_sequence(emitter) { - emitter.state = yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE - } else { - emitter.state = yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE - } - return true -} - -// Expect MAPPING-START. -func yaml_emitter_emit_mapping_start(emitter *yaml_emitter_t, event *yaml_event_t) bool { - if !yaml_emitter_process_anchor(emitter) { - return false - } - if !yaml_emitter_process_tag(emitter) { - return false - } - if emitter.flow_level > 0 || emitter.canonical || event.mapping_style() == yaml_FLOW_MAPPING_STYLE || - yaml_emitter_check_empty_mapping(emitter) { - emitter.state = yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE - } else { - emitter.state = yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE - } - return true -} - -// Check if the document content is an empty scalar. -func yaml_emitter_check_empty_document(emitter *yaml_emitter_t) bool { - return false // [Go] Huh? -} - -// Check if the next events represent an empty sequence. -func yaml_emitter_check_empty_sequence(emitter *yaml_emitter_t) bool { - if len(emitter.events)-emitter.events_head < 2 { - return false - } - return emitter.events[emitter.events_head].typ == yaml_SEQUENCE_START_EVENT && - emitter.events[emitter.events_head+1].typ == yaml_SEQUENCE_END_EVENT -} - -// Check if the next events represent an empty mapping. -func yaml_emitter_check_empty_mapping(emitter *yaml_emitter_t) bool { - if len(emitter.events)-emitter.events_head < 2 { - return false - } - return emitter.events[emitter.events_head].typ == yaml_MAPPING_START_EVENT && - emitter.events[emitter.events_head+1].typ == yaml_MAPPING_END_EVENT -} - -// Check if the next node can be expressed as a simple key. -func yaml_emitter_check_simple_key(emitter *yaml_emitter_t) bool { - length := 0 - switch emitter.events[emitter.events_head].typ { - case yaml_ALIAS_EVENT: - length += len(emitter.anchor_data.anchor) - case yaml_SCALAR_EVENT: - if emitter.scalar_data.multiline { - return false - } - length += len(emitter.anchor_data.anchor) + - len(emitter.tag_data.handle) + - len(emitter.tag_data.suffix) + - len(emitter.scalar_data.value) - case yaml_SEQUENCE_START_EVENT: - if !yaml_emitter_check_empty_sequence(emitter) { - return false - } - length += len(emitter.anchor_data.anchor) + - len(emitter.tag_data.handle) + - len(emitter.tag_data.suffix) - case yaml_MAPPING_START_EVENT: - if !yaml_emitter_check_empty_mapping(emitter) { - return false - } - length += len(emitter.anchor_data.anchor) + - len(emitter.tag_data.handle) + - len(emitter.tag_data.suffix) - default: - return false - } - return length <= 128 -} - -// Determine an acceptable scalar style. -func yaml_emitter_select_scalar_style(emitter *yaml_emitter_t, event *yaml_event_t) bool { - - no_tag := len(emitter.tag_data.handle) == 0 && len(emitter.tag_data.suffix) == 0 - if no_tag && !event.implicit && !event.quoted_implicit { - return yaml_emitter_set_emitter_error(emitter, "neither tag nor implicit flags are specified") - } - - style := event.scalar_style() - if style == yaml_ANY_SCALAR_STYLE { - style = yaml_PLAIN_SCALAR_STYLE - } - if emitter.canonical { - style = yaml_DOUBLE_QUOTED_SCALAR_STYLE - } - if emitter.simple_key_context && emitter.scalar_data.multiline { - style = yaml_DOUBLE_QUOTED_SCALAR_STYLE - } - - if style == yaml_PLAIN_SCALAR_STYLE { - if emitter.flow_level > 0 && !emitter.scalar_data.flow_plain_allowed || - emitter.flow_level == 0 && !emitter.scalar_data.block_plain_allowed { - style = yaml_SINGLE_QUOTED_SCALAR_STYLE - } - if len(emitter.scalar_data.value) == 0 && (emitter.flow_level > 0 || emitter.simple_key_context) { - style = yaml_SINGLE_QUOTED_SCALAR_STYLE - } - if no_tag && !event.implicit { - style = yaml_SINGLE_QUOTED_SCALAR_STYLE - } - } - if style == yaml_SINGLE_QUOTED_SCALAR_STYLE { - if !emitter.scalar_data.single_quoted_allowed { - style = yaml_DOUBLE_QUOTED_SCALAR_STYLE - } - } - if style == yaml_LITERAL_SCALAR_STYLE || style == yaml_FOLDED_SCALAR_STYLE { - if !emitter.scalar_data.block_allowed || emitter.flow_level > 0 || emitter.simple_key_context { - style = yaml_DOUBLE_QUOTED_SCALAR_STYLE - } - } - - if no_tag && !event.quoted_implicit && style != yaml_PLAIN_SCALAR_STYLE { - emitter.tag_data.handle = []byte{'!'} - } - emitter.scalar_data.style = style - return true -} - -// Write an anchor. -func yaml_emitter_process_anchor(emitter *yaml_emitter_t) bool { - if emitter.anchor_data.anchor == nil { - return true - } - c := []byte{'&'} - if emitter.anchor_data.alias { - c[0] = '*' - } - if !yaml_emitter_write_indicator(emitter, c, true, false, false) { - return false - } - return yaml_emitter_write_anchor(emitter, emitter.anchor_data.anchor) -} - -// Write a tag. -func yaml_emitter_process_tag(emitter *yaml_emitter_t) bool { - if len(emitter.tag_data.handle) == 0 && len(emitter.tag_data.suffix) == 0 { - return true - } - if len(emitter.tag_data.handle) > 0 { - if !yaml_emitter_write_tag_handle(emitter, emitter.tag_data.handle) { - return false - } - if len(emitter.tag_data.suffix) > 0 { - if !yaml_emitter_write_tag_content(emitter, emitter.tag_data.suffix, false) { - return false - } - } - } else { - // [Go] Allocate these slices elsewhere. - if !yaml_emitter_write_indicator(emitter, []byte("!<"), true, false, false) { - return false - } - if !yaml_emitter_write_tag_content(emitter, emitter.tag_data.suffix, false) { - return false - } - if !yaml_emitter_write_indicator(emitter, []byte{'>'}, false, false, false) { - return false - } - } - return true -} - -// Write a scalar. -func yaml_emitter_process_scalar(emitter *yaml_emitter_t) bool { - switch emitter.scalar_data.style { - case yaml_PLAIN_SCALAR_STYLE: - return yaml_emitter_write_plain_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context) - - case yaml_SINGLE_QUOTED_SCALAR_STYLE: - return yaml_emitter_write_single_quoted_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context) - - case yaml_DOUBLE_QUOTED_SCALAR_STYLE: - return yaml_emitter_write_double_quoted_scalar(emitter, emitter.scalar_data.value, !emitter.simple_key_context) - - case yaml_LITERAL_SCALAR_STYLE: - return yaml_emitter_write_literal_scalar(emitter, emitter.scalar_data.value) - - case yaml_FOLDED_SCALAR_STYLE: - return yaml_emitter_write_folded_scalar(emitter, emitter.scalar_data.value) - } - panic("unknown scalar style") -} - -// Write a head comment. -func yaml_emitter_process_head_comment(emitter *yaml_emitter_t) bool { - if len(emitter.tail_comment) > 0 { - if !yaml_emitter_write_indent(emitter) { - return false - } - if !yaml_emitter_write_comment(emitter, emitter.tail_comment) { - return false - } - emitter.tail_comment = emitter.tail_comment[:0] - emitter.foot_indent = emitter.indent - if emitter.foot_indent < 0 { - emitter.foot_indent = 0 - } - } - - if len(emitter.head_comment) == 0 { - return true - } - if !yaml_emitter_write_indent(emitter) { - return false - } - if !yaml_emitter_write_comment(emitter, emitter.head_comment) { - return false - } - emitter.head_comment = emitter.head_comment[:0] - return true -} - -// Write an line comment. -func yaml_emitter_process_line_comment_linebreak(emitter *yaml_emitter_t, linebreak bool) bool { - if len(emitter.line_comment) == 0 { - // The next 3 lines are needed to resolve an issue with leading newlines - // See https://github.com/go-yaml/yaml/issues/755 - // When linebreak is set to true, put_break will be called and will add - // the needed newline. - if linebreak && !put_break(emitter) { - return false - } - return true - } - if !emitter.whitespace { - if !put(emitter, ' ') { - return false - } - } - if !yaml_emitter_write_comment(emitter, emitter.line_comment) { - return false - } - emitter.line_comment = emitter.line_comment[:0] - return true -} - -// Write a foot comment. -func yaml_emitter_process_foot_comment(emitter *yaml_emitter_t) bool { - if len(emitter.foot_comment) == 0 { - return true - } - if !yaml_emitter_write_indent(emitter) { - return false - } - if !yaml_emitter_write_comment(emitter, emitter.foot_comment) { - return false - } - emitter.foot_comment = emitter.foot_comment[:0] - emitter.foot_indent = emitter.indent - if emitter.foot_indent < 0 { - emitter.foot_indent = 0 - } - return true -} - -// Check if a %YAML directive is valid. -func yaml_emitter_analyze_version_directive(emitter *yaml_emitter_t, version_directive *yaml_version_directive_t) bool { - if version_directive.major != 1 || version_directive.minor != 1 { - return yaml_emitter_set_emitter_error(emitter, "incompatible %YAML directive") - } - return true -} - -// Check if a %TAG directive is valid. -func yaml_emitter_analyze_tag_directive(emitter *yaml_emitter_t, tag_directive *yaml_tag_directive_t) bool { - handle := tag_directive.handle - prefix := tag_directive.prefix - if len(handle) == 0 { - return yaml_emitter_set_emitter_error(emitter, "tag handle must not be empty") - } - if handle[0] != '!' { - return yaml_emitter_set_emitter_error(emitter, "tag handle must start with '!'") - } - if handle[len(handle)-1] != '!' { - return yaml_emitter_set_emitter_error(emitter, "tag handle must end with '!'") - } - for i := 1; i < len(handle)-1; i += width(handle[i]) { - if !is_alpha(handle, i) { - return yaml_emitter_set_emitter_error(emitter, "tag handle must contain alphanumerical characters only") - } - } - if len(prefix) == 0 { - return yaml_emitter_set_emitter_error(emitter, "tag prefix must not be empty") - } - return true -} - -// Check if an anchor is valid. -func yaml_emitter_analyze_anchor(emitter *yaml_emitter_t, anchor []byte, alias bool) bool { - if len(anchor) == 0 { - problem := "anchor value must not be empty" - if alias { - problem = "alias value must not be empty" - } - return yaml_emitter_set_emitter_error(emitter, problem) - } - for i := 0; i < len(anchor); i += width(anchor[i]) { - if !is_alpha(anchor, i) { - problem := "anchor value must contain alphanumerical characters only" - if alias { - problem = "alias value must contain alphanumerical characters only" - } - return yaml_emitter_set_emitter_error(emitter, problem) - } - } - emitter.anchor_data.anchor = anchor - emitter.anchor_data.alias = alias - return true -} - -// Check if a tag is valid. -func yaml_emitter_analyze_tag(emitter *yaml_emitter_t, tag []byte) bool { - if len(tag) == 0 { - return yaml_emitter_set_emitter_error(emitter, "tag value must not be empty") - } - for i := 0; i < len(emitter.tag_directives); i++ { - tag_directive := &emitter.tag_directives[i] - if bytes.HasPrefix(tag, tag_directive.prefix) { - emitter.tag_data.handle = tag_directive.handle - emitter.tag_data.suffix = tag[len(tag_directive.prefix):] - return true - } - } - emitter.tag_data.suffix = tag - return true -} - -// Check if a scalar is valid. -func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool { - var ( - block_indicators = false - flow_indicators = false - line_breaks = false - special_characters = false - tab_characters = false - - leading_space = false - leading_break = false - trailing_space = false - trailing_break = false - break_space = false - space_break = false - - preceded_by_whitespace = false - followed_by_whitespace = false - previous_space = false - previous_break = false - ) - - emitter.scalar_data.value = value - - if len(value) == 0 { - emitter.scalar_data.multiline = false - emitter.scalar_data.flow_plain_allowed = false - emitter.scalar_data.block_plain_allowed = true - emitter.scalar_data.single_quoted_allowed = true - emitter.scalar_data.block_allowed = false - return true - } - - if len(value) >= 3 && ((value[0] == '-' && value[1] == '-' && value[2] == '-') || (value[0] == '.' && value[1] == '.' && value[2] == '.')) { - block_indicators = true - flow_indicators = true - } - - preceded_by_whitespace = true - for i, w := 0, 0; i < len(value); i += w { - w = width(value[i]) - followed_by_whitespace = i+w >= len(value) || is_blank(value, i+w) - - if i == 0 { - switch value[i] { - case '#', ',', '[', ']', '{', '}', '&', '*', '!', '|', '>', '\'', '"', '%', '@', '`': - flow_indicators = true - block_indicators = true - case '?', ':': - flow_indicators = true - if followed_by_whitespace { - block_indicators = true - } - case '-': - if followed_by_whitespace { - flow_indicators = true - block_indicators = true - } - } - } else { - switch value[i] { - case ',', '?', '[', ']', '{', '}': - flow_indicators = true - case ':': - flow_indicators = true - if followed_by_whitespace { - block_indicators = true - } - case '#': - if preceded_by_whitespace { - flow_indicators = true - block_indicators = true - } - } - } - - if value[i] == '\t' { - tab_characters = true - } else if !is_printable(value, i) || !is_ascii(value, i) && !emitter.unicode { - special_characters = true - } - if is_space(value, i) { - if i == 0 { - leading_space = true - } - if i+width(value[i]) == len(value) { - trailing_space = true - } - if previous_break { - break_space = true - } - previous_space = true - previous_break = false - } else if is_break(value, i) { - line_breaks = true - if i == 0 { - leading_break = true - } - if i+width(value[i]) == len(value) { - trailing_break = true - } - if previous_space { - space_break = true - } - previous_space = false - previous_break = true - } else { - previous_space = false - previous_break = false - } - - // [Go]: Why 'z'? Couldn't be the end of the string as that's the loop condition. - preceded_by_whitespace = is_blankz(value, i) - } - - emitter.scalar_data.multiline = line_breaks - emitter.scalar_data.flow_plain_allowed = true - emitter.scalar_data.block_plain_allowed = true - emitter.scalar_data.single_quoted_allowed = true - emitter.scalar_data.block_allowed = true - - if leading_space || leading_break || trailing_space || trailing_break { - emitter.scalar_data.flow_plain_allowed = false - emitter.scalar_data.block_plain_allowed = false - } - if trailing_space { - emitter.scalar_data.block_allowed = false - } - if break_space { - emitter.scalar_data.flow_plain_allowed = false - emitter.scalar_data.block_plain_allowed = false - emitter.scalar_data.single_quoted_allowed = false - } - if space_break || tab_characters || special_characters { - emitter.scalar_data.flow_plain_allowed = false - emitter.scalar_data.block_plain_allowed = false - emitter.scalar_data.single_quoted_allowed = false - } - if space_break || special_characters { - emitter.scalar_data.block_allowed = false - } - if line_breaks { - emitter.scalar_data.flow_plain_allowed = false - emitter.scalar_data.block_plain_allowed = false - } - if flow_indicators { - emitter.scalar_data.flow_plain_allowed = false - } - if block_indicators { - emitter.scalar_data.block_plain_allowed = false - } - return true -} - -// Check if the event data is valid. -func yaml_emitter_analyze_event(emitter *yaml_emitter_t, event *yaml_event_t) bool { - - emitter.anchor_data.anchor = nil - emitter.tag_data.handle = nil - emitter.tag_data.suffix = nil - emitter.scalar_data.value = nil - - if len(event.head_comment) > 0 { - emitter.head_comment = event.head_comment - } - if len(event.line_comment) > 0 { - emitter.line_comment = event.line_comment - } - if len(event.foot_comment) > 0 { - emitter.foot_comment = event.foot_comment - } - if len(event.tail_comment) > 0 { - emitter.tail_comment = event.tail_comment - } - - switch event.typ { - case yaml_ALIAS_EVENT: - if !yaml_emitter_analyze_anchor(emitter, event.anchor, true) { - return false - } - - case yaml_SCALAR_EVENT: - if len(event.anchor) > 0 { - if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) { - return false - } - } - if len(event.tag) > 0 && (emitter.canonical || (!event.implicit && !event.quoted_implicit)) { - if !yaml_emitter_analyze_tag(emitter, event.tag) { - return false - } - } - if !yaml_emitter_analyze_scalar(emitter, event.value) { - return false - } - - case yaml_SEQUENCE_START_EVENT: - if len(event.anchor) > 0 { - if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) { - return false - } - } - if len(event.tag) > 0 && (emitter.canonical || !event.implicit) { - if !yaml_emitter_analyze_tag(emitter, event.tag) { - return false - } - } - - case yaml_MAPPING_START_EVENT: - if len(event.anchor) > 0 { - if !yaml_emitter_analyze_anchor(emitter, event.anchor, false) { - return false - } - } - if len(event.tag) > 0 && (emitter.canonical || !event.implicit) { - if !yaml_emitter_analyze_tag(emitter, event.tag) { - return false - } - } - } - return true -} - -// Write the BOM character. -func yaml_emitter_write_bom(emitter *yaml_emitter_t) bool { - if !flush(emitter) { - return false - } - pos := emitter.buffer_pos - emitter.buffer[pos+0] = '\xEF' - emitter.buffer[pos+1] = '\xBB' - emitter.buffer[pos+2] = '\xBF' - emitter.buffer_pos += 3 - return true -} - -func yaml_emitter_write_indent(emitter *yaml_emitter_t) bool { - indent := emitter.indent - if indent < 0 { - indent = 0 - } - if !emitter.indention || emitter.column > indent || (emitter.column == indent && !emitter.whitespace) { - if !put_break(emitter) { - return false - } - } - if emitter.foot_indent == indent { - if !put_break(emitter) { - return false - } - } - for emitter.column < indent { - if !put(emitter, ' ') { - return false - } - } - emitter.whitespace = true - //emitter.indention = true - emitter.space_above = false - emitter.foot_indent = -1 - return true -} - -func yaml_emitter_write_indicator(emitter *yaml_emitter_t, indicator []byte, need_whitespace, is_whitespace, is_indention bool) bool { - if need_whitespace && !emitter.whitespace { - if !put(emitter, ' ') { - return false - } - } - if !write_all(emitter, indicator) { - return false - } - emitter.whitespace = is_whitespace - emitter.indention = (emitter.indention && is_indention) - emitter.open_ended = false - return true -} - -func yaml_emitter_write_anchor(emitter *yaml_emitter_t, value []byte) bool { - if !write_all(emitter, value) { - return false - } - emitter.whitespace = false - emitter.indention = false - return true -} - -func yaml_emitter_write_tag_handle(emitter *yaml_emitter_t, value []byte) bool { - if !emitter.whitespace { - if !put(emitter, ' ') { - return false - } - } - if !write_all(emitter, value) { - return false - } - emitter.whitespace = false - emitter.indention = false - return true -} - -func yaml_emitter_write_tag_content(emitter *yaml_emitter_t, value []byte, need_whitespace bool) bool { - if need_whitespace && !emitter.whitespace { - if !put(emitter, ' ') { - return false - } - } - for i := 0; i < len(value); { - var must_write bool - switch value[i] { - case ';', '/', '?', ':', '@', '&', '=', '+', '$', ',', '_', '.', '~', '*', '\'', '(', ')', '[', ']': - must_write = true - default: - must_write = is_alpha(value, i) - } - if must_write { - if !write(emitter, value, &i) { - return false - } - } else { - w := width(value[i]) - for k := 0; k < w; k++ { - octet := value[i] - i++ - if !put(emitter, '%') { - return false - } - - c := octet >> 4 - if c < 10 { - c += '0' - } else { - c += 'A' - 10 - } - if !put(emitter, c) { - return false - } - - c = octet & 0x0f - if c < 10 { - c += '0' - } else { - c += 'A' - 10 - } - if !put(emitter, c) { - return false - } - } - } - } - emitter.whitespace = false - emitter.indention = false - return true -} - -func yaml_emitter_write_plain_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { - if len(value) > 0 && !emitter.whitespace { - if !put(emitter, ' ') { - return false - } - } - - spaces := false - breaks := false - for i := 0; i < len(value); { - if is_space(value, i) { - if allow_breaks && !spaces && emitter.column > emitter.best_width && !is_space(value, i+1) { - if !yaml_emitter_write_indent(emitter) { - return false - } - i += width(value[i]) - } else { - if !write(emitter, value, &i) { - return false - } - } - spaces = true - } else if is_break(value, i) { - if !breaks && value[i] == '\n' { - if !put_break(emitter) { - return false - } - } - if !write_break(emitter, value, &i) { - return false - } - //emitter.indention = true - breaks = true - } else { - if breaks { - if !yaml_emitter_write_indent(emitter) { - return false - } - } - if !write(emitter, value, &i) { - return false - } - emitter.indention = false - spaces = false - breaks = false - } - } - - if len(value) > 0 { - emitter.whitespace = false - } - emitter.indention = false - if emitter.root_context { - emitter.open_ended = true - } - - return true -} - -func yaml_emitter_write_single_quoted_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { - - if !yaml_emitter_write_indicator(emitter, []byte{'\''}, true, false, false) { - return false - } - - spaces := false - breaks := false - for i := 0; i < len(value); { - if is_space(value, i) { - if allow_breaks && !spaces && emitter.column > emitter.best_width && i > 0 && i < len(value)-1 && !is_space(value, i+1) { - if !yaml_emitter_write_indent(emitter) { - return false - } - i += width(value[i]) - } else { - if !write(emitter, value, &i) { - return false - } - } - spaces = true - } else if is_break(value, i) { - if !breaks && value[i] == '\n' { - if !put_break(emitter) { - return false - } - } - if !write_break(emitter, value, &i) { - return false - } - //emitter.indention = true - breaks = true - } else { - if breaks { - if !yaml_emitter_write_indent(emitter) { - return false - } - } - if value[i] == '\'' { - if !put(emitter, '\'') { - return false - } - } - if !write(emitter, value, &i) { - return false - } - emitter.indention = false - spaces = false - breaks = false - } - } - if !yaml_emitter_write_indicator(emitter, []byte{'\''}, false, false, false) { - return false - } - emitter.whitespace = false - emitter.indention = false - return true -} - -func yaml_emitter_write_double_quoted_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool { - spaces := false - if !yaml_emitter_write_indicator(emitter, []byte{'"'}, true, false, false) { - return false - } - - for i := 0; i < len(value); { - if !is_printable(value, i) || (!emitter.unicode && !is_ascii(value, i)) || - is_bom(value, i) || is_break(value, i) || - value[i] == '"' || value[i] == '\\' { - - octet := value[i] - - var w int - var v rune - switch { - case octet&0x80 == 0x00: - w, v = 1, rune(octet&0x7F) - case octet&0xE0 == 0xC0: - w, v = 2, rune(octet&0x1F) - case octet&0xF0 == 0xE0: - w, v = 3, rune(octet&0x0F) - case octet&0xF8 == 0xF0: - w, v = 4, rune(octet&0x07) - } - for k := 1; k < w; k++ { - octet = value[i+k] - v = (v << 6) + (rune(octet) & 0x3F) - } - i += w - - if !put(emitter, '\\') { - return false - } - - var ok bool - switch v { - case 0x00: - ok = put(emitter, '0') - case 0x07: - ok = put(emitter, 'a') - case 0x08: - ok = put(emitter, 'b') - case 0x09: - ok = put(emitter, 't') - case 0x0A: - ok = put(emitter, 'n') - case 0x0b: - ok = put(emitter, 'v') - case 0x0c: - ok = put(emitter, 'f') - case 0x0d: - ok = put(emitter, 'r') - case 0x1b: - ok = put(emitter, 'e') - case 0x22: - ok = put(emitter, '"') - case 0x5c: - ok = put(emitter, '\\') - case 0x85: - ok = put(emitter, 'N') - case 0xA0: - ok = put(emitter, '_') - case 0x2028: - ok = put(emitter, 'L') - case 0x2029: - ok = put(emitter, 'P') - default: - if v <= 0xFF { - ok = put(emitter, 'x') - w = 2 - } else if v <= 0xFFFF { - ok = put(emitter, 'u') - w = 4 - } else { - ok = put(emitter, 'U') - w = 8 - } - for k := (w - 1) * 4; ok && k >= 0; k -= 4 { - digit := byte((v >> uint(k)) & 0x0F) - if digit < 10 { - ok = put(emitter, digit+'0') - } else { - ok = put(emitter, digit+'A'-10) - } - } - } - if !ok { - return false - } - spaces = false - } else if is_space(value, i) { - if allow_breaks && !spaces && emitter.column > emitter.best_width && i > 0 && i < len(value)-1 { - if !yaml_emitter_write_indent(emitter) { - return false - } - if is_space(value, i+1) { - if !put(emitter, '\\') { - return false - } - } - i += width(value[i]) - } else if !write(emitter, value, &i) { - return false - } - spaces = true - } else { - if !write(emitter, value, &i) { - return false - } - spaces = false - } - } - if !yaml_emitter_write_indicator(emitter, []byte{'"'}, false, false, false) { - return false - } - emitter.whitespace = false - emitter.indention = false - return true -} - -func yaml_emitter_write_block_scalar_hints(emitter *yaml_emitter_t, value []byte) bool { - if is_space(value, 0) || is_break(value, 0) { - indent_hint := []byte{'0' + byte(emitter.best_indent)} - if !yaml_emitter_write_indicator(emitter, indent_hint, false, false, false) { - return false - } - } - - emitter.open_ended = false - - var chomp_hint [1]byte - if len(value) == 0 { - chomp_hint[0] = '-' - } else { - i := len(value) - 1 - for value[i]&0xC0 == 0x80 { - i-- - } - if !is_break(value, i) { - chomp_hint[0] = '-' - } else if i == 0 { - chomp_hint[0] = '+' - emitter.open_ended = true - } else { - i-- - for value[i]&0xC0 == 0x80 { - i-- - } - if is_break(value, i) { - chomp_hint[0] = '+' - emitter.open_ended = true - } - } - } - if chomp_hint[0] != 0 { - if !yaml_emitter_write_indicator(emitter, chomp_hint[:], false, false, false) { - return false - } - } - return true -} - -func yaml_emitter_write_literal_scalar(emitter *yaml_emitter_t, value []byte) bool { - if !yaml_emitter_write_indicator(emitter, []byte{'|'}, true, false, false) { - return false - } - if !yaml_emitter_write_block_scalar_hints(emitter, value) { - return false - } - if !yaml_emitter_process_line_comment_linebreak(emitter, true) { - return false - } - //emitter.indention = true - emitter.whitespace = true - breaks := true - for i := 0; i < len(value); { - if is_break(value, i) { - if !write_break(emitter, value, &i) { - return false - } - //emitter.indention = true - breaks = true - } else { - if breaks { - if !yaml_emitter_write_indent(emitter) { - return false - } - } - if !write(emitter, value, &i) { - return false - } - emitter.indention = false - breaks = false - } - } - - return true -} - -func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) bool { - if !yaml_emitter_write_indicator(emitter, []byte{'>'}, true, false, false) { - return false - } - if !yaml_emitter_write_block_scalar_hints(emitter, value) { - return false - } - if !yaml_emitter_process_line_comment_linebreak(emitter, true) { - return false - } - - //emitter.indention = true - emitter.whitespace = true - - breaks := true - leading_spaces := true - for i := 0; i < len(value); { - if is_break(value, i) { - if !breaks && !leading_spaces && value[i] == '\n' { - k := 0 - for is_break(value, k) { - k += width(value[k]) - } - if !is_blankz(value, k) { - if !put_break(emitter) { - return false - } - } - } - if !write_break(emitter, value, &i) { - return false - } - //emitter.indention = true - breaks = true - } else { - if breaks { - if !yaml_emitter_write_indent(emitter) { - return false - } - leading_spaces = is_blank(value, i) - } - if !breaks && is_space(value, i) && !is_space(value, i+1) && emitter.column > emitter.best_width { - if !yaml_emitter_write_indent(emitter) { - return false - } - i += width(value[i]) - } else { - if !write(emitter, value, &i) { - return false - } - } - emitter.indention = false - breaks = false - } - } - return true -} - -func yaml_emitter_write_comment(emitter *yaml_emitter_t, comment []byte) bool { - breaks := false - pound := false - for i := 0; i < len(comment); { - if is_break(comment, i) { - if !write_break(emitter, comment, &i) { - return false - } - //emitter.indention = true - breaks = true - pound = false - } else { - if breaks && !yaml_emitter_write_indent(emitter) { - return false - } - if !pound { - if comment[i] != '#' && (!put(emitter, '#') || !put(emitter, ' ')) { - return false - } - pound = true - } - if !write(emitter, comment, &i) { - return false - } - emitter.indention = false - breaks = false - } - } - if !breaks && !put_break(emitter) { - return false - } - - emitter.whitespace = true - //emitter.indention = true - return true -} diff --git a/vendor/go.yaml.in/yaml/v3/encode.go b/vendor/go.yaml.in/yaml/v3/encode.go deleted file mode 100644 index de9e72a3e..000000000 --- a/vendor/go.yaml.in/yaml/v3/encode.go +++ /dev/null @@ -1,577 +0,0 @@ -// -// Copyright (c) 2011-2019 Canonical Ltd -// -// 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 yaml - -import ( - "encoding" - "fmt" - "io" - "reflect" - "regexp" - "sort" - "strconv" - "strings" - "time" - "unicode/utf8" -) - -type encoder struct { - emitter yaml_emitter_t - event yaml_event_t - out []byte - flow bool - indent int - doneInit bool -} - -func newEncoder() *encoder { - e := &encoder{} - yaml_emitter_initialize(&e.emitter) - yaml_emitter_set_output_string(&e.emitter, &e.out) - yaml_emitter_set_unicode(&e.emitter, true) - return e -} - -func newEncoderWithWriter(w io.Writer) *encoder { - e := &encoder{} - yaml_emitter_initialize(&e.emitter) - yaml_emitter_set_output_writer(&e.emitter, w) - yaml_emitter_set_unicode(&e.emitter, true) - return e -} - -func (e *encoder) init() { - if e.doneInit { - return - } - if e.indent == 0 { - e.indent = 4 - } - e.emitter.best_indent = e.indent - yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING) - e.emit() - e.doneInit = true -} - -func (e *encoder) finish() { - e.emitter.open_ended = false - yaml_stream_end_event_initialize(&e.event) - e.emit() -} - -func (e *encoder) destroy() { - yaml_emitter_delete(&e.emitter) -} - -func (e *encoder) emit() { - // This will internally delete the e.event value. - e.must(yaml_emitter_emit(&e.emitter, &e.event)) -} - -func (e *encoder) must(ok bool) { - if !ok { - msg := e.emitter.problem - if msg == "" { - msg = "unknown problem generating YAML content" - } - failf("%s", msg) - } -} - -func (e *encoder) marshalDoc(tag string, in reflect.Value) { - e.init() - var node *Node - if in.IsValid() { - node, _ = in.Interface().(*Node) - } - if node != nil && node.Kind == DocumentNode { - e.nodev(in) - } else { - yaml_document_start_event_initialize(&e.event, nil, nil, true) - e.emit() - e.marshal(tag, in) - yaml_document_end_event_initialize(&e.event, true) - e.emit() - } -} - -func (e *encoder) marshal(tag string, in reflect.Value) { - tag = shortTag(tag) - if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() { - e.nilv() - return - } - iface := in.Interface() - switch value := iface.(type) { - case *Node: - e.nodev(in) - return - case Node: - if !in.CanAddr() { - var n = reflect.New(in.Type()).Elem() - n.Set(in) - in = n - } - e.nodev(in.Addr()) - return - case time.Time: - e.timev(tag, in) - return - case *time.Time: - e.timev(tag, in.Elem()) - return - case time.Duration: - e.stringv(tag, reflect.ValueOf(value.String())) - return - case Marshaler: - v, err := value.MarshalYAML() - if err != nil { - fail(err) - } - if v == nil { - e.nilv() - return - } - e.marshal(tag, reflect.ValueOf(v)) - return - case encoding.TextMarshaler: - text, err := value.MarshalText() - if err != nil { - fail(err) - } - in = reflect.ValueOf(string(text)) - case nil: - e.nilv() - return - } - switch in.Kind() { - case reflect.Interface: - e.marshal(tag, in.Elem()) - case reflect.Map: - e.mapv(tag, in) - case reflect.Ptr: - e.marshal(tag, in.Elem()) - case reflect.Struct: - e.structv(tag, in) - case reflect.Slice, reflect.Array: - e.slicev(tag, in) - case reflect.String: - e.stringv(tag, in) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - e.intv(tag, in) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - e.uintv(tag, in) - case reflect.Float32, reflect.Float64: - e.floatv(tag, in) - case reflect.Bool: - e.boolv(tag, in) - default: - panic("cannot marshal type: " + in.Type().String()) - } -} - -func (e *encoder) mapv(tag string, in reflect.Value) { - e.mappingv(tag, func() { - keys := keyList(in.MapKeys()) - sort.Sort(keys) - for _, k := range keys { - e.marshal("", k) - e.marshal("", in.MapIndex(k)) - } - }) -} - -func (e *encoder) fieldByIndex(v reflect.Value, index []int) (field reflect.Value) { - for _, num := range index { - for { - if v.Kind() == reflect.Ptr { - if v.IsNil() { - return reflect.Value{} - } - v = v.Elem() - continue - } - break - } - v = v.Field(num) - } - return v -} - -func (e *encoder) structv(tag string, in reflect.Value) { - sinfo, err := getStructInfo(in.Type()) - if err != nil { - panic(err) - } - e.mappingv(tag, func() { - for _, info := range sinfo.FieldsList { - var value reflect.Value - if info.Inline == nil { - value = in.Field(info.Num) - } else { - value = e.fieldByIndex(in, info.Inline) - if !value.IsValid() { - continue - } - } - if info.OmitEmpty && isZero(value) { - continue - } - e.marshal("", reflect.ValueOf(info.Key)) - e.flow = info.Flow - e.marshal("", value) - } - if sinfo.InlineMap >= 0 { - m := in.Field(sinfo.InlineMap) - if m.Len() > 0 { - e.flow = false - keys := keyList(m.MapKeys()) - sort.Sort(keys) - for _, k := range keys { - if _, found := sinfo.FieldsMap[k.String()]; found { - panic(fmt.Sprintf("cannot have key %q in inlined map: conflicts with struct field", k.String())) - } - e.marshal("", k) - e.flow = false - e.marshal("", m.MapIndex(k)) - } - } - } - }) -} - -func (e *encoder) mappingv(tag string, f func()) { - implicit := tag == "" - style := yaml_BLOCK_MAPPING_STYLE - if e.flow { - e.flow = false - style = yaml_FLOW_MAPPING_STYLE - } - yaml_mapping_start_event_initialize(&e.event, nil, []byte(tag), implicit, style) - e.emit() - f() - yaml_mapping_end_event_initialize(&e.event) - e.emit() -} - -func (e *encoder) slicev(tag string, in reflect.Value) { - implicit := tag == "" - style := yaml_BLOCK_SEQUENCE_STYLE - if e.flow { - e.flow = false - style = yaml_FLOW_SEQUENCE_STYLE - } - e.must(yaml_sequence_start_event_initialize(&e.event, nil, []byte(tag), implicit, style)) - e.emit() - n := in.Len() - for i := 0; i < n; i++ { - e.marshal("", in.Index(i)) - } - e.must(yaml_sequence_end_event_initialize(&e.event)) - e.emit() -} - -// isBase60 returns whether s is in base 60 notation as defined in YAML 1.1. -// -// The base 60 float notation in YAML 1.1 is a terrible idea and is unsupported -// in YAML 1.2 and by this package, but these should be marshalled quoted for -// the time being for compatibility with other parsers. -func isBase60Float(s string) (result bool) { - // Fast path. - if s == "" { - return false - } - c := s[0] - if !(c == '+' || c == '-' || c >= '0' && c <= '9') || strings.IndexByte(s, ':') < 0 { - return false - } - // Do the full match. - return base60float.MatchString(s) -} - -// From http://yaml.org/type/float.html, except the regular expression there -// is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix. -var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`) - -// isOldBool returns whether s is bool notation as defined in YAML 1.1. -// -// We continue to force strings that YAML 1.1 would interpret as booleans to be -// rendered as quotes strings so that the marshalled output valid for YAML 1.1 -// parsing. -func isOldBool(s string) (result bool) { - switch s { - case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON", - "n", "N", "no", "No", "NO", "off", "Off", "OFF": - return true - default: - return false - } -} - -func (e *encoder) stringv(tag string, in reflect.Value) { - var style yaml_scalar_style_t - s := in.String() - canUsePlain := true - switch { - case !utf8.ValidString(s): - if tag == binaryTag { - failf("explicitly tagged !!binary data must be base64-encoded") - } - if tag != "" { - failf("cannot marshal invalid UTF-8 data as %s", shortTag(tag)) - } - // It can't be encoded directly as YAML so use a binary tag - // and encode it as base64. - tag = binaryTag - s = encodeBase64(s) - case tag == "": - // Check to see if it would resolve to a specific - // tag when encoded unquoted. If it doesn't, - // there's no need to quote it. - rtag, _ := resolve("", s) - canUsePlain = rtag == strTag && !(isBase60Float(s) || isOldBool(s)) - } - // Note: it's possible for user code to emit invalid YAML - // if they explicitly specify a tag and a string containing - // text that's incompatible with that tag. - switch { - case strings.Contains(s, "\n"): - if e.flow { - style = yaml_DOUBLE_QUOTED_SCALAR_STYLE - } else { - style = yaml_LITERAL_SCALAR_STYLE - } - case canUsePlain: - style = yaml_PLAIN_SCALAR_STYLE - default: - style = yaml_DOUBLE_QUOTED_SCALAR_STYLE - } - e.emitScalar(s, "", tag, style, nil, nil, nil, nil) -} - -func (e *encoder) boolv(tag string, in reflect.Value) { - var s string - if in.Bool() { - s = "true" - } else { - s = "false" - } - e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) -} - -func (e *encoder) intv(tag string, in reflect.Value) { - s := strconv.FormatInt(in.Int(), 10) - e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) -} - -func (e *encoder) uintv(tag string, in reflect.Value) { - s := strconv.FormatUint(in.Uint(), 10) - e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) -} - -func (e *encoder) timev(tag string, in reflect.Value) { - t := in.Interface().(time.Time) - s := t.Format(time.RFC3339Nano) - e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) -} - -func (e *encoder) floatv(tag string, in reflect.Value) { - // Issue #352: When formatting, use the precision of the underlying value - precision := 64 - if in.Kind() == reflect.Float32 { - precision = 32 - } - - s := strconv.FormatFloat(in.Float(), 'g', -1, precision) - switch s { - case "+Inf": - s = ".inf" - case "-Inf": - s = "-.inf" - case "NaN": - s = ".nan" - } - e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) -} - -func (e *encoder) nilv() { - e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil) -} - -func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t, head, line, foot, tail []byte) { - // TODO Kill this function. Replace all initialize calls by their underlining Go literals. - implicit := tag == "" - if !implicit { - tag = longTag(tag) - } - e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style)) - e.event.head_comment = head - e.event.line_comment = line - e.event.foot_comment = foot - e.event.tail_comment = tail - e.emit() -} - -func (e *encoder) nodev(in reflect.Value) { - e.node(in.Interface().(*Node), "") -} - -func (e *encoder) node(node *Node, tail string) { - // Zero nodes behave as nil. - if node.Kind == 0 && node.IsZero() { - e.nilv() - return - } - - // If the tag was not explicitly requested, and dropping it won't change the - // implicit tag of the value, don't include it in the presentation. - var tag = node.Tag - var stag = shortTag(tag) - var forceQuoting bool - if tag != "" && node.Style&TaggedStyle == 0 { - if node.Kind == ScalarNode { - if stag == strTag && node.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0 { - tag = "" - } else { - rtag, _ := resolve("", node.Value) - if rtag == stag { - tag = "" - } else if stag == strTag { - tag = "" - forceQuoting = true - } - } - } else { - var rtag string - switch node.Kind { - case MappingNode: - rtag = mapTag - case SequenceNode: - rtag = seqTag - } - if rtag == stag { - tag = "" - } - } - } - - switch node.Kind { - case DocumentNode: - yaml_document_start_event_initialize(&e.event, nil, nil, true) - e.event.head_comment = []byte(node.HeadComment) - e.emit() - for _, node := range node.Content { - e.node(node, "") - } - yaml_document_end_event_initialize(&e.event, true) - e.event.foot_comment = []byte(node.FootComment) - e.emit() - - case SequenceNode: - style := yaml_BLOCK_SEQUENCE_STYLE - if node.Style&FlowStyle != 0 { - style = yaml_FLOW_SEQUENCE_STYLE - } - e.must(yaml_sequence_start_event_initialize(&e.event, []byte(node.Anchor), []byte(longTag(tag)), tag == "", style)) - e.event.head_comment = []byte(node.HeadComment) - e.emit() - for _, node := range node.Content { - e.node(node, "") - } - e.must(yaml_sequence_end_event_initialize(&e.event)) - e.event.line_comment = []byte(node.LineComment) - e.event.foot_comment = []byte(node.FootComment) - e.emit() - - case MappingNode: - style := yaml_BLOCK_MAPPING_STYLE - if node.Style&FlowStyle != 0 { - style = yaml_FLOW_MAPPING_STYLE - } - yaml_mapping_start_event_initialize(&e.event, []byte(node.Anchor), []byte(longTag(tag)), tag == "", style) - e.event.tail_comment = []byte(tail) - e.event.head_comment = []byte(node.HeadComment) - e.emit() - - // The tail logic below moves the foot comment of prior keys to the following key, - // since the value for each key may be a nested structure and the foot needs to be - // processed only the entirety of the value is streamed. The last tail is processed - // with the mapping end event. - var tail string - for i := 0; i+1 < len(node.Content); i += 2 { - k := node.Content[i] - foot := k.FootComment - if foot != "" { - kopy := *k - kopy.FootComment = "" - k = &kopy - } - e.node(k, tail) - tail = foot - - v := node.Content[i+1] - e.node(v, "") - } - - yaml_mapping_end_event_initialize(&e.event) - e.event.tail_comment = []byte(tail) - e.event.line_comment = []byte(node.LineComment) - e.event.foot_comment = []byte(node.FootComment) - e.emit() - - case AliasNode: - yaml_alias_event_initialize(&e.event, []byte(node.Value)) - e.event.head_comment = []byte(node.HeadComment) - e.event.line_comment = []byte(node.LineComment) - e.event.foot_comment = []byte(node.FootComment) - e.emit() - - case ScalarNode: - value := node.Value - if !utf8.ValidString(value) { - if stag == binaryTag { - failf("explicitly tagged !!binary data must be base64-encoded") - } - if stag != "" { - failf("cannot marshal invalid UTF-8 data as %s", stag) - } - // It can't be encoded directly as YAML so use a binary tag - // and encode it as base64. - tag = binaryTag - value = encodeBase64(value) - } - - style := yaml_PLAIN_SCALAR_STYLE - switch { - case node.Style&DoubleQuotedStyle != 0: - style = yaml_DOUBLE_QUOTED_SCALAR_STYLE - case node.Style&SingleQuotedStyle != 0: - style = yaml_SINGLE_QUOTED_SCALAR_STYLE - case node.Style&LiteralStyle != 0: - style = yaml_LITERAL_SCALAR_STYLE - case node.Style&FoldedStyle != 0: - style = yaml_FOLDED_SCALAR_STYLE - case strings.Contains(value, "\n"): - style = yaml_LITERAL_SCALAR_STYLE - case forceQuoting: - style = yaml_DOUBLE_QUOTED_SCALAR_STYLE - } - - e.emitScalar(value, node.Anchor, tag, style, []byte(node.HeadComment), []byte(node.LineComment), []byte(node.FootComment), []byte(tail)) - default: - failf("cannot encode node with unknown kind %d", node.Kind) - } -} diff --git a/vendor/go.yaml.in/yaml/v3/parserc.go b/vendor/go.yaml.in/yaml/v3/parserc.go deleted file mode 100644 index f35829db4..000000000 --- a/vendor/go.yaml.in/yaml/v3/parserc.go +++ /dev/null @@ -1,1260 +0,0 @@ -// -// Copyright (c) 2011-2019 Canonical Ltd -// Copyright (c) 2006-2010 Kirill Simonov -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package yaml - -import ( - "bytes" -) - -// The parser implements the following grammar: -// -// stream ::= STREAM-START implicit_document? explicit_document* STREAM-END -// implicit_document ::= block_node DOCUMENT-END* -// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* -// block_node_or_indentless_sequence ::= -// ALIAS -// | properties (block_content | indentless_block_sequence)? -// | block_content -// | indentless_block_sequence -// block_node ::= ALIAS -// | properties block_content? -// | block_content -// flow_node ::= ALIAS -// | properties flow_content? -// | flow_content -// properties ::= TAG ANCHOR? | ANCHOR TAG? -// block_content ::= block_collection | flow_collection | SCALAR -// flow_content ::= flow_collection | SCALAR -// block_collection ::= block_sequence | block_mapping -// flow_collection ::= flow_sequence | flow_mapping -// block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END -// indentless_sequence ::= (BLOCK-ENTRY block_node?)+ -// block_mapping ::= BLOCK-MAPPING_START -// ((KEY block_node_or_indentless_sequence?)? -// (VALUE block_node_or_indentless_sequence?)?)* -// BLOCK-END -// flow_sequence ::= FLOW-SEQUENCE-START -// (flow_sequence_entry FLOW-ENTRY)* -// flow_sequence_entry? -// FLOW-SEQUENCE-END -// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? -// flow_mapping ::= FLOW-MAPPING-START -// (flow_mapping_entry FLOW-ENTRY)* -// flow_mapping_entry? -// FLOW-MAPPING-END -// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? - -// Peek the next token in the token queue. -func peek_token(parser *yaml_parser_t) *yaml_token_t { - if parser.token_available || yaml_parser_fetch_more_tokens(parser) { - token := &parser.tokens[parser.tokens_head] - yaml_parser_unfold_comments(parser, token) - return token - } - return nil -} - -// yaml_parser_unfold_comments walks through the comments queue and joins all -// comments behind the position of the provided token into the respective -// top-level comment slices in the parser. -func yaml_parser_unfold_comments(parser *yaml_parser_t, token *yaml_token_t) { - for parser.comments_head < len(parser.comments) && token.start_mark.index >= parser.comments[parser.comments_head].token_mark.index { - comment := &parser.comments[parser.comments_head] - if len(comment.head) > 0 { - if token.typ == yaml_BLOCK_END_TOKEN { - // No heads on ends, so keep comment.head for a follow up token. - break - } - if len(parser.head_comment) > 0 { - parser.head_comment = append(parser.head_comment, '\n') - } - parser.head_comment = append(parser.head_comment, comment.head...) - } - if len(comment.foot) > 0 { - if len(parser.foot_comment) > 0 { - parser.foot_comment = append(parser.foot_comment, '\n') - } - parser.foot_comment = append(parser.foot_comment, comment.foot...) - } - if len(comment.line) > 0 { - if len(parser.line_comment) > 0 { - parser.line_comment = append(parser.line_comment, '\n') - } - parser.line_comment = append(parser.line_comment, comment.line...) - } - *comment = yaml_comment_t{} - parser.comments_head++ - } -} - -// Remove the next token from the queue (must be called after peek_token). -func skip_token(parser *yaml_parser_t) { - parser.token_available = false - parser.tokens_parsed++ - parser.stream_end_produced = parser.tokens[parser.tokens_head].typ == yaml_STREAM_END_TOKEN - parser.tokens_head++ -} - -// Get the next event. -func yaml_parser_parse(parser *yaml_parser_t, event *yaml_event_t) bool { - // Erase the event object. - *event = yaml_event_t{} - - // No events after the end of the stream or error. - if parser.stream_end_produced || parser.error != yaml_NO_ERROR || parser.state == yaml_PARSE_END_STATE { - return true - } - - // Generate the next event. - return yaml_parser_state_machine(parser, event) -} - -// Set parser error. -func yaml_parser_set_parser_error(parser *yaml_parser_t, problem string, problem_mark yaml_mark_t) bool { - parser.error = yaml_PARSER_ERROR - parser.problem = problem - parser.problem_mark = problem_mark - return false -} - -func yaml_parser_set_parser_error_context(parser *yaml_parser_t, context string, context_mark yaml_mark_t, problem string, problem_mark yaml_mark_t) bool { - parser.error = yaml_PARSER_ERROR - parser.context = context - parser.context_mark = context_mark - parser.problem = problem - parser.problem_mark = problem_mark - return false -} - -// State dispatcher. -func yaml_parser_state_machine(parser *yaml_parser_t, event *yaml_event_t) bool { - //trace("yaml_parser_state_machine", "state:", parser.state.String()) - - switch parser.state { - case yaml_PARSE_STREAM_START_STATE: - return yaml_parser_parse_stream_start(parser, event) - - case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE: - return yaml_parser_parse_document_start(parser, event, true) - - case yaml_PARSE_DOCUMENT_START_STATE: - return yaml_parser_parse_document_start(parser, event, false) - - case yaml_PARSE_DOCUMENT_CONTENT_STATE: - return yaml_parser_parse_document_content(parser, event) - - case yaml_PARSE_DOCUMENT_END_STATE: - return yaml_parser_parse_document_end(parser, event) - - case yaml_PARSE_BLOCK_NODE_STATE: - return yaml_parser_parse_node(parser, event, true, false) - - case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE: - return yaml_parser_parse_node(parser, event, true, true) - - case yaml_PARSE_FLOW_NODE_STATE: - return yaml_parser_parse_node(parser, event, false, false) - - case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE: - return yaml_parser_parse_block_sequence_entry(parser, event, true) - - case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE: - return yaml_parser_parse_block_sequence_entry(parser, event, false) - - case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE: - return yaml_parser_parse_indentless_sequence_entry(parser, event) - - case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE: - return yaml_parser_parse_block_mapping_key(parser, event, true) - - case yaml_PARSE_BLOCK_MAPPING_KEY_STATE: - return yaml_parser_parse_block_mapping_key(parser, event, false) - - case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE: - return yaml_parser_parse_block_mapping_value(parser, event) - - case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE: - return yaml_parser_parse_flow_sequence_entry(parser, event, true) - - case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE: - return yaml_parser_parse_flow_sequence_entry(parser, event, false) - - case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE: - return yaml_parser_parse_flow_sequence_entry_mapping_key(parser, event) - - case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE: - return yaml_parser_parse_flow_sequence_entry_mapping_value(parser, event) - - case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE: - return yaml_parser_parse_flow_sequence_entry_mapping_end(parser, event) - - case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE: - return yaml_parser_parse_flow_mapping_key(parser, event, true) - - case yaml_PARSE_FLOW_MAPPING_KEY_STATE: - return yaml_parser_parse_flow_mapping_key(parser, event, false) - - case yaml_PARSE_FLOW_MAPPING_VALUE_STATE: - return yaml_parser_parse_flow_mapping_value(parser, event, false) - - case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE: - return yaml_parser_parse_flow_mapping_value(parser, event, true) - - default: - panic("invalid parser state") - } -} - -// Parse the production: -// -// stream ::= STREAM-START implicit_document? explicit_document* STREAM-END -// ************ -func yaml_parser_parse_stream_start(parser *yaml_parser_t, event *yaml_event_t) bool { - token := peek_token(parser) - if token == nil { - return false - } - if token.typ != yaml_STREAM_START_TOKEN { - return yaml_parser_set_parser_error(parser, "did not find expected ", token.start_mark) - } - parser.state = yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE - *event = yaml_event_t{ - typ: yaml_STREAM_START_EVENT, - start_mark: token.start_mark, - end_mark: token.end_mark, - encoding: token.encoding, - } - skip_token(parser) - return true -} - -// Parse the productions: -// -// implicit_document ::= block_node DOCUMENT-END* -// * -// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* -// ************************* -func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t, implicit bool) bool { - - token := peek_token(parser) - if token == nil { - return false - } - - // Parse extra document end indicators. - if !implicit { - for token.typ == yaml_DOCUMENT_END_TOKEN { - skip_token(parser) - token = peek_token(parser) - if token == nil { - return false - } - } - } - - if implicit && token.typ != yaml_VERSION_DIRECTIVE_TOKEN && - token.typ != yaml_TAG_DIRECTIVE_TOKEN && - token.typ != yaml_DOCUMENT_START_TOKEN && - token.typ != yaml_STREAM_END_TOKEN { - // Parse an implicit document. - if !yaml_parser_process_directives(parser, nil, nil) { - return false - } - parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE) - parser.state = yaml_PARSE_BLOCK_NODE_STATE - - var head_comment []byte - if len(parser.head_comment) > 0 { - // [Go] Scan the header comment backwards, and if an empty line is found, break - // the header so the part before the last empty line goes into the - // document header, while the bottom of it goes into a follow up event. - for i := len(parser.head_comment) - 1; i > 0; i-- { - if parser.head_comment[i] == '\n' { - if i == len(parser.head_comment)-1 { - head_comment = parser.head_comment[:i] - parser.head_comment = parser.head_comment[i+1:] - break - } else if parser.head_comment[i-1] == '\n' { - head_comment = parser.head_comment[:i-1] - parser.head_comment = parser.head_comment[i+1:] - break - } - } - } - } - - *event = yaml_event_t{ - typ: yaml_DOCUMENT_START_EVENT, - start_mark: token.start_mark, - end_mark: token.end_mark, - - head_comment: head_comment, - } - - } else if token.typ != yaml_STREAM_END_TOKEN { - // Parse an explicit document. - var version_directive *yaml_version_directive_t - var tag_directives []yaml_tag_directive_t - start_mark := token.start_mark - if !yaml_parser_process_directives(parser, &version_directive, &tag_directives) { - return false - } - token = peek_token(parser) - if token == nil { - return false - } - if token.typ != yaml_DOCUMENT_START_TOKEN { - yaml_parser_set_parser_error(parser, - "did not find expected ", token.start_mark) - return false - } - parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE) - parser.state = yaml_PARSE_DOCUMENT_CONTENT_STATE - end_mark := token.end_mark - - *event = yaml_event_t{ - typ: yaml_DOCUMENT_START_EVENT, - start_mark: start_mark, - end_mark: end_mark, - version_directive: version_directive, - tag_directives: tag_directives, - implicit: false, - } - skip_token(parser) - - } else { - // Parse the stream end. - parser.state = yaml_PARSE_END_STATE - *event = yaml_event_t{ - typ: yaml_STREAM_END_EVENT, - start_mark: token.start_mark, - end_mark: token.end_mark, - } - skip_token(parser) - } - - return true -} - -// Parse the productions: -// -// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* -// *********** -func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event_t) bool { - token := peek_token(parser) - if token == nil { - return false - } - - if token.typ == yaml_VERSION_DIRECTIVE_TOKEN || - token.typ == yaml_TAG_DIRECTIVE_TOKEN || - token.typ == yaml_DOCUMENT_START_TOKEN || - token.typ == yaml_DOCUMENT_END_TOKEN || - token.typ == yaml_STREAM_END_TOKEN { - parser.state = parser.states[len(parser.states)-1] - parser.states = parser.states[:len(parser.states)-1] - return yaml_parser_process_empty_scalar(parser, event, - token.start_mark) - } - return yaml_parser_parse_node(parser, event, true, false) -} - -// Parse the productions: -// -// implicit_document ::= block_node DOCUMENT-END* -// ************* -// explicit_document ::= DIRECTIVE* DOCUMENT-START block_node? DOCUMENT-END* -func yaml_parser_parse_document_end(parser *yaml_parser_t, event *yaml_event_t) bool { - token := peek_token(parser) - if token == nil { - return false - } - - start_mark := token.start_mark - end_mark := token.start_mark - - implicit := true - if token.typ == yaml_DOCUMENT_END_TOKEN { - end_mark = token.end_mark - skip_token(parser) - implicit = false - } - - parser.tag_directives = parser.tag_directives[:0] - - parser.state = yaml_PARSE_DOCUMENT_START_STATE - *event = yaml_event_t{ - typ: yaml_DOCUMENT_END_EVENT, - start_mark: start_mark, - end_mark: end_mark, - implicit: implicit, - } - yaml_parser_set_event_comments(parser, event) - if len(event.head_comment) > 0 && len(event.foot_comment) == 0 { - event.foot_comment = event.head_comment - event.head_comment = nil - } - return true -} - -func yaml_parser_set_event_comments(parser *yaml_parser_t, event *yaml_event_t) { - event.head_comment = parser.head_comment - event.line_comment = parser.line_comment - event.foot_comment = parser.foot_comment - parser.head_comment = nil - parser.line_comment = nil - parser.foot_comment = nil - parser.tail_comment = nil - parser.stem_comment = nil -} - -// Parse the productions: -// -// block_node_or_indentless_sequence ::= -// ALIAS -// ***** -// | properties (block_content | indentless_block_sequence)? -// ********** * -// | block_content | indentless_block_sequence -// * -// block_node ::= ALIAS -// ***** -// | properties block_content? -// ********** * -// | block_content -// * -// flow_node ::= ALIAS -// ***** -// | properties flow_content? -// ********** * -// | flow_content -// * -// properties ::= TAG ANCHOR? | ANCHOR TAG? -// ************************* -// block_content ::= block_collection | flow_collection | SCALAR -// ****** -// flow_content ::= flow_collection | SCALAR -// ****** -func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, indentless_sequence bool) bool { - //defer trace("yaml_parser_parse_node", "block:", block, "indentless_sequence:", indentless_sequence)() - - token := peek_token(parser) - if token == nil { - return false - } - - if token.typ == yaml_ALIAS_TOKEN { - parser.state = parser.states[len(parser.states)-1] - parser.states = parser.states[:len(parser.states)-1] - *event = yaml_event_t{ - typ: yaml_ALIAS_EVENT, - start_mark: token.start_mark, - end_mark: token.end_mark, - anchor: token.value, - } - yaml_parser_set_event_comments(parser, event) - skip_token(parser) - return true - } - - start_mark := token.start_mark - end_mark := token.start_mark - - var tag_token bool - var tag_handle, tag_suffix, anchor []byte - var tag_mark yaml_mark_t - if token.typ == yaml_ANCHOR_TOKEN { - anchor = token.value - start_mark = token.start_mark - end_mark = token.end_mark - skip_token(parser) - token = peek_token(parser) - if token == nil { - return false - } - if token.typ == yaml_TAG_TOKEN { - tag_token = true - tag_handle = token.value - tag_suffix = token.suffix - tag_mark = token.start_mark - end_mark = token.end_mark - skip_token(parser) - token = peek_token(parser) - if token == nil { - return false - } - } - } else if token.typ == yaml_TAG_TOKEN { - tag_token = true - tag_handle = token.value - tag_suffix = token.suffix - start_mark = token.start_mark - tag_mark = token.start_mark - end_mark = token.end_mark - skip_token(parser) - token = peek_token(parser) - if token == nil { - return false - } - if token.typ == yaml_ANCHOR_TOKEN { - anchor = token.value - end_mark = token.end_mark - skip_token(parser) - token = peek_token(parser) - if token == nil { - return false - } - } - } - - var tag []byte - if tag_token { - if len(tag_handle) == 0 { - tag = tag_suffix - tag_suffix = nil - } else { - for i := range parser.tag_directives { - if bytes.Equal(parser.tag_directives[i].handle, tag_handle) { - tag = append([]byte(nil), parser.tag_directives[i].prefix...) - tag = append(tag, tag_suffix...) - break - } - } - if len(tag) == 0 { - yaml_parser_set_parser_error_context(parser, - "while parsing a node", start_mark, - "found undefined tag handle", tag_mark) - return false - } - } - } - - implicit := len(tag) == 0 - if indentless_sequence && token.typ == yaml_BLOCK_ENTRY_TOKEN { - end_mark = token.end_mark - parser.state = yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE - *event = yaml_event_t{ - typ: yaml_SEQUENCE_START_EVENT, - start_mark: start_mark, - end_mark: end_mark, - anchor: anchor, - tag: tag, - implicit: implicit, - style: yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE), - } - return true - } - if token.typ == yaml_SCALAR_TOKEN { - var plain_implicit, quoted_implicit bool - end_mark = token.end_mark - if (len(tag) == 0 && token.style == yaml_PLAIN_SCALAR_STYLE) || (len(tag) == 1 && tag[0] == '!') { - plain_implicit = true - } else if len(tag) == 0 { - quoted_implicit = true - } - parser.state = parser.states[len(parser.states)-1] - parser.states = parser.states[:len(parser.states)-1] - - *event = yaml_event_t{ - typ: yaml_SCALAR_EVENT, - start_mark: start_mark, - end_mark: end_mark, - anchor: anchor, - tag: tag, - value: token.value, - implicit: plain_implicit, - quoted_implicit: quoted_implicit, - style: yaml_style_t(token.style), - } - yaml_parser_set_event_comments(parser, event) - skip_token(parser) - return true - } - if token.typ == yaml_FLOW_SEQUENCE_START_TOKEN { - // [Go] Some of the events below can be merged as they differ only on style. - end_mark = token.end_mark - parser.state = yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE - *event = yaml_event_t{ - typ: yaml_SEQUENCE_START_EVENT, - start_mark: start_mark, - end_mark: end_mark, - anchor: anchor, - tag: tag, - implicit: implicit, - style: yaml_style_t(yaml_FLOW_SEQUENCE_STYLE), - } - yaml_parser_set_event_comments(parser, event) - return true - } - if token.typ == yaml_FLOW_MAPPING_START_TOKEN { - end_mark = token.end_mark - parser.state = yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE - *event = yaml_event_t{ - typ: yaml_MAPPING_START_EVENT, - start_mark: start_mark, - end_mark: end_mark, - anchor: anchor, - tag: tag, - implicit: implicit, - style: yaml_style_t(yaml_FLOW_MAPPING_STYLE), - } - yaml_parser_set_event_comments(parser, event) - return true - } - if block && token.typ == yaml_BLOCK_SEQUENCE_START_TOKEN { - end_mark = token.end_mark - parser.state = yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE - *event = yaml_event_t{ - typ: yaml_SEQUENCE_START_EVENT, - start_mark: start_mark, - end_mark: end_mark, - anchor: anchor, - tag: tag, - implicit: implicit, - style: yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE), - } - if parser.stem_comment != nil { - event.head_comment = parser.stem_comment - parser.stem_comment = nil - } - return true - } - if block && token.typ == yaml_BLOCK_MAPPING_START_TOKEN { - end_mark = token.end_mark - parser.state = yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE - *event = yaml_event_t{ - typ: yaml_MAPPING_START_EVENT, - start_mark: start_mark, - end_mark: end_mark, - anchor: anchor, - tag: tag, - implicit: implicit, - style: yaml_style_t(yaml_BLOCK_MAPPING_STYLE), - } - if parser.stem_comment != nil { - event.head_comment = parser.stem_comment - parser.stem_comment = nil - } - return true - } - if len(anchor) > 0 || len(tag) > 0 { - parser.state = parser.states[len(parser.states)-1] - parser.states = parser.states[:len(parser.states)-1] - - *event = yaml_event_t{ - typ: yaml_SCALAR_EVENT, - start_mark: start_mark, - end_mark: end_mark, - anchor: anchor, - tag: tag, - implicit: implicit, - quoted_implicit: false, - style: yaml_style_t(yaml_PLAIN_SCALAR_STYLE), - } - return true - } - - context := "while parsing a flow node" - if block { - context = "while parsing a block node" - } - yaml_parser_set_parser_error_context(parser, context, start_mark, - "did not find expected node content", token.start_mark) - return false -} - -// Parse the productions: -// -// block_sequence ::= BLOCK-SEQUENCE-START (BLOCK-ENTRY block_node?)* BLOCK-END -// ******************** *********** * ********* -func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { - if first { - token := peek_token(parser) - if token == nil { - return false - } - parser.marks = append(parser.marks, token.start_mark) - skip_token(parser) - } - - token := peek_token(parser) - if token == nil { - return false - } - - if token.typ == yaml_BLOCK_ENTRY_TOKEN { - mark := token.end_mark - prior_head_len := len(parser.head_comment) - skip_token(parser) - yaml_parser_split_stem_comment(parser, prior_head_len) - token = peek_token(parser) - if token == nil { - return false - } - if token.typ != yaml_BLOCK_ENTRY_TOKEN && token.typ != yaml_BLOCK_END_TOKEN { - parser.states = append(parser.states, yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE) - return yaml_parser_parse_node(parser, event, true, false) - } else { - parser.state = yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE - return yaml_parser_process_empty_scalar(parser, event, mark) - } - } - if token.typ == yaml_BLOCK_END_TOKEN { - parser.state = parser.states[len(parser.states)-1] - parser.states = parser.states[:len(parser.states)-1] - parser.marks = parser.marks[:len(parser.marks)-1] - - *event = yaml_event_t{ - typ: yaml_SEQUENCE_END_EVENT, - start_mark: token.start_mark, - end_mark: token.end_mark, - } - - skip_token(parser) - return true - } - - context_mark := parser.marks[len(parser.marks)-1] - parser.marks = parser.marks[:len(parser.marks)-1] - return yaml_parser_set_parser_error_context(parser, - "while parsing a block collection", context_mark, - "did not find expected '-' indicator", token.start_mark) -} - -// Parse the productions: -// -// indentless_sequence ::= (BLOCK-ENTRY block_node?)+ -// *********** * -func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *yaml_event_t) bool { - token := peek_token(parser) - if token == nil { - return false - } - - if token.typ == yaml_BLOCK_ENTRY_TOKEN { - mark := token.end_mark - prior_head_len := len(parser.head_comment) - skip_token(parser) - yaml_parser_split_stem_comment(parser, prior_head_len) - token = peek_token(parser) - if token == nil { - return false - } - if token.typ != yaml_BLOCK_ENTRY_TOKEN && - token.typ != yaml_KEY_TOKEN && - token.typ != yaml_VALUE_TOKEN && - token.typ != yaml_BLOCK_END_TOKEN { - parser.states = append(parser.states, yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE) - return yaml_parser_parse_node(parser, event, true, false) - } - parser.state = yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE - return yaml_parser_process_empty_scalar(parser, event, mark) - } - parser.state = parser.states[len(parser.states)-1] - parser.states = parser.states[:len(parser.states)-1] - - *event = yaml_event_t{ - typ: yaml_SEQUENCE_END_EVENT, - start_mark: token.start_mark, - end_mark: token.start_mark, // [Go] Shouldn't this be token.end_mark? - } - return true -} - -// Split stem comment from head comment. -// -// When a sequence or map is found under a sequence entry, the former head comment -// is assigned to the underlying sequence or map as a whole, not the individual -// sequence or map entry as would be expected otherwise. To handle this case the -// previous head comment is moved aside as the stem comment. -func yaml_parser_split_stem_comment(parser *yaml_parser_t, stem_len int) { - if stem_len == 0 { - return - } - - token := peek_token(parser) - if token == nil || token.typ != yaml_BLOCK_SEQUENCE_START_TOKEN && token.typ != yaml_BLOCK_MAPPING_START_TOKEN { - return - } - - parser.stem_comment = parser.head_comment[:stem_len] - if len(parser.head_comment) == stem_len { - parser.head_comment = nil - } else { - // Copy suffix to prevent very strange bugs if someone ever appends - // further bytes to the prefix in the stem_comment slice above. - parser.head_comment = append([]byte(nil), parser.head_comment[stem_len+1:]...) - } -} - -// Parse the productions: -// -// block_mapping ::= BLOCK-MAPPING_START -// ******************* -// ((KEY block_node_or_indentless_sequence?)? -// *** * -// (VALUE block_node_or_indentless_sequence?)?)* -// -// BLOCK-END -// ********* -func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { - if first { - token := peek_token(parser) - if token == nil { - return false - } - parser.marks = append(parser.marks, token.start_mark) - skip_token(parser) - } - - token := peek_token(parser) - if token == nil { - return false - } - - // [Go] A tail comment was left from the prior mapping value processed. Emit an event - // as it needs to be processed with that value and not the following key. - if len(parser.tail_comment) > 0 { - *event = yaml_event_t{ - typ: yaml_TAIL_COMMENT_EVENT, - start_mark: token.start_mark, - end_mark: token.end_mark, - foot_comment: parser.tail_comment, - } - parser.tail_comment = nil - return true - } - - if token.typ == yaml_KEY_TOKEN { - mark := token.end_mark - skip_token(parser) - token = peek_token(parser) - if token == nil { - return false - } - if token.typ != yaml_KEY_TOKEN && - token.typ != yaml_VALUE_TOKEN && - token.typ != yaml_BLOCK_END_TOKEN { - parser.states = append(parser.states, yaml_PARSE_BLOCK_MAPPING_VALUE_STATE) - return yaml_parser_parse_node(parser, event, true, true) - } else { - parser.state = yaml_PARSE_BLOCK_MAPPING_VALUE_STATE - return yaml_parser_process_empty_scalar(parser, event, mark) - } - } else if token.typ == yaml_BLOCK_END_TOKEN { - parser.state = parser.states[len(parser.states)-1] - parser.states = parser.states[:len(parser.states)-1] - parser.marks = parser.marks[:len(parser.marks)-1] - *event = yaml_event_t{ - typ: yaml_MAPPING_END_EVENT, - start_mark: token.start_mark, - end_mark: token.end_mark, - } - yaml_parser_set_event_comments(parser, event) - skip_token(parser) - return true - } - - context_mark := parser.marks[len(parser.marks)-1] - parser.marks = parser.marks[:len(parser.marks)-1] - return yaml_parser_set_parser_error_context(parser, - "while parsing a block mapping", context_mark, - "did not find expected key", token.start_mark) -} - -// Parse the productions: -// -// block_mapping ::= BLOCK-MAPPING_START -// -// ((KEY block_node_or_indentless_sequence?)? -// -// (VALUE block_node_or_indentless_sequence?)?)* -// ***** * -// BLOCK-END -func yaml_parser_parse_block_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool { - token := peek_token(parser) - if token == nil { - return false - } - if token.typ == yaml_VALUE_TOKEN { - mark := token.end_mark - skip_token(parser) - token = peek_token(parser) - if token == nil { - return false - } - if token.typ != yaml_KEY_TOKEN && - token.typ != yaml_VALUE_TOKEN && - token.typ != yaml_BLOCK_END_TOKEN { - parser.states = append(parser.states, yaml_PARSE_BLOCK_MAPPING_KEY_STATE) - return yaml_parser_parse_node(parser, event, true, true) - } - parser.state = yaml_PARSE_BLOCK_MAPPING_KEY_STATE - return yaml_parser_process_empty_scalar(parser, event, mark) - } - parser.state = yaml_PARSE_BLOCK_MAPPING_KEY_STATE - return yaml_parser_process_empty_scalar(parser, event, token.start_mark) -} - -// Parse the productions: -// -// flow_sequence ::= FLOW-SEQUENCE-START -// ******************* -// (flow_sequence_entry FLOW-ENTRY)* -// * ********** -// flow_sequence_entry? -// * -// FLOW-SEQUENCE-END -// ***************** -// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? -// * -func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { - if first { - token := peek_token(parser) - if token == nil { - return false - } - parser.marks = append(parser.marks, token.start_mark) - skip_token(parser) - } - token := peek_token(parser) - if token == nil { - return false - } - if token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { - if !first { - if token.typ == yaml_FLOW_ENTRY_TOKEN { - skip_token(parser) - token = peek_token(parser) - if token == nil { - return false - } - } else { - context_mark := parser.marks[len(parser.marks)-1] - parser.marks = parser.marks[:len(parser.marks)-1] - return yaml_parser_set_parser_error_context(parser, - "while parsing a flow sequence", context_mark, - "did not find expected ',' or ']'", token.start_mark) - } - } - - if token.typ == yaml_KEY_TOKEN { - parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE - *event = yaml_event_t{ - typ: yaml_MAPPING_START_EVENT, - start_mark: token.start_mark, - end_mark: token.end_mark, - implicit: true, - style: yaml_style_t(yaml_FLOW_MAPPING_STYLE), - } - skip_token(parser) - return true - } else if token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { - parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE) - return yaml_parser_parse_node(parser, event, false, false) - } - } - - parser.state = parser.states[len(parser.states)-1] - parser.states = parser.states[:len(parser.states)-1] - parser.marks = parser.marks[:len(parser.marks)-1] - - *event = yaml_event_t{ - typ: yaml_SEQUENCE_END_EVENT, - start_mark: token.start_mark, - end_mark: token.end_mark, - } - yaml_parser_set_event_comments(parser, event) - - skip_token(parser) - return true -} - -// Parse the productions: -// -// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? -// *** * -func yaml_parser_parse_flow_sequence_entry_mapping_key(parser *yaml_parser_t, event *yaml_event_t) bool { - token := peek_token(parser) - if token == nil { - return false - } - if token.typ != yaml_VALUE_TOKEN && - token.typ != yaml_FLOW_ENTRY_TOKEN && - token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { - parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE) - return yaml_parser_parse_node(parser, event, false, false) - } - mark := token.end_mark - skip_token(parser) - parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE - return yaml_parser_process_empty_scalar(parser, event, mark) -} - -// Parse the productions: -// -// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? -// ***** * -func yaml_parser_parse_flow_sequence_entry_mapping_value(parser *yaml_parser_t, event *yaml_event_t) bool { - token := peek_token(parser) - if token == nil { - return false - } - if token.typ == yaml_VALUE_TOKEN { - skip_token(parser) - token := peek_token(parser) - if token == nil { - return false - } - if token.typ != yaml_FLOW_ENTRY_TOKEN && token.typ != yaml_FLOW_SEQUENCE_END_TOKEN { - parser.states = append(parser.states, yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE) - return yaml_parser_parse_node(parser, event, false, false) - } - } - parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE - return yaml_parser_process_empty_scalar(parser, event, token.start_mark) -} - -// Parse the productions: -// -// flow_sequence_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? -// * -func yaml_parser_parse_flow_sequence_entry_mapping_end(parser *yaml_parser_t, event *yaml_event_t) bool { - token := peek_token(parser) - if token == nil { - return false - } - parser.state = yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE - *event = yaml_event_t{ - typ: yaml_MAPPING_END_EVENT, - start_mark: token.start_mark, - end_mark: token.start_mark, // [Go] Shouldn't this be end_mark? - } - return true -} - -// Parse the productions: -// -// flow_mapping ::= FLOW-MAPPING-START -// ****************** -// (flow_mapping_entry FLOW-ENTRY)* -// * ********** -// flow_mapping_entry? -// ****************** -// FLOW-MAPPING-END -// **************** -// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? -// * *** * -func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event_t, first bool) bool { - if first { - token := peek_token(parser) - parser.marks = append(parser.marks, token.start_mark) - skip_token(parser) - } - - token := peek_token(parser) - if token == nil { - return false - } - - if token.typ != yaml_FLOW_MAPPING_END_TOKEN { - if !first { - if token.typ == yaml_FLOW_ENTRY_TOKEN { - skip_token(parser) - token = peek_token(parser) - if token == nil { - return false - } - } else { - context_mark := parser.marks[len(parser.marks)-1] - parser.marks = parser.marks[:len(parser.marks)-1] - return yaml_parser_set_parser_error_context(parser, - "while parsing a flow mapping", context_mark, - "did not find expected ',' or '}'", token.start_mark) - } - } - - if token.typ == yaml_KEY_TOKEN { - skip_token(parser) - token = peek_token(parser) - if token == nil { - return false - } - if token.typ != yaml_VALUE_TOKEN && - token.typ != yaml_FLOW_ENTRY_TOKEN && - token.typ != yaml_FLOW_MAPPING_END_TOKEN { - parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_VALUE_STATE) - return yaml_parser_parse_node(parser, event, false, false) - } else { - parser.state = yaml_PARSE_FLOW_MAPPING_VALUE_STATE - return yaml_parser_process_empty_scalar(parser, event, token.start_mark) - } - } else if token.typ != yaml_FLOW_MAPPING_END_TOKEN { - parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE) - return yaml_parser_parse_node(parser, event, false, false) - } - } - - parser.state = parser.states[len(parser.states)-1] - parser.states = parser.states[:len(parser.states)-1] - parser.marks = parser.marks[:len(parser.marks)-1] - *event = yaml_event_t{ - typ: yaml_MAPPING_END_EVENT, - start_mark: token.start_mark, - end_mark: token.end_mark, - } - yaml_parser_set_event_comments(parser, event) - skip_token(parser) - return true -} - -// Parse the productions: -// -// flow_mapping_entry ::= flow_node | KEY flow_node? (VALUE flow_node?)? -// * ***** * -func yaml_parser_parse_flow_mapping_value(parser *yaml_parser_t, event *yaml_event_t, empty bool) bool { - token := peek_token(parser) - if token == nil { - return false - } - if empty { - parser.state = yaml_PARSE_FLOW_MAPPING_KEY_STATE - return yaml_parser_process_empty_scalar(parser, event, token.start_mark) - } - if token.typ == yaml_VALUE_TOKEN { - skip_token(parser) - token = peek_token(parser) - if token == nil { - return false - } - if token.typ != yaml_FLOW_ENTRY_TOKEN && token.typ != yaml_FLOW_MAPPING_END_TOKEN { - parser.states = append(parser.states, yaml_PARSE_FLOW_MAPPING_KEY_STATE) - return yaml_parser_parse_node(parser, event, false, false) - } - } - parser.state = yaml_PARSE_FLOW_MAPPING_KEY_STATE - return yaml_parser_process_empty_scalar(parser, event, token.start_mark) -} - -// Generate an empty scalar event. -func yaml_parser_process_empty_scalar(parser *yaml_parser_t, event *yaml_event_t, mark yaml_mark_t) bool { - *event = yaml_event_t{ - typ: yaml_SCALAR_EVENT, - start_mark: mark, - end_mark: mark, - value: nil, // Empty - implicit: true, - style: yaml_style_t(yaml_PLAIN_SCALAR_STYLE), - } - return true -} - -var default_tag_directives = []yaml_tag_directive_t{ - {[]byte("!"), []byte("!")}, - {[]byte("!!"), []byte("tag:yaml.org,2002:")}, -} - -// Parse directives. -func yaml_parser_process_directives(parser *yaml_parser_t, - version_directive_ref **yaml_version_directive_t, - tag_directives_ref *[]yaml_tag_directive_t) bool { - - var version_directive *yaml_version_directive_t - var tag_directives []yaml_tag_directive_t - - token := peek_token(parser) - if token == nil { - return false - } - - for token.typ == yaml_VERSION_DIRECTIVE_TOKEN || token.typ == yaml_TAG_DIRECTIVE_TOKEN { - if token.typ == yaml_VERSION_DIRECTIVE_TOKEN { - if version_directive != nil { - yaml_parser_set_parser_error(parser, - "found duplicate %YAML directive", token.start_mark) - return false - } - if token.major != 1 || token.minor != 1 { - yaml_parser_set_parser_error(parser, - "found incompatible YAML document", token.start_mark) - return false - } - version_directive = &yaml_version_directive_t{ - major: token.major, - minor: token.minor, - } - } else if token.typ == yaml_TAG_DIRECTIVE_TOKEN { - value := yaml_tag_directive_t{ - handle: token.value, - prefix: token.prefix, - } - if !yaml_parser_append_tag_directive(parser, value, false, token.start_mark) { - return false - } - tag_directives = append(tag_directives, value) - } - - skip_token(parser) - token = peek_token(parser) - if token == nil { - return false - } - } - - for i := range default_tag_directives { - if !yaml_parser_append_tag_directive(parser, default_tag_directives[i], true, token.start_mark) { - return false - } - } - - if version_directive_ref != nil { - *version_directive_ref = version_directive - } - if tag_directives_ref != nil { - *tag_directives_ref = tag_directives - } - return true -} - -// Append a tag directive to the directives stack. -func yaml_parser_append_tag_directive(parser *yaml_parser_t, value yaml_tag_directive_t, allow_duplicates bool, mark yaml_mark_t) bool { - for i := range parser.tag_directives { - if bytes.Equal(value.handle, parser.tag_directives[i].handle) { - if allow_duplicates { - return true - } - return yaml_parser_set_parser_error(parser, "found duplicate %TAG directive", mark) - } - } - - // [Go] I suspect the copy is unnecessary. This was likely done - // because there was no way to track ownership of the data. - value_copy := yaml_tag_directive_t{ - handle: make([]byte, len(value.handle)), - prefix: make([]byte, len(value.prefix)), - } - copy(value_copy.handle, value.handle) - copy(value_copy.prefix, value.prefix) - parser.tag_directives = append(parser.tag_directives, value_copy) - return true -} diff --git a/vendor/go.yaml.in/yaml/v3/readerc.go b/vendor/go.yaml.in/yaml/v3/readerc.go deleted file mode 100644 index 56af24536..000000000 --- a/vendor/go.yaml.in/yaml/v3/readerc.go +++ /dev/null @@ -1,434 +0,0 @@ -// -// Copyright (c) 2011-2019 Canonical Ltd -// Copyright (c) 2006-2010 Kirill Simonov -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package yaml - -import ( - "io" -) - -// Set the reader error and return 0. -func yaml_parser_set_reader_error(parser *yaml_parser_t, problem string, offset int, value int) bool { - parser.error = yaml_READER_ERROR - parser.problem = problem - parser.problem_offset = offset - parser.problem_value = value - return false -} - -// Byte order marks. -const ( - bom_UTF8 = "\xef\xbb\xbf" - bom_UTF16LE = "\xff\xfe" - bom_UTF16BE = "\xfe\xff" -) - -// Determine the input stream encoding by checking the BOM symbol. If no BOM is -// found, the UTF-8 encoding is assumed. Return 1 on success, 0 on failure. -func yaml_parser_determine_encoding(parser *yaml_parser_t) bool { - // Ensure that we had enough bytes in the raw buffer. - for !parser.eof && len(parser.raw_buffer)-parser.raw_buffer_pos < 3 { - if !yaml_parser_update_raw_buffer(parser) { - return false - } - } - - // Determine the encoding. - buf := parser.raw_buffer - pos := parser.raw_buffer_pos - avail := len(buf) - pos - if avail >= 2 && buf[pos] == bom_UTF16LE[0] && buf[pos+1] == bom_UTF16LE[1] { - parser.encoding = yaml_UTF16LE_ENCODING - parser.raw_buffer_pos += 2 - parser.offset += 2 - } else if avail >= 2 && buf[pos] == bom_UTF16BE[0] && buf[pos+1] == bom_UTF16BE[1] { - parser.encoding = yaml_UTF16BE_ENCODING - parser.raw_buffer_pos += 2 - parser.offset += 2 - } else if avail >= 3 && buf[pos] == bom_UTF8[0] && buf[pos+1] == bom_UTF8[1] && buf[pos+2] == bom_UTF8[2] { - parser.encoding = yaml_UTF8_ENCODING - parser.raw_buffer_pos += 3 - parser.offset += 3 - } else { - parser.encoding = yaml_UTF8_ENCODING - } - return true -} - -// Update the raw buffer. -func yaml_parser_update_raw_buffer(parser *yaml_parser_t) bool { - size_read := 0 - - // Return if the raw buffer is full. - if parser.raw_buffer_pos == 0 && len(parser.raw_buffer) == cap(parser.raw_buffer) { - return true - } - - // Return on EOF. - if parser.eof { - return true - } - - // Move the remaining bytes in the raw buffer to the beginning. - if parser.raw_buffer_pos > 0 && parser.raw_buffer_pos < len(parser.raw_buffer) { - copy(parser.raw_buffer, parser.raw_buffer[parser.raw_buffer_pos:]) - } - parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)-parser.raw_buffer_pos] - parser.raw_buffer_pos = 0 - - // Call the read handler to fill the buffer. - size_read, err := parser.read_handler(parser, parser.raw_buffer[len(parser.raw_buffer):cap(parser.raw_buffer)]) - parser.raw_buffer = parser.raw_buffer[:len(parser.raw_buffer)+size_read] - if err == io.EOF { - parser.eof = true - } else if err != nil { - return yaml_parser_set_reader_error(parser, "input error: "+err.Error(), parser.offset, -1) - } - return true -} - -// Ensure that the buffer contains at least `length` characters. -// Return true on success, false on failure. -// -// The length is supposed to be significantly less that the buffer size. -func yaml_parser_update_buffer(parser *yaml_parser_t, length int) bool { - if parser.read_handler == nil { - panic("read handler must be set") - } - - // [Go] This function was changed to guarantee the requested length size at EOF. - // The fact we need to do this is pretty awful, but the description above implies - // for that to be the case, and there are tests - - // If the EOF flag is set and the raw buffer is empty, do nothing. - if parser.eof && parser.raw_buffer_pos == len(parser.raw_buffer) { - // [Go] ACTUALLY! Read the documentation of this function above. - // This is just broken. To return true, we need to have the - // given length in the buffer. Not doing that means every single - // check that calls this function to make sure the buffer has a - // given length is Go) panicking; or C) accessing invalid memory. - //return true - } - - // Return if the buffer contains enough characters. - if parser.unread >= length { - return true - } - - // Determine the input encoding if it is not known yet. - if parser.encoding == yaml_ANY_ENCODING { - if !yaml_parser_determine_encoding(parser) { - return false - } - } - - // Move the unread characters to the beginning of the buffer. - buffer_len := len(parser.buffer) - if parser.buffer_pos > 0 && parser.buffer_pos < buffer_len { - copy(parser.buffer, parser.buffer[parser.buffer_pos:]) - buffer_len -= parser.buffer_pos - parser.buffer_pos = 0 - } else if parser.buffer_pos == buffer_len { - buffer_len = 0 - parser.buffer_pos = 0 - } - - // Open the whole buffer for writing, and cut it before returning. - parser.buffer = parser.buffer[:cap(parser.buffer)] - - // Fill the buffer until it has enough characters. - first := true - for parser.unread < length { - - // Fill the raw buffer if necessary. - if !first || parser.raw_buffer_pos == len(parser.raw_buffer) { - if !yaml_parser_update_raw_buffer(parser) { - parser.buffer = parser.buffer[:buffer_len] - return false - } - } - first = false - - // Decode the raw buffer. - inner: - for parser.raw_buffer_pos != len(parser.raw_buffer) { - var value rune - var width int - - raw_unread := len(parser.raw_buffer) - parser.raw_buffer_pos - - // Decode the next character. - switch parser.encoding { - case yaml_UTF8_ENCODING: - // Decode a UTF-8 character. Check RFC 3629 - // (http://www.ietf.org/rfc/rfc3629.txt) for more details. - // - // The following table (taken from the RFC) is used for - // decoding. - // - // Char. number range | UTF-8 octet sequence - // (hexadecimal) | (binary) - // --------------------+------------------------------------ - // 0000 0000-0000 007F | 0xxxxxxx - // 0000 0080-0000 07FF | 110xxxxx 10xxxxxx - // 0000 0800-0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx - // 0001 0000-0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - // - // Additionally, the characters in the range 0xD800-0xDFFF - // are prohibited as they are reserved for use with UTF-16 - // surrogate pairs. - - // Determine the length of the UTF-8 sequence. - octet := parser.raw_buffer[parser.raw_buffer_pos] - switch { - case octet&0x80 == 0x00: - width = 1 - case octet&0xE0 == 0xC0: - width = 2 - case octet&0xF0 == 0xE0: - width = 3 - case octet&0xF8 == 0xF0: - width = 4 - default: - // The leading octet is invalid. - return yaml_parser_set_reader_error(parser, - "invalid leading UTF-8 octet", - parser.offset, int(octet)) - } - - // Check if the raw buffer contains an incomplete character. - if width > raw_unread { - if parser.eof { - return yaml_parser_set_reader_error(parser, - "incomplete UTF-8 octet sequence", - parser.offset, -1) - } - break inner - } - - // Decode the leading octet. - switch { - case octet&0x80 == 0x00: - value = rune(octet & 0x7F) - case octet&0xE0 == 0xC0: - value = rune(octet & 0x1F) - case octet&0xF0 == 0xE0: - value = rune(octet & 0x0F) - case octet&0xF8 == 0xF0: - value = rune(octet & 0x07) - default: - value = 0 - } - - // Check and decode the trailing octets. - for k := 1; k < width; k++ { - octet = parser.raw_buffer[parser.raw_buffer_pos+k] - - // Check if the octet is valid. - if (octet & 0xC0) != 0x80 { - return yaml_parser_set_reader_error(parser, - "invalid trailing UTF-8 octet", - parser.offset+k, int(octet)) - } - - // Decode the octet. - value = (value << 6) + rune(octet&0x3F) - } - - // Check the length of the sequence against the value. - switch { - case width == 1: - case width == 2 && value >= 0x80: - case width == 3 && value >= 0x800: - case width == 4 && value >= 0x10000: - default: - return yaml_parser_set_reader_error(parser, - "invalid length of a UTF-8 sequence", - parser.offset, -1) - } - - // Check the range of the value. - if value >= 0xD800 && value <= 0xDFFF || value > 0x10FFFF { - return yaml_parser_set_reader_error(parser, - "invalid Unicode character", - parser.offset, int(value)) - } - - case yaml_UTF16LE_ENCODING, yaml_UTF16BE_ENCODING: - var low, high int - if parser.encoding == yaml_UTF16LE_ENCODING { - low, high = 0, 1 - } else { - low, high = 1, 0 - } - - // The UTF-16 encoding is not as simple as one might - // naively think. Check RFC 2781 - // (http://www.ietf.org/rfc/rfc2781.txt). - // - // Normally, two subsequent bytes describe a Unicode - // character. However a special technique (called a - // surrogate pair) is used for specifying character - // values larger than 0xFFFF. - // - // A surrogate pair consists of two pseudo-characters: - // high surrogate area (0xD800-0xDBFF) - // low surrogate area (0xDC00-0xDFFF) - // - // The following formulas are used for decoding - // and encoding characters using surrogate pairs: - // - // U = U' + 0x10000 (0x01 00 00 <= U <= 0x10 FF FF) - // U' = yyyyyyyyyyxxxxxxxxxx (0 <= U' <= 0x0F FF FF) - // W1 = 110110yyyyyyyyyy - // W2 = 110111xxxxxxxxxx - // - // where U is the character value, W1 is the high surrogate - // area, W2 is the low surrogate area. - - // Check for incomplete UTF-16 character. - if raw_unread < 2 { - if parser.eof { - return yaml_parser_set_reader_error(parser, - "incomplete UTF-16 character", - parser.offset, -1) - } - break inner - } - - // Get the character. - value = rune(parser.raw_buffer[parser.raw_buffer_pos+low]) + - (rune(parser.raw_buffer[parser.raw_buffer_pos+high]) << 8) - - // Check for unexpected low surrogate area. - if value&0xFC00 == 0xDC00 { - return yaml_parser_set_reader_error(parser, - "unexpected low surrogate area", - parser.offset, int(value)) - } - - // Check for a high surrogate area. - if value&0xFC00 == 0xD800 { - width = 4 - - // Check for incomplete surrogate pair. - if raw_unread < 4 { - if parser.eof { - return yaml_parser_set_reader_error(parser, - "incomplete UTF-16 surrogate pair", - parser.offset, -1) - } - break inner - } - - // Get the next character. - value2 := rune(parser.raw_buffer[parser.raw_buffer_pos+low+2]) + - (rune(parser.raw_buffer[parser.raw_buffer_pos+high+2]) << 8) - - // Check for a low surrogate area. - if value2&0xFC00 != 0xDC00 { - return yaml_parser_set_reader_error(parser, - "expected low surrogate area", - parser.offset+2, int(value2)) - } - - // Generate the value of the surrogate pair. - value = 0x10000 + ((value & 0x3FF) << 10) + (value2 & 0x3FF) - } else { - width = 2 - } - - default: - panic("impossible") - } - - // Check if the character is in the allowed range: - // #x9 | #xA | #xD | [#x20-#x7E] (8 bit) - // | #x85 | [#xA0-#xD7FF] | [#xE000-#xFFFD] (16 bit) - // | [#x10000-#x10FFFF] (32 bit) - switch { - case value == 0x09: - case value == 0x0A: - case value == 0x0D: - case value >= 0x20 && value <= 0x7E: - case value == 0x85: - case value >= 0xA0 && value <= 0xD7FF: - case value >= 0xE000 && value <= 0xFFFD: - case value >= 0x10000 && value <= 0x10FFFF: - default: - return yaml_parser_set_reader_error(parser, - "control characters are not allowed", - parser.offset, int(value)) - } - - // Move the raw pointers. - parser.raw_buffer_pos += width - parser.offset += width - - // Finally put the character into the buffer. - if value <= 0x7F { - // 0000 0000-0000 007F . 0xxxxxxx - parser.buffer[buffer_len+0] = byte(value) - buffer_len += 1 - } else if value <= 0x7FF { - // 0000 0080-0000 07FF . 110xxxxx 10xxxxxx - parser.buffer[buffer_len+0] = byte(0xC0 + (value >> 6)) - parser.buffer[buffer_len+1] = byte(0x80 + (value & 0x3F)) - buffer_len += 2 - } else if value <= 0xFFFF { - // 0000 0800-0000 FFFF . 1110xxxx 10xxxxxx 10xxxxxx - parser.buffer[buffer_len+0] = byte(0xE0 + (value >> 12)) - parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 6) & 0x3F)) - parser.buffer[buffer_len+2] = byte(0x80 + (value & 0x3F)) - buffer_len += 3 - } else { - // 0001 0000-0010 FFFF . 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - parser.buffer[buffer_len+0] = byte(0xF0 + (value >> 18)) - parser.buffer[buffer_len+1] = byte(0x80 + ((value >> 12) & 0x3F)) - parser.buffer[buffer_len+2] = byte(0x80 + ((value >> 6) & 0x3F)) - parser.buffer[buffer_len+3] = byte(0x80 + (value & 0x3F)) - buffer_len += 4 - } - - parser.unread++ - } - - // On EOF, put NUL into the buffer and return. - if parser.eof { - parser.buffer[buffer_len] = 0 - buffer_len++ - parser.unread++ - break - } - } - // [Go] Read the documentation of this function above. To return true, - // we need to have the given length in the buffer. Not doing that means - // every single check that calls this function to make sure the buffer - // has a given length is Go) panicking; or C) accessing invalid memory. - // This happens here due to the EOF above breaking early. - for buffer_len < length { - parser.buffer[buffer_len] = 0 - buffer_len++ - } - parser.buffer = parser.buffer[:buffer_len] - return true -} diff --git a/vendor/go.yaml.in/yaml/v3/resolve.go b/vendor/go.yaml.in/yaml/v3/resolve.go deleted file mode 100644 index 64ae88805..000000000 --- a/vendor/go.yaml.in/yaml/v3/resolve.go +++ /dev/null @@ -1,326 +0,0 @@ -// -// Copyright (c) 2011-2019 Canonical Ltd -// -// 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 yaml - -import ( - "encoding/base64" - "math" - "regexp" - "strconv" - "strings" - "time" -) - -type resolveMapItem struct { - value interface{} - tag string -} - -var resolveTable = make([]byte, 256) -var resolveMap = make(map[string]resolveMapItem) - -func init() { - t := resolveTable - t[int('+')] = 'S' // Sign - t[int('-')] = 'S' - for _, c := range "0123456789" { - t[int(c)] = 'D' // Digit - } - for _, c := range "yYnNtTfFoO~" { - t[int(c)] = 'M' // In map - } - t[int('.')] = '.' // Float (potentially in map) - - var resolveMapList = []struct { - v interface{} - tag string - l []string - }{ - {true, boolTag, []string{"true", "True", "TRUE"}}, - {false, boolTag, []string{"false", "False", "FALSE"}}, - {nil, nullTag, []string{"", "~", "null", "Null", "NULL"}}, - {math.NaN(), floatTag, []string{".nan", ".NaN", ".NAN"}}, - {math.Inf(+1), floatTag, []string{".inf", ".Inf", ".INF"}}, - {math.Inf(+1), floatTag, []string{"+.inf", "+.Inf", "+.INF"}}, - {math.Inf(-1), floatTag, []string{"-.inf", "-.Inf", "-.INF"}}, - {"<<", mergeTag, []string{"<<"}}, - } - - m := resolveMap - for _, item := range resolveMapList { - for _, s := range item.l { - m[s] = resolveMapItem{item.v, item.tag} - } - } -} - -const ( - nullTag = "!!null" - boolTag = "!!bool" - strTag = "!!str" - intTag = "!!int" - floatTag = "!!float" - timestampTag = "!!timestamp" - seqTag = "!!seq" - mapTag = "!!map" - binaryTag = "!!binary" - mergeTag = "!!merge" -) - -var longTags = make(map[string]string) -var shortTags = make(map[string]string) - -func init() { - for _, stag := range []string{nullTag, boolTag, strTag, intTag, floatTag, timestampTag, seqTag, mapTag, binaryTag, mergeTag} { - ltag := longTag(stag) - longTags[stag] = ltag - shortTags[ltag] = stag - } -} - -const longTagPrefix = "tag:yaml.org,2002:" - -func shortTag(tag string) string { - if strings.HasPrefix(tag, longTagPrefix) { - if stag, ok := shortTags[tag]; ok { - return stag - } - return "!!" + tag[len(longTagPrefix):] - } - return tag -} - -func longTag(tag string) string { - if strings.HasPrefix(tag, "!!") { - if ltag, ok := longTags[tag]; ok { - return ltag - } - return longTagPrefix + tag[2:] - } - return tag -} - -func resolvableTag(tag string) bool { - switch tag { - case "", strTag, boolTag, intTag, floatTag, nullTag, timestampTag: - return true - } - return false -} - -var yamlStyleFloat = regexp.MustCompile(`^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$`) - -func resolve(tag string, in string) (rtag string, out interface{}) { - tag = shortTag(tag) - if !resolvableTag(tag) { - return tag, in - } - - defer func() { - switch tag { - case "", rtag, strTag, binaryTag: - return - case floatTag: - if rtag == intTag { - switch v := out.(type) { - case int64: - rtag = floatTag - out = float64(v) - return - case int: - rtag = floatTag - out = float64(v) - return - } - } - } - failf("cannot decode %s `%s` as a %s", shortTag(rtag), in, shortTag(tag)) - }() - - // Any data is accepted as a !!str or !!binary. - // Otherwise, the prefix is enough of a hint about what it might be. - hint := byte('N') - if in != "" { - hint = resolveTable[in[0]] - } - if hint != 0 && tag != strTag && tag != binaryTag { - // Handle things we can lookup in a map. - if item, ok := resolveMap[in]; ok { - return item.tag, item.value - } - - // Base 60 floats are a bad idea, were dropped in YAML 1.2, and - // are purposefully unsupported here. They're still quoted on - // the way out for compatibility with other parser, though. - - switch hint { - case 'M': - // We've already checked the map above. - - case '.': - // Not in the map, so maybe a normal float. - floatv, err := strconv.ParseFloat(in, 64) - if err == nil { - return floatTag, floatv - } - - case 'D', 'S': - // Int, float, or timestamp. - // Only try values as a timestamp if the value is unquoted or there's an explicit - // !!timestamp tag. - if tag == "" || tag == timestampTag { - t, ok := parseTimestamp(in) - if ok { - return timestampTag, t - } - } - - plain := strings.Replace(in, "_", "", -1) - intv, err := strconv.ParseInt(plain, 0, 64) - if err == nil { - if intv == int64(int(intv)) { - return intTag, int(intv) - } else { - return intTag, intv - } - } - uintv, err := strconv.ParseUint(plain, 0, 64) - if err == nil { - return intTag, uintv - } - if yamlStyleFloat.MatchString(plain) { - floatv, err := strconv.ParseFloat(plain, 64) - if err == nil { - return floatTag, floatv - } - } - if strings.HasPrefix(plain, "0b") { - intv, err := strconv.ParseInt(plain[2:], 2, 64) - if err == nil { - if intv == int64(int(intv)) { - return intTag, int(intv) - } else { - return intTag, intv - } - } - uintv, err := strconv.ParseUint(plain[2:], 2, 64) - if err == nil { - return intTag, uintv - } - } else if strings.HasPrefix(plain, "-0b") { - intv, err := strconv.ParseInt("-"+plain[3:], 2, 64) - if err == nil { - if true || intv == int64(int(intv)) { - return intTag, int(intv) - } else { - return intTag, intv - } - } - } - // Octals as introduced in version 1.2 of the spec. - // Octals from the 1.1 spec, spelled as 0777, are still - // decoded by default in v3 as well for compatibility. - // May be dropped in v4 depending on how usage evolves. - if strings.HasPrefix(plain, "0o") { - intv, err := strconv.ParseInt(plain[2:], 8, 64) - if err == nil { - if intv == int64(int(intv)) { - return intTag, int(intv) - } else { - return intTag, intv - } - } - uintv, err := strconv.ParseUint(plain[2:], 8, 64) - if err == nil { - return intTag, uintv - } - } else if strings.HasPrefix(plain, "-0o") { - intv, err := strconv.ParseInt("-"+plain[3:], 8, 64) - if err == nil { - if true || intv == int64(int(intv)) { - return intTag, int(intv) - } else { - return intTag, intv - } - } - } - default: - panic("internal error: missing handler for resolver table: " + string(rune(hint)) + " (with " + in + ")") - } - } - return strTag, in -} - -// encodeBase64 encodes s as base64 that is broken up into multiple lines -// as appropriate for the resulting length. -func encodeBase64(s string) string { - const lineLen = 70 - encLen := base64.StdEncoding.EncodedLen(len(s)) - lines := encLen/lineLen + 1 - buf := make([]byte, encLen*2+lines) - in := buf[0:encLen] - out := buf[encLen:] - base64.StdEncoding.Encode(in, []byte(s)) - k := 0 - for i := 0; i < len(in); i += lineLen { - j := i + lineLen - if j > len(in) { - j = len(in) - } - k += copy(out[k:], in[i:j]) - if lines > 1 { - out[k] = '\n' - k++ - } - } - return string(out[:k]) -} - -// This is a subset of the formats allowed by the regular expression -// defined at http://yaml.org/type/timestamp.html. -var allowedTimestampFormats = []string{ - "2006-1-2T15:4:5.999999999Z07:00", // RCF3339Nano with short date fields. - "2006-1-2t15:4:5.999999999Z07:00", // RFC3339Nano with short date fields and lower-case "t". - "2006-1-2 15:4:5.999999999", // space separated with no time zone - "2006-1-2", // date only - // Notable exception: time.Parse cannot handle: "2001-12-14 21:59:43.10 -5" - // from the set of examples. -} - -// parseTimestamp parses s as a timestamp string and -// returns the timestamp and reports whether it succeeded. -// Timestamp formats are defined at http://yaml.org/type/timestamp.html -func parseTimestamp(s string) (time.Time, bool) { - // TODO write code to check all the formats supported by - // http://yaml.org/type/timestamp.html instead of using time.Parse. - - // Quick check: all date formats start with YYYY-. - i := 0 - for ; i < len(s); i++ { - if c := s[i]; c < '0' || c > '9' { - break - } - } - if i != 4 || i == len(s) || s[i] != '-' { - return time.Time{}, false - } - for _, format := range allowedTimestampFormats { - if t, err := time.Parse(format, s); err == nil { - return t, true - } - } - return time.Time{}, false -} diff --git a/vendor/go.yaml.in/yaml/v3/scannerc.go b/vendor/go.yaml.in/yaml/v3/scannerc.go deleted file mode 100644 index 30b1f0892..000000000 --- a/vendor/go.yaml.in/yaml/v3/scannerc.go +++ /dev/null @@ -1,3040 +0,0 @@ -// -// Copyright (c) 2011-2019 Canonical Ltd -// Copyright (c) 2006-2010 Kirill Simonov -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package yaml - -import ( - "bytes" - "fmt" -) - -// Introduction -// ************ -// -// The following notes assume that you are familiar with the YAML specification -// (http://yaml.org/spec/1.2/spec.html). We mostly follow it, although in -// some cases we are less restrictive that it requires. -// -// The process of transforming a YAML stream into a sequence of events is -// divided on two steps: Scanning and Parsing. -// -// The Scanner transforms the input stream into a sequence of tokens, while the -// parser transform the sequence of tokens produced by the Scanner into a -// sequence of parsing events. -// -// The Scanner is rather clever and complicated. The Parser, on the contrary, -// is a straightforward implementation of a recursive-descendant parser (or, -// LL(1) parser, as it is usually called). -// -// Actually there are two issues of Scanning that might be called "clever", the -// rest is quite straightforward. The issues are "block collection start" and -// "simple keys". Both issues are explained below in details. -// -// Here the Scanning step is explained and implemented. We start with the list -// of all the tokens produced by the Scanner together with short descriptions. -// -// Now, tokens: -// -// STREAM-START(encoding) # The stream start. -// STREAM-END # The stream end. -// VERSION-DIRECTIVE(major,minor) # The '%YAML' directive. -// TAG-DIRECTIVE(handle,prefix) # The '%TAG' directive. -// DOCUMENT-START # '---' -// DOCUMENT-END # '...' -// BLOCK-SEQUENCE-START # Indentation increase denoting a block -// BLOCK-MAPPING-START # sequence or a block mapping. -// BLOCK-END # Indentation decrease. -// FLOW-SEQUENCE-START # '[' -// FLOW-SEQUENCE-END # ']' -// BLOCK-SEQUENCE-START # '{' -// BLOCK-SEQUENCE-END # '}' -// BLOCK-ENTRY # '-' -// FLOW-ENTRY # ',' -// KEY # '?' or nothing (simple keys). -// VALUE # ':' -// ALIAS(anchor) # '*anchor' -// ANCHOR(anchor) # '&anchor' -// TAG(handle,suffix) # '!handle!suffix' -// SCALAR(value,style) # A scalar. -// -// The following two tokens are "virtual" tokens denoting the beginning and the -// end of the stream: -// -// STREAM-START(encoding) -// STREAM-END -// -// We pass the information about the input stream encoding with the -// STREAM-START token. -// -// The next two tokens are responsible for tags: -// -// VERSION-DIRECTIVE(major,minor) -// TAG-DIRECTIVE(handle,prefix) -// -// Example: -// -// %YAML 1.1 -// %TAG ! !foo -// %TAG !yaml! tag:yaml.org,2002: -// --- -// -// The correspoding sequence of tokens: -// -// STREAM-START(utf-8) -// VERSION-DIRECTIVE(1,1) -// TAG-DIRECTIVE("!","!foo") -// TAG-DIRECTIVE("!yaml","tag:yaml.org,2002:") -// DOCUMENT-START -// STREAM-END -// -// Note that the VERSION-DIRECTIVE and TAG-DIRECTIVE tokens occupy a whole -// line. -// -// The document start and end indicators are represented by: -// -// DOCUMENT-START -// DOCUMENT-END -// -// Note that if a YAML stream contains an implicit document (without '---' -// and '...' indicators), no DOCUMENT-START and DOCUMENT-END tokens will be -// produced. -// -// In the following examples, we present whole documents together with the -// produced tokens. -// -// 1. An implicit document: -// -// 'a scalar' -// -// Tokens: -// -// STREAM-START(utf-8) -// SCALAR("a scalar",single-quoted) -// STREAM-END -// -// 2. An explicit document: -// -// --- -// 'a scalar' -// ... -// -// Tokens: -// -// STREAM-START(utf-8) -// DOCUMENT-START -// SCALAR("a scalar",single-quoted) -// DOCUMENT-END -// STREAM-END -// -// 3. Several documents in a stream: -// -// 'a scalar' -// --- -// 'another scalar' -// --- -// 'yet another scalar' -// -// Tokens: -// -// STREAM-START(utf-8) -// SCALAR("a scalar",single-quoted) -// DOCUMENT-START -// SCALAR("another scalar",single-quoted) -// DOCUMENT-START -// SCALAR("yet another scalar",single-quoted) -// STREAM-END -// -// We have already introduced the SCALAR token above. The following tokens are -// used to describe aliases, anchors, tag, and scalars: -// -// ALIAS(anchor) -// ANCHOR(anchor) -// TAG(handle,suffix) -// SCALAR(value,style) -// -// The following series of examples illustrate the usage of these tokens: -// -// 1. A recursive sequence: -// -// &A [ *A ] -// -// Tokens: -// -// STREAM-START(utf-8) -// ANCHOR("A") -// FLOW-SEQUENCE-START -// ALIAS("A") -// FLOW-SEQUENCE-END -// STREAM-END -// -// 2. A tagged scalar: -// -// !!float "3.14" # A good approximation. -// -// Tokens: -// -// STREAM-START(utf-8) -// TAG("!!","float") -// SCALAR("3.14",double-quoted) -// STREAM-END -// -// 3. Various scalar styles: -// -// --- # Implicit empty plain scalars do not produce tokens. -// --- a plain scalar -// --- 'a single-quoted scalar' -// --- "a double-quoted scalar" -// --- |- -// a literal scalar -// --- >- -// a folded -// scalar -// -// Tokens: -// -// STREAM-START(utf-8) -// DOCUMENT-START -// DOCUMENT-START -// SCALAR("a plain scalar",plain) -// DOCUMENT-START -// SCALAR("a single-quoted scalar",single-quoted) -// DOCUMENT-START -// SCALAR("a double-quoted scalar",double-quoted) -// DOCUMENT-START -// SCALAR("a literal scalar",literal) -// DOCUMENT-START -// SCALAR("a folded scalar",folded) -// STREAM-END -// -// Now it's time to review collection-related tokens. We will start with -// flow collections: -// -// FLOW-SEQUENCE-START -// FLOW-SEQUENCE-END -// FLOW-MAPPING-START -// FLOW-MAPPING-END -// FLOW-ENTRY -// KEY -// VALUE -// -// The tokens FLOW-SEQUENCE-START, FLOW-SEQUENCE-END, FLOW-MAPPING-START, and -// FLOW-MAPPING-END represent the indicators '[', ']', '{', and '}' -// correspondingly. FLOW-ENTRY represent the ',' indicator. Finally the -// indicators '?' and ':', which are used for denoting mapping keys and values, -// are represented by the KEY and VALUE tokens. -// -// The following examples show flow collections: -// -// 1. A flow sequence: -// -// [item 1, item 2, item 3] -// -// Tokens: -// -// STREAM-START(utf-8) -// FLOW-SEQUENCE-START -// SCALAR("item 1",plain) -// FLOW-ENTRY -// SCALAR("item 2",plain) -// FLOW-ENTRY -// SCALAR("item 3",plain) -// FLOW-SEQUENCE-END -// STREAM-END -// -// 2. A flow mapping: -// -// { -// a simple key: a value, # Note that the KEY token is produced. -// ? a complex key: another value, -// } -// -// Tokens: -// -// STREAM-START(utf-8) -// FLOW-MAPPING-START -// KEY -// SCALAR("a simple key",plain) -// VALUE -// SCALAR("a value",plain) -// FLOW-ENTRY -// KEY -// SCALAR("a complex key",plain) -// VALUE -// SCALAR("another value",plain) -// FLOW-ENTRY -// FLOW-MAPPING-END -// STREAM-END -// -// A simple key is a key which is not denoted by the '?' indicator. Note that -// the Scanner still produce the KEY token whenever it encounters a simple key. -// -// For scanning block collections, the following tokens are used (note that we -// repeat KEY and VALUE here): -// -// BLOCK-SEQUENCE-START -// BLOCK-MAPPING-START -// BLOCK-END -// BLOCK-ENTRY -// KEY -// VALUE -// -// The tokens BLOCK-SEQUENCE-START and BLOCK-MAPPING-START denote indentation -// increase that precedes a block collection (cf. the INDENT token in Python). -// The token BLOCK-END denote indentation decrease that ends a block collection -// (cf. the DEDENT token in Python). However YAML has some syntax pecularities -// that makes detections of these tokens more complex. -// -// The tokens BLOCK-ENTRY, KEY, and VALUE are used to represent the indicators -// '-', '?', and ':' correspondingly. -// -// The following examples show how the tokens BLOCK-SEQUENCE-START, -// BLOCK-MAPPING-START, and BLOCK-END are emitted by the Scanner: -// -// 1. Block sequences: -// -// - item 1 -// - item 2 -// - -// - item 3.1 -// - item 3.2 -// - -// key 1: value 1 -// key 2: value 2 -// -// Tokens: -// -// STREAM-START(utf-8) -// BLOCK-SEQUENCE-START -// BLOCK-ENTRY -// SCALAR("item 1",plain) -// BLOCK-ENTRY -// SCALAR("item 2",plain) -// BLOCK-ENTRY -// BLOCK-SEQUENCE-START -// BLOCK-ENTRY -// SCALAR("item 3.1",plain) -// BLOCK-ENTRY -// SCALAR("item 3.2",plain) -// BLOCK-END -// BLOCK-ENTRY -// BLOCK-MAPPING-START -// KEY -// SCALAR("key 1",plain) -// VALUE -// SCALAR("value 1",plain) -// KEY -// SCALAR("key 2",plain) -// VALUE -// SCALAR("value 2",plain) -// BLOCK-END -// BLOCK-END -// STREAM-END -// -// 2. Block mappings: -// -// a simple key: a value # The KEY token is produced here. -// ? a complex key -// : another value -// a mapping: -// key 1: value 1 -// key 2: value 2 -// a sequence: -// - item 1 -// - item 2 -// -// Tokens: -// -// STREAM-START(utf-8) -// BLOCK-MAPPING-START -// KEY -// SCALAR("a simple key",plain) -// VALUE -// SCALAR("a value",plain) -// KEY -// SCALAR("a complex key",plain) -// VALUE -// SCALAR("another value",plain) -// KEY -// SCALAR("a mapping",plain) -// BLOCK-MAPPING-START -// KEY -// SCALAR("key 1",plain) -// VALUE -// SCALAR("value 1",plain) -// KEY -// SCALAR("key 2",plain) -// VALUE -// SCALAR("value 2",plain) -// BLOCK-END -// KEY -// SCALAR("a sequence",plain) -// VALUE -// BLOCK-SEQUENCE-START -// BLOCK-ENTRY -// SCALAR("item 1",plain) -// BLOCK-ENTRY -// SCALAR("item 2",plain) -// BLOCK-END -// BLOCK-END -// STREAM-END -// -// YAML does not always require to start a new block collection from a new -// line. If the current line contains only '-', '?', and ':' indicators, a new -// block collection may start at the current line. The following examples -// illustrate this case: -// -// 1. Collections in a sequence: -// -// - - item 1 -// - item 2 -// - key 1: value 1 -// key 2: value 2 -// - ? complex key -// : complex value -// -// Tokens: -// -// STREAM-START(utf-8) -// BLOCK-SEQUENCE-START -// BLOCK-ENTRY -// BLOCK-SEQUENCE-START -// BLOCK-ENTRY -// SCALAR("item 1",plain) -// BLOCK-ENTRY -// SCALAR("item 2",plain) -// BLOCK-END -// BLOCK-ENTRY -// BLOCK-MAPPING-START -// KEY -// SCALAR("key 1",plain) -// VALUE -// SCALAR("value 1",plain) -// KEY -// SCALAR("key 2",plain) -// VALUE -// SCALAR("value 2",plain) -// BLOCK-END -// BLOCK-ENTRY -// BLOCK-MAPPING-START -// KEY -// SCALAR("complex key") -// VALUE -// SCALAR("complex value") -// BLOCK-END -// BLOCK-END -// STREAM-END -// -// 2. Collections in a mapping: -// -// ? a sequence -// : - item 1 -// - item 2 -// ? a mapping -// : key 1: value 1 -// key 2: value 2 -// -// Tokens: -// -// STREAM-START(utf-8) -// BLOCK-MAPPING-START -// KEY -// SCALAR("a sequence",plain) -// VALUE -// BLOCK-SEQUENCE-START -// BLOCK-ENTRY -// SCALAR("item 1",plain) -// BLOCK-ENTRY -// SCALAR("item 2",plain) -// BLOCK-END -// KEY -// SCALAR("a mapping",plain) -// VALUE -// BLOCK-MAPPING-START -// KEY -// SCALAR("key 1",plain) -// VALUE -// SCALAR("value 1",plain) -// KEY -// SCALAR("key 2",plain) -// VALUE -// SCALAR("value 2",plain) -// BLOCK-END -// BLOCK-END -// STREAM-END -// -// YAML also permits non-indented sequences if they are included into a block -// mapping. In this case, the token BLOCK-SEQUENCE-START is not produced: -// -// key: -// - item 1 # BLOCK-SEQUENCE-START is NOT produced here. -// - item 2 -// -// Tokens: -// -// STREAM-START(utf-8) -// BLOCK-MAPPING-START -// KEY -// SCALAR("key",plain) -// VALUE -// BLOCK-ENTRY -// SCALAR("item 1",plain) -// BLOCK-ENTRY -// SCALAR("item 2",plain) -// BLOCK-END -// - -// Ensure that the buffer contains the required number of characters. -// Return true on success, false on failure (reader error or memory error). -func cache(parser *yaml_parser_t, length int) bool { - // [Go] This was inlined: !cache(A, B) -> unread < B && !update(A, B) - return parser.unread >= length || yaml_parser_update_buffer(parser, length) -} - -// Advance the buffer pointer. -func skip(parser *yaml_parser_t) { - if !is_blank(parser.buffer, parser.buffer_pos) { - parser.newlines = 0 - } - parser.mark.index++ - parser.mark.column++ - parser.unread-- - parser.buffer_pos += width(parser.buffer[parser.buffer_pos]) -} - -func skip_line(parser *yaml_parser_t) { - if is_crlf(parser.buffer, parser.buffer_pos) { - parser.mark.index += 2 - parser.mark.column = 0 - parser.mark.line++ - parser.unread -= 2 - parser.buffer_pos += 2 - parser.newlines++ - } else if is_break(parser.buffer, parser.buffer_pos) { - parser.mark.index++ - parser.mark.column = 0 - parser.mark.line++ - parser.unread-- - parser.buffer_pos += width(parser.buffer[parser.buffer_pos]) - parser.newlines++ - } -} - -// Copy a character to a string buffer and advance pointers. -func read(parser *yaml_parser_t, s []byte) []byte { - if !is_blank(parser.buffer, parser.buffer_pos) { - parser.newlines = 0 - } - w := width(parser.buffer[parser.buffer_pos]) - if w == 0 { - panic("invalid character sequence") - } - if len(s) == 0 { - s = make([]byte, 0, 32) - } - if w == 1 && len(s)+w <= cap(s) { - s = s[:len(s)+1] - s[len(s)-1] = parser.buffer[parser.buffer_pos] - parser.buffer_pos++ - } else { - s = append(s, parser.buffer[parser.buffer_pos:parser.buffer_pos+w]...) - parser.buffer_pos += w - } - parser.mark.index++ - parser.mark.column++ - parser.unread-- - return s -} - -// Copy a line break character to a string buffer and advance pointers. -func read_line(parser *yaml_parser_t, s []byte) []byte { - buf := parser.buffer - pos := parser.buffer_pos - switch { - case buf[pos] == '\r' && buf[pos+1] == '\n': - // CR LF . LF - s = append(s, '\n') - parser.buffer_pos += 2 - parser.mark.index++ - parser.unread-- - case buf[pos] == '\r' || buf[pos] == '\n': - // CR|LF . LF - s = append(s, '\n') - parser.buffer_pos += 1 - case buf[pos] == '\xC2' && buf[pos+1] == '\x85': - // NEL . LF - s = append(s, '\n') - parser.buffer_pos += 2 - case buf[pos] == '\xE2' && buf[pos+1] == '\x80' && (buf[pos+2] == '\xA8' || buf[pos+2] == '\xA9'): - // LS|PS . LS|PS - s = append(s, buf[parser.buffer_pos:pos+3]...) - parser.buffer_pos += 3 - default: - return s - } - parser.mark.index++ - parser.mark.column = 0 - parser.mark.line++ - parser.unread-- - parser.newlines++ - return s -} - -// Get the next token. -func yaml_parser_scan(parser *yaml_parser_t, token *yaml_token_t) bool { - // Erase the token object. - *token = yaml_token_t{} // [Go] Is this necessary? - - // No tokens after STREAM-END or error. - if parser.stream_end_produced || parser.error != yaml_NO_ERROR { - return true - } - - // Ensure that the tokens queue contains enough tokens. - if !parser.token_available { - if !yaml_parser_fetch_more_tokens(parser) { - return false - } - } - - // Fetch the next token from the queue. - *token = parser.tokens[parser.tokens_head] - parser.tokens_head++ - parser.tokens_parsed++ - parser.token_available = false - - if token.typ == yaml_STREAM_END_TOKEN { - parser.stream_end_produced = true - } - return true -} - -// Set the scanner error and return false. -func yaml_parser_set_scanner_error(parser *yaml_parser_t, context string, context_mark yaml_mark_t, problem string) bool { - parser.error = yaml_SCANNER_ERROR - parser.context = context - parser.context_mark = context_mark - parser.problem = problem - parser.problem_mark = parser.mark - return false -} - -func yaml_parser_set_scanner_tag_error(parser *yaml_parser_t, directive bool, context_mark yaml_mark_t, problem string) bool { - context := "while parsing a tag" - if directive { - context = "while parsing a %TAG directive" - } - return yaml_parser_set_scanner_error(parser, context, context_mark, problem) -} - -func trace(args ...interface{}) func() { - pargs := append([]interface{}{"+++"}, args...) - fmt.Println(pargs...) - pargs = append([]interface{}{"---"}, args...) - return func() { fmt.Println(pargs...) } -} - -// Ensure that the tokens queue contains at least one token which can be -// returned to the Parser. -func yaml_parser_fetch_more_tokens(parser *yaml_parser_t) bool { - // While we need more tokens to fetch, do it. - for { - // [Go] The comment parsing logic requires a lookahead of two tokens - // so that foot comments may be parsed in time of associating them - // with the tokens that are parsed before them, and also for line - // comments to be transformed into head comments in some edge cases. - if parser.tokens_head < len(parser.tokens)-2 { - // If a potential simple key is at the head position, we need to fetch - // the next token to disambiguate it. - head_tok_idx, ok := parser.simple_keys_by_tok[parser.tokens_parsed] - if !ok { - break - } else if valid, ok := yaml_simple_key_is_valid(parser, &parser.simple_keys[head_tok_idx]); !ok { - return false - } else if !valid { - break - } - } - // Fetch the next token. - if !yaml_parser_fetch_next_token(parser) { - return false - } - } - - parser.token_available = true - return true -} - -// The dispatcher for token fetchers. -func yaml_parser_fetch_next_token(parser *yaml_parser_t) (ok bool) { - // Ensure that the buffer is initialized. - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - - // Check if we just started scanning. Fetch STREAM-START then. - if !parser.stream_start_produced { - return yaml_parser_fetch_stream_start(parser) - } - - scan_mark := parser.mark - - // Eat whitespaces and comments until we reach the next token. - if !yaml_parser_scan_to_next_token(parser) { - return false - } - - // [Go] While unrolling indents, transform the head comments of prior - // indentation levels observed after scan_start into foot comments at - // the respective indexes. - - // Check the indentation level against the current column. - if !yaml_parser_unroll_indent(parser, parser.mark.column, scan_mark) { - return false - } - - // Ensure that the buffer contains at least 4 characters. 4 is the length - // of the longest indicators ('--- ' and '... '). - if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) { - return false - } - - // Is it the end of the stream? - if is_z(parser.buffer, parser.buffer_pos) { - return yaml_parser_fetch_stream_end(parser) - } - - // Is it a directive? - if parser.mark.column == 0 && parser.buffer[parser.buffer_pos] == '%' { - return yaml_parser_fetch_directive(parser) - } - - buf := parser.buffer - pos := parser.buffer_pos - - // Is it the document start indicator? - if parser.mark.column == 0 && buf[pos] == '-' && buf[pos+1] == '-' && buf[pos+2] == '-' && is_blankz(buf, pos+3) { - return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_START_TOKEN) - } - - // Is it the document end indicator? - if parser.mark.column == 0 && buf[pos] == '.' && buf[pos+1] == '.' && buf[pos+2] == '.' && is_blankz(buf, pos+3) { - return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_END_TOKEN) - } - - comment_mark := parser.mark - if len(parser.tokens) > 0 && (parser.flow_level == 0 && buf[pos] == ':' || parser.flow_level > 0 && buf[pos] == ',') { - // Associate any following comments with the prior token. - comment_mark = parser.tokens[len(parser.tokens)-1].start_mark - } - defer func() { - if !ok { - return - } - if len(parser.tokens) > 0 && parser.tokens[len(parser.tokens)-1].typ == yaml_BLOCK_ENTRY_TOKEN { - // Sequence indicators alone have no line comments. It becomes - // a head comment for whatever follows. - return - } - if !yaml_parser_scan_line_comment(parser, comment_mark) { - ok = false - return - } - }() - - // Is it the flow sequence start indicator? - if buf[pos] == '[' { - return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_SEQUENCE_START_TOKEN) - } - - // Is it the flow mapping start indicator? - if parser.buffer[parser.buffer_pos] == '{' { - return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_MAPPING_START_TOKEN) - } - - // Is it the flow sequence end indicator? - if parser.buffer[parser.buffer_pos] == ']' { - return yaml_parser_fetch_flow_collection_end(parser, - yaml_FLOW_SEQUENCE_END_TOKEN) - } - - // Is it the flow mapping end indicator? - if parser.buffer[parser.buffer_pos] == '}' { - return yaml_parser_fetch_flow_collection_end(parser, - yaml_FLOW_MAPPING_END_TOKEN) - } - - // Is it the flow entry indicator? - if parser.buffer[parser.buffer_pos] == ',' { - return yaml_parser_fetch_flow_entry(parser) - } - - // Is it the block entry indicator? - if parser.buffer[parser.buffer_pos] == '-' && is_blankz(parser.buffer, parser.buffer_pos+1) { - return yaml_parser_fetch_block_entry(parser) - } - - // Is it the key indicator? - if parser.buffer[parser.buffer_pos] == '?' && (parser.flow_level > 0 || is_blankz(parser.buffer, parser.buffer_pos+1)) { - return yaml_parser_fetch_key(parser) - } - - // Is it the value indicator? - if parser.buffer[parser.buffer_pos] == ':' && (parser.flow_level > 0 || is_blankz(parser.buffer, parser.buffer_pos+1)) { - return yaml_parser_fetch_value(parser) - } - - // Is it an alias? - if parser.buffer[parser.buffer_pos] == '*' { - return yaml_parser_fetch_anchor(parser, yaml_ALIAS_TOKEN) - } - - // Is it an anchor? - if parser.buffer[parser.buffer_pos] == '&' { - return yaml_parser_fetch_anchor(parser, yaml_ANCHOR_TOKEN) - } - - // Is it a tag? - if parser.buffer[parser.buffer_pos] == '!' { - return yaml_parser_fetch_tag(parser) - } - - // Is it a literal scalar? - if parser.buffer[parser.buffer_pos] == '|' && parser.flow_level == 0 { - return yaml_parser_fetch_block_scalar(parser, true) - } - - // Is it a folded scalar? - if parser.buffer[parser.buffer_pos] == '>' && parser.flow_level == 0 { - return yaml_parser_fetch_block_scalar(parser, false) - } - - // Is it a single-quoted scalar? - if parser.buffer[parser.buffer_pos] == '\'' { - return yaml_parser_fetch_flow_scalar(parser, true) - } - - // Is it a double-quoted scalar? - if parser.buffer[parser.buffer_pos] == '"' { - return yaml_parser_fetch_flow_scalar(parser, false) - } - - // Is it a plain scalar? - // - // A plain scalar may start with any non-blank characters except - // - // '-', '?', ':', ',', '[', ']', '{', '}', - // '#', '&', '*', '!', '|', '>', '\'', '\"', - // '%', '@', '`'. - // - // In the block context (and, for the '-' indicator, in the flow context - // too), it may also start with the characters - // - // '-', '?', ':' - // - // if it is followed by a non-space character. - // - // The last rule is more restrictive than the specification requires. - // [Go] TODO Make this logic more reasonable. - //switch parser.buffer[parser.buffer_pos] { - //case '-', '?', ':', ',', '?', '-', ',', ':', ']', '[', '}', '{', '&', '#', '!', '*', '>', '|', '"', '\'', '@', '%', '-', '`': - //} - if !(is_blankz(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == '-' || - parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':' || - parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == '[' || - parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '{' || - parser.buffer[parser.buffer_pos] == '}' || parser.buffer[parser.buffer_pos] == '#' || - parser.buffer[parser.buffer_pos] == '&' || parser.buffer[parser.buffer_pos] == '*' || - parser.buffer[parser.buffer_pos] == '!' || parser.buffer[parser.buffer_pos] == '|' || - parser.buffer[parser.buffer_pos] == '>' || parser.buffer[parser.buffer_pos] == '\'' || - parser.buffer[parser.buffer_pos] == '"' || parser.buffer[parser.buffer_pos] == '%' || - parser.buffer[parser.buffer_pos] == '@' || parser.buffer[parser.buffer_pos] == '`') || - (parser.buffer[parser.buffer_pos] == '-' && !is_blank(parser.buffer, parser.buffer_pos+1)) || - (parser.flow_level == 0 && - (parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == ':') && - !is_blankz(parser.buffer, parser.buffer_pos+1)) { - return yaml_parser_fetch_plain_scalar(parser) - } - - // If we don't determine the token type so far, it is an error. - return yaml_parser_set_scanner_error(parser, - "while scanning for the next token", parser.mark, - "found character that cannot start any token") -} - -func yaml_simple_key_is_valid(parser *yaml_parser_t, simple_key *yaml_simple_key_t) (valid, ok bool) { - if !simple_key.possible { - return false, true - } - - // The 1.2 specification says: - // - // "If the ? indicator is omitted, parsing needs to see past the - // implicit key to recognize it as such. To limit the amount of - // lookahead required, the “:” indicator must appear at most 1024 - // Unicode characters beyond the start of the key. In addition, the key - // is restricted to a single line." - // - if simple_key.mark.line < parser.mark.line || simple_key.mark.index+1024 < parser.mark.index { - // Check if the potential simple key to be removed is required. - if simple_key.required { - return false, yaml_parser_set_scanner_error(parser, - "while scanning a simple key", simple_key.mark, - "could not find expected ':'") - } - simple_key.possible = false - return false, true - } - return true, true -} - -// Check if a simple key may start at the current position and add it if -// needed. -func yaml_parser_save_simple_key(parser *yaml_parser_t) bool { - // A simple key is required at the current position if the scanner is in - // the block context and the current column coincides with the indentation - // level. - - required := parser.flow_level == 0 && parser.indent == parser.mark.column - - // - // If the current position may start a simple key, save it. - // - if parser.simple_key_allowed { - simple_key := yaml_simple_key_t{ - possible: true, - required: required, - token_number: parser.tokens_parsed + (len(parser.tokens) - parser.tokens_head), - mark: parser.mark, - } - - if !yaml_parser_remove_simple_key(parser) { - return false - } - parser.simple_keys[len(parser.simple_keys)-1] = simple_key - parser.simple_keys_by_tok[simple_key.token_number] = len(parser.simple_keys) - 1 - } - return true -} - -// Remove a potential simple key at the current flow level. -func yaml_parser_remove_simple_key(parser *yaml_parser_t) bool { - i := len(parser.simple_keys) - 1 - if parser.simple_keys[i].possible { - // If the key is required, it is an error. - if parser.simple_keys[i].required { - return yaml_parser_set_scanner_error(parser, - "while scanning a simple key", parser.simple_keys[i].mark, - "could not find expected ':'") - } - // Remove the key from the stack. - parser.simple_keys[i].possible = false - delete(parser.simple_keys_by_tok, parser.simple_keys[i].token_number) - } - return true -} - -// max_flow_level limits the flow_level -const max_flow_level = 10000 - -// Increase the flow level and resize the simple key list if needed. -func yaml_parser_increase_flow_level(parser *yaml_parser_t) bool { - // Reset the simple key on the next level. - parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{ - possible: false, - required: false, - token_number: parser.tokens_parsed + (len(parser.tokens) - parser.tokens_head), - mark: parser.mark, - }) - - // Increase the flow level. - parser.flow_level++ - if parser.flow_level > max_flow_level { - return yaml_parser_set_scanner_error(parser, - "while increasing flow level", parser.simple_keys[len(parser.simple_keys)-1].mark, - fmt.Sprintf("exceeded max depth of %d", max_flow_level)) - } - return true -} - -// Decrease the flow level. -func yaml_parser_decrease_flow_level(parser *yaml_parser_t) bool { - if parser.flow_level > 0 { - parser.flow_level-- - last := len(parser.simple_keys) - 1 - delete(parser.simple_keys_by_tok, parser.simple_keys[last].token_number) - parser.simple_keys = parser.simple_keys[:last] - } - return true -} - -// max_indents limits the indents stack size -const max_indents = 10000 - -// Push the current indentation level to the stack and set the new level -// the current column is greater than the indentation level. In this case, -// append or insert the specified token into the token queue. -func yaml_parser_roll_indent(parser *yaml_parser_t, column, number int, typ yaml_token_type_t, mark yaml_mark_t) bool { - // In the flow context, do nothing. - if parser.flow_level > 0 { - return true - } - - if parser.indent < column { - // Push the current indentation level to the stack and set the new - // indentation level. - parser.indents = append(parser.indents, parser.indent) - parser.indent = column - if len(parser.indents) > max_indents { - return yaml_parser_set_scanner_error(parser, - "while increasing indent level", parser.simple_keys[len(parser.simple_keys)-1].mark, - fmt.Sprintf("exceeded max depth of %d", max_indents)) - } - - // Create a token and insert it into the queue. - token := yaml_token_t{ - typ: typ, - start_mark: mark, - end_mark: mark, - } - if number > -1 { - number -= parser.tokens_parsed - } - yaml_insert_token(parser, number, &token) - } - return true -} - -// Pop indentation levels from the indents stack until the current level -// becomes less or equal to the column. For each indentation level, append -// the BLOCK-END token. -func yaml_parser_unroll_indent(parser *yaml_parser_t, column int, scan_mark yaml_mark_t) bool { - // In the flow context, do nothing. - if parser.flow_level > 0 { - return true - } - - block_mark := scan_mark - block_mark.index-- - - // Loop through the indentation levels in the stack. - for parser.indent > column { - - // [Go] Reposition the end token before potential following - // foot comments of parent blocks. For that, search - // backwards for recent comments that were at the same - // indent as the block that is ending now. - stop_index := block_mark.index - for i := len(parser.comments) - 1; i >= 0; i-- { - comment := &parser.comments[i] - - if comment.end_mark.index < stop_index { - // Don't go back beyond the start of the comment/whitespace scan, unless column < 0. - // If requested indent column is < 0, then the document is over and everything else - // is a foot anyway. - break - } - if comment.start_mark.column == parser.indent+1 { - // This is a good match. But maybe there's a former comment - // at that same indent level, so keep searching. - block_mark = comment.start_mark - } - - // While the end of the former comment matches with - // the start of the following one, we know there's - // nothing in between and scanning is still safe. - stop_index = comment.scan_mark.index - } - - // Create a token and append it to the queue. - token := yaml_token_t{ - typ: yaml_BLOCK_END_TOKEN, - start_mark: block_mark, - end_mark: block_mark, - } - yaml_insert_token(parser, -1, &token) - - // Pop the indentation level. - parser.indent = parser.indents[len(parser.indents)-1] - parser.indents = parser.indents[:len(parser.indents)-1] - } - return true -} - -// Initialize the scanner and produce the STREAM-START token. -func yaml_parser_fetch_stream_start(parser *yaml_parser_t) bool { - - // Set the initial indentation. - parser.indent = -1 - - // Initialize the simple key stack. - parser.simple_keys = append(parser.simple_keys, yaml_simple_key_t{}) - - parser.simple_keys_by_tok = make(map[int]int) - - // A simple key is allowed at the beginning of the stream. - parser.simple_key_allowed = true - - // We have started. - parser.stream_start_produced = true - - // Create the STREAM-START token and append it to the queue. - token := yaml_token_t{ - typ: yaml_STREAM_START_TOKEN, - start_mark: parser.mark, - end_mark: parser.mark, - encoding: parser.encoding, - } - yaml_insert_token(parser, -1, &token) - return true -} - -// Produce the STREAM-END token and shut down the scanner. -func yaml_parser_fetch_stream_end(parser *yaml_parser_t) bool { - - // Force new line. - if parser.mark.column != 0 { - parser.mark.column = 0 - parser.mark.line++ - } - - // Reset the indentation level. - if !yaml_parser_unroll_indent(parser, -1, parser.mark) { - return false - } - - // Reset simple keys. - if !yaml_parser_remove_simple_key(parser) { - return false - } - - parser.simple_key_allowed = false - - // Create the STREAM-END token and append it to the queue. - token := yaml_token_t{ - typ: yaml_STREAM_END_TOKEN, - start_mark: parser.mark, - end_mark: parser.mark, - } - yaml_insert_token(parser, -1, &token) - return true -} - -// Produce a VERSION-DIRECTIVE or TAG-DIRECTIVE token. -func yaml_parser_fetch_directive(parser *yaml_parser_t) bool { - // Reset the indentation level. - if !yaml_parser_unroll_indent(parser, -1, parser.mark) { - return false - } - - // Reset simple keys. - if !yaml_parser_remove_simple_key(parser) { - return false - } - - parser.simple_key_allowed = false - - // Create the YAML-DIRECTIVE or TAG-DIRECTIVE token. - token := yaml_token_t{} - if !yaml_parser_scan_directive(parser, &token) { - return false - } - // Append the token to the queue. - yaml_insert_token(parser, -1, &token) - return true -} - -// Produce the DOCUMENT-START or DOCUMENT-END token. -func yaml_parser_fetch_document_indicator(parser *yaml_parser_t, typ yaml_token_type_t) bool { - // Reset the indentation level. - if !yaml_parser_unroll_indent(parser, -1, parser.mark) { - return false - } - - // Reset simple keys. - if !yaml_parser_remove_simple_key(parser) { - return false - } - - parser.simple_key_allowed = false - - // Consume the token. - start_mark := parser.mark - - skip(parser) - skip(parser) - skip(parser) - - end_mark := parser.mark - - // Create the DOCUMENT-START or DOCUMENT-END token. - token := yaml_token_t{ - typ: typ, - start_mark: start_mark, - end_mark: end_mark, - } - // Append the token to the queue. - yaml_insert_token(parser, -1, &token) - return true -} - -// Produce the FLOW-SEQUENCE-START or FLOW-MAPPING-START token. -func yaml_parser_fetch_flow_collection_start(parser *yaml_parser_t, typ yaml_token_type_t) bool { - - // The indicators '[' and '{' may start a simple key. - if !yaml_parser_save_simple_key(parser) { - return false - } - - // Increase the flow level. - if !yaml_parser_increase_flow_level(parser) { - return false - } - - // A simple key may follow the indicators '[' and '{'. - parser.simple_key_allowed = true - - // Consume the token. - start_mark := parser.mark - skip(parser) - end_mark := parser.mark - - // Create the FLOW-SEQUENCE-START of FLOW-MAPPING-START token. - token := yaml_token_t{ - typ: typ, - start_mark: start_mark, - end_mark: end_mark, - } - // Append the token to the queue. - yaml_insert_token(parser, -1, &token) - return true -} - -// Produce the FLOW-SEQUENCE-END or FLOW-MAPPING-END token. -func yaml_parser_fetch_flow_collection_end(parser *yaml_parser_t, typ yaml_token_type_t) bool { - // Reset any potential simple key on the current flow level. - if !yaml_parser_remove_simple_key(parser) { - return false - } - - // Decrease the flow level. - if !yaml_parser_decrease_flow_level(parser) { - return false - } - - // No simple keys after the indicators ']' and '}'. - parser.simple_key_allowed = false - - // Consume the token. - - start_mark := parser.mark - skip(parser) - end_mark := parser.mark - - // Create the FLOW-SEQUENCE-END of FLOW-MAPPING-END token. - token := yaml_token_t{ - typ: typ, - start_mark: start_mark, - end_mark: end_mark, - } - // Append the token to the queue. - yaml_insert_token(parser, -1, &token) - return true -} - -// Produce the FLOW-ENTRY token. -func yaml_parser_fetch_flow_entry(parser *yaml_parser_t) bool { - // Reset any potential simple keys on the current flow level. - if !yaml_parser_remove_simple_key(parser) { - return false - } - - // Simple keys are allowed after ','. - parser.simple_key_allowed = true - - // Consume the token. - start_mark := parser.mark - skip(parser) - end_mark := parser.mark - - // Create the FLOW-ENTRY token and append it to the queue. - token := yaml_token_t{ - typ: yaml_FLOW_ENTRY_TOKEN, - start_mark: start_mark, - end_mark: end_mark, - } - yaml_insert_token(parser, -1, &token) - return true -} - -// Produce the BLOCK-ENTRY token. -func yaml_parser_fetch_block_entry(parser *yaml_parser_t) bool { - // Check if the scanner is in the block context. - if parser.flow_level == 0 { - // Check if we are allowed to start a new entry. - if !parser.simple_key_allowed { - return yaml_parser_set_scanner_error(parser, "", parser.mark, - "block sequence entries are not allowed in this context") - } - // Add the BLOCK-SEQUENCE-START token if needed. - if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_SEQUENCE_START_TOKEN, parser.mark) { - return false - } - } else { - // It is an error for the '-' indicator to occur in the flow context, - // but we let the Parser detect and report about it because the Parser - // is able to point to the context. - } - - // Reset any potential simple keys on the current flow level. - if !yaml_parser_remove_simple_key(parser) { - return false - } - - // Simple keys are allowed after '-'. - parser.simple_key_allowed = true - - // Consume the token. - start_mark := parser.mark - skip(parser) - end_mark := parser.mark - - // Create the BLOCK-ENTRY token and append it to the queue. - token := yaml_token_t{ - typ: yaml_BLOCK_ENTRY_TOKEN, - start_mark: start_mark, - end_mark: end_mark, - } - yaml_insert_token(parser, -1, &token) - return true -} - -// Produce the KEY token. -func yaml_parser_fetch_key(parser *yaml_parser_t) bool { - - // In the block context, additional checks are required. - if parser.flow_level == 0 { - // Check if we are allowed to start a new key (not nessesary simple). - if !parser.simple_key_allowed { - return yaml_parser_set_scanner_error(parser, "", parser.mark, - "mapping keys are not allowed in this context") - } - // Add the BLOCK-MAPPING-START token if needed. - if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_MAPPING_START_TOKEN, parser.mark) { - return false - } - } - - // Reset any potential simple keys on the current flow level. - if !yaml_parser_remove_simple_key(parser) { - return false - } - - // Simple keys are allowed after '?' in the block context. - parser.simple_key_allowed = parser.flow_level == 0 - - // Consume the token. - start_mark := parser.mark - skip(parser) - end_mark := parser.mark - - // Create the KEY token and append it to the queue. - token := yaml_token_t{ - typ: yaml_KEY_TOKEN, - start_mark: start_mark, - end_mark: end_mark, - } - yaml_insert_token(parser, -1, &token) - return true -} - -// Produce the VALUE token. -func yaml_parser_fetch_value(parser *yaml_parser_t) bool { - - simple_key := &parser.simple_keys[len(parser.simple_keys)-1] - - // Have we found a simple key? - if valid, ok := yaml_simple_key_is_valid(parser, simple_key); !ok { - return false - - } else if valid { - - // Create the KEY token and insert it into the queue. - token := yaml_token_t{ - typ: yaml_KEY_TOKEN, - start_mark: simple_key.mark, - end_mark: simple_key.mark, - } - yaml_insert_token(parser, simple_key.token_number-parser.tokens_parsed, &token) - - // In the block context, we may need to add the BLOCK-MAPPING-START token. - if !yaml_parser_roll_indent(parser, simple_key.mark.column, - simple_key.token_number, - yaml_BLOCK_MAPPING_START_TOKEN, simple_key.mark) { - return false - } - - // Remove the simple key. - simple_key.possible = false - delete(parser.simple_keys_by_tok, simple_key.token_number) - - // A simple key cannot follow another simple key. - parser.simple_key_allowed = false - - } else { - // The ':' indicator follows a complex key. - - // In the block context, extra checks are required. - if parser.flow_level == 0 { - - // Check if we are allowed to start a complex value. - if !parser.simple_key_allowed { - return yaml_parser_set_scanner_error(parser, "", parser.mark, - "mapping values are not allowed in this context") - } - - // Add the BLOCK-MAPPING-START token if needed. - if !yaml_parser_roll_indent(parser, parser.mark.column, -1, yaml_BLOCK_MAPPING_START_TOKEN, parser.mark) { - return false - } - } - - // Simple keys after ':' are allowed in the block context. - parser.simple_key_allowed = parser.flow_level == 0 - } - - // Consume the token. - start_mark := parser.mark - skip(parser) - end_mark := parser.mark - - // Create the VALUE token and append it to the queue. - token := yaml_token_t{ - typ: yaml_VALUE_TOKEN, - start_mark: start_mark, - end_mark: end_mark, - } - yaml_insert_token(parser, -1, &token) - return true -} - -// Produce the ALIAS or ANCHOR token. -func yaml_parser_fetch_anchor(parser *yaml_parser_t, typ yaml_token_type_t) bool { - // An anchor or an alias could be a simple key. - if !yaml_parser_save_simple_key(parser) { - return false - } - - // A simple key cannot follow an anchor or an alias. - parser.simple_key_allowed = false - - // Create the ALIAS or ANCHOR token and append it to the queue. - var token yaml_token_t - if !yaml_parser_scan_anchor(parser, &token, typ) { - return false - } - yaml_insert_token(parser, -1, &token) - return true -} - -// Produce the TAG token. -func yaml_parser_fetch_tag(parser *yaml_parser_t) bool { - // A tag could be a simple key. - if !yaml_parser_save_simple_key(parser) { - return false - } - - // A simple key cannot follow a tag. - parser.simple_key_allowed = false - - // Create the TAG token and append it to the queue. - var token yaml_token_t - if !yaml_parser_scan_tag(parser, &token) { - return false - } - yaml_insert_token(parser, -1, &token) - return true -} - -// Produce the SCALAR(...,literal) or SCALAR(...,folded) tokens. -func yaml_parser_fetch_block_scalar(parser *yaml_parser_t, literal bool) bool { - // Remove any potential simple keys. - if !yaml_parser_remove_simple_key(parser) { - return false - } - - // A simple key may follow a block scalar. - parser.simple_key_allowed = true - - // Create the SCALAR token and append it to the queue. - var token yaml_token_t - if !yaml_parser_scan_block_scalar(parser, &token, literal) { - return false - } - yaml_insert_token(parser, -1, &token) - return true -} - -// Produce the SCALAR(...,single-quoted) or SCALAR(...,double-quoted) tokens. -func yaml_parser_fetch_flow_scalar(parser *yaml_parser_t, single bool) bool { - // A plain scalar could be a simple key. - if !yaml_parser_save_simple_key(parser) { - return false - } - - // A simple key cannot follow a flow scalar. - parser.simple_key_allowed = false - - // Create the SCALAR token and append it to the queue. - var token yaml_token_t - if !yaml_parser_scan_flow_scalar(parser, &token, single) { - return false - } - yaml_insert_token(parser, -1, &token) - return true -} - -// Produce the SCALAR(...,plain) token. -func yaml_parser_fetch_plain_scalar(parser *yaml_parser_t) bool { - // A plain scalar could be a simple key. - if !yaml_parser_save_simple_key(parser) { - return false - } - - // A simple key cannot follow a flow scalar. - parser.simple_key_allowed = false - - // Create the SCALAR token and append it to the queue. - var token yaml_token_t - if !yaml_parser_scan_plain_scalar(parser, &token) { - return false - } - yaml_insert_token(parser, -1, &token) - return true -} - -// Eat whitespaces and comments until the next token is found. -func yaml_parser_scan_to_next_token(parser *yaml_parser_t) bool { - - scan_mark := parser.mark - - // Until the next token is not found. - for { - // Allow the BOM mark to start a line. - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - if parser.mark.column == 0 && is_bom(parser.buffer, parser.buffer_pos) { - skip(parser) - } - - // Eat whitespaces. - // Tabs are allowed: - // - in the flow context - // - in the block context, but not at the beginning of the line or - // after '-', '?', or ':' (complex value). - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - - for parser.buffer[parser.buffer_pos] == ' ' || ((parser.flow_level > 0 || !parser.simple_key_allowed) && parser.buffer[parser.buffer_pos] == '\t') { - skip(parser) - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - } - - // Check if we just had a line comment under a sequence entry that - // looks more like a header to the following content. Similar to this: - // - // - # The comment - // - Some data - // - // If so, transform the line comment to a head comment and reposition. - if len(parser.comments) > 0 && len(parser.tokens) > 1 { - tokenA := parser.tokens[len(parser.tokens)-2] - tokenB := parser.tokens[len(parser.tokens)-1] - comment := &parser.comments[len(parser.comments)-1] - if tokenA.typ == yaml_BLOCK_SEQUENCE_START_TOKEN && tokenB.typ == yaml_BLOCK_ENTRY_TOKEN && len(comment.line) > 0 && !is_break(parser.buffer, parser.buffer_pos) { - // If it was in the prior line, reposition so it becomes a - // header of the follow up token. Otherwise, keep it in place - // so it becomes a header of the former. - comment.head = comment.line - comment.line = nil - if comment.start_mark.line == parser.mark.line-1 { - comment.token_mark = parser.mark - } - } - } - - // Eat a comment until a line break. - if parser.buffer[parser.buffer_pos] == '#' { - if !yaml_parser_scan_comments(parser, scan_mark) { - return false - } - } - - // If it is a line break, eat it. - if is_break(parser.buffer, parser.buffer_pos) { - if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { - return false - } - skip_line(parser) - - // In the block context, a new line may start a simple key. - if parser.flow_level == 0 { - parser.simple_key_allowed = true - } - } else { - break // We have found a token. - } - } - - return true -} - -// Scan a YAML-DIRECTIVE or TAG-DIRECTIVE token. -// -// Scope: -// -// %YAML 1.1 # a comment \n -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -// %TAG !yaml! tag:yaml.org,2002: \n -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -func yaml_parser_scan_directive(parser *yaml_parser_t, token *yaml_token_t) bool { - // Eat '%'. - start_mark := parser.mark - skip(parser) - - // Scan the directive name. - var name []byte - if !yaml_parser_scan_directive_name(parser, start_mark, &name) { - return false - } - - // Is it a YAML directive? - if bytes.Equal(name, []byte("YAML")) { - // Scan the VERSION directive value. - var major, minor int8 - if !yaml_parser_scan_version_directive_value(parser, start_mark, &major, &minor) { - return false - } - end_mark := parser.mark - - // Create a VERSION-DIRECTIVE token. - *token = yaml_token_t{ - typ: yaml_VERSION_DIRECTIVE_TOKEN, - start_mark: start_mark, - end_mark: end_mark, - major: major, - minor: minor, - } - - // Is it a TAG directive? - } else if bytes.Equal(name, []byte("TAG")) { - // Scan the TAG directive value. - var handle, prefix []byte - if !yaml_parser_scan_tag_directive_value(parser, start_mark, &handle, &prefix) { - return false - } - end_mark := parser.mark - - // Create a TAG-DIRECTIVE token. - *token = yaml_token_t{ - typ: yaml_TAG_DIRECTIVE_TOKEN, - start_mark: start_mark, - end_mark: end_mark, - value: handle, - prefix: prefix, - } - - // Unknown directive. - } else { - yaml_parser_set_scanner_error(parser, "while scanning a directive", - start_mark, "found unknown directive name") - return false - } - - // Eat the rest of the line including any comments. - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - - for is_blank(parser.buffer, parser.buffer_pos) { - skip(parser) - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - } - - if parser.buffer[parser.buffer_pos] == '#' { - // [Go] Discard this inline comment for the time being. - //if !yaml_parser_scan_line_comment(parser, start_mark) { - // return false - //} - for !is_breakz(parser.buffer, parser.buffer_pos) { - skip(parser) - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - } - } - - // Check if we are at the end of the line. - if !is_breakz(parser.buffer, parser.buffer_pos) { - yaml_parser_set_scanner_error(parser, "while scanning a directive", - start_mark, "did not find expected comment or line break") - return false - } - - // Eat a line break. - if is_break(parser.buffer, parser.buffer_pos) { - if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { - return false - } - skip_line(parser) - } - - return true -} - -// Scan the directive name. -// -// Scope: -// -// %YAML 1.1 # a comment \n -// ^^^^ -// %TAG !yaml! tag:yaml.org,2002: \n -// ^^^ -func yaml_parser_scan_directive_name(parser *yaml_parser_t, start_mark yaml_mark_t, name *[]byte) bool { - // Consume the directive name. - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - - var s []byte - for is_alpha(parser.buffer, parser.buffer_pos) { - s = read(parser, s) - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - } - - // Check if the name is empty. - if len(s) == 0 { - yaml_parser_set_scanner_error(parser, "while scanning a directive", - start_mark, "could not find expected directive name") - return false - } - - // Check for an blank character after the name. - if !is_blankz(parser.buffer, parser.buffer_pos) { - yaml_parser_set_scanner_error(parser, "while scanning a directive", - start_mark, "found unexpected non-alphabetical character") - return false - } - *name = s - return true -} - -// Scan the value of VERSION-DIRECTIVE. -// -// Scope: -// -// %YAML 1.1 # a comment \n -// ^^^^^^ -func yaml_parser_scan_version_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, major, minor *int8) bool { - // Eat whitespaces. - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - for is_blank(parser.buffer, parser.buffer_pos) { - skip(parser) - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - } - - // Consume the major version number. - if !yaml_parser_scan_version_directive_number(parser, start_mark, major) { - return false - } - - // Eat '.'. - if parser.buffer[parser.buffer_pos] != '.' { - return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", - start_mark, "did not find expected digit or '.' character") - } - - skip(parser) - - // Consume the minor version number. - if !yaml_parser_scan_version_directive_number(parser, start_mark, minor) { - return false - } - return true -} - -const max_number_length = 2 - -// Scan the version number of VERSION-DIRECTIVE. -// -// Scope: -// -// %YAML 1.1 # a comment \n -// ^ -// %YAML 1.1 # a comment \n -// ^ -func yaml_parser_scan_version_directive_number(parser *yaml_parser_t, start_mark yaml_mark_t, number *int8) bool { - - // Repeat while the next character is digit. - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - var value, length int8 - for is_digit(parser.buffer, parser.buffer_pos) { - // Check if the number is too long. - length++ - if length > max_number_length { - return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", - start_mark, "found extremely long version number") - } - value = value*10 + int8(as_digit(parser.buffer, parser.buffer_pos)) - skip(parser) - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - } - - // Check if the number was present. - if length == 0 { - return yaml_parser_set_scanner_error(parser, "while scanning a %YAML directive", - start_mark, "did not find expected version number") - } - *number = value - return true -} - -// Scan the value of a TAG-DIRECTIVE token. -// -// Scope: -// -// %TAG !yaml! tag:yaml.org,2002: \n -// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -func yaml_parser_scan_tag_directive_value(parser *yaml_parser_t, start_mark yaml_mark_t, handle, prefix *[]byte) bool { - var handle_value, prefix_value []byte - - // Eat whitespaces. - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - - for is_blank(parser.buffer, parser.buffer_pos) { - skip(parser) - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - } - - // Scan a handle. - if !yaml_parser_scan_tag_handle(parser, true, start_mark, &handle_value) { - return false - } - - // Expect a whitespace. - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - if !is_blank(parser.buffer, parser.buffer_pos) { - yaml_parser_set_scanner_error(parser, "while scanning a %TAG directive", - start_mark, "did not find expected whitespace") - return false - } - - // Eat whitespaces. - for is_blank(parser.buffer, parser.buffer_pos) { - skip(parser) - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - } - - // Scan a prefix. - if !yaml_parser_scan_tag_uri(parser, true, nil, start_mark, &prefix_value) { - return false - } - - // Expect a whitespace or line break. - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - if !is_blankz(parser.buffer, parser.buffer_pos) { - yaml_parser_set_scanner_error(parser, "while scanning a %TAG directive", - start_mark, "did not find expected whitespace or line break") - return false - } - - *handle = handle_value - *prefix = prefix_value - return true -} - -func yaml_parser_scan_anchor(parser *yaml_parser_t, token *yaml_token_t, typ yaml_token_type_t) bool { - var s []byte - - // Eat the indicator character. - start_mark := parser.mark - skip(parser) - - // Consume the value. - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - - for is_alpha(parser.buffer, parser.buffer_pos) { - s = read(parser, s) - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - } - - end_mark := parser.mark - - /* - * Check if length of the anchor is greater than 0 and it is followed by - * a whitespace character or one of the indicators: - * - * '?', ':', ',', ']', '}', '%', '@', '`'. - */ - - if len(s) == 0 || - !(is_blankz(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == '?' || - parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == ',' || - parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '}' || - parser.buffer[parser.buffer_pos] == '%' || parser.buffer[parser.buffer_pos] == '@' || - parser.buffer[parser.buffer_pos] == '`') { - context := "while scanning an alias" - if typ == yaml_ANCHOR_TOKEN { - context = "while scanning an anchor" - } - yaml_parser_set_scanner_error(parser, context, start_mark, - "did not find expected alphabetic or numeric character") - return false - } - - // Create a token. - *token = yaml_token_t{ - typ: typ, - start_mark: start_mark, - end_mark: end_mark, - value: s, - } - - return true -} - -/* - * Scan a TAG token. - */ - -func yaml_parser_scan_tag(parser *yaml_parser_t, token *yaml_token_t) bool { - var handle, suffix []byte - - start_mark := parser.mark - - // Check if the tag is in the canonical form. - if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { - return false - } - - if parser.buffer[parser.buffer_pos+1] == '<' { - // Keep the handle as '' - - // Eat '!<' - skip(parser) - skip(parser) - - // Consume the tag value. - if !yaml_parser_scan_tag_uri(parser, false, nil, start_mark, &suffix) { - return false - } - - // Check for '>' and eat it. - if parser.buffer[parser.buffer_pos] != '>' { - yaml_parser_set_scanner_error(parser, "while scanning a tag", - start_mark, "did not find the expected '>'") - return false - } - - skip(parser) - } else { - // The tag has either the '!suffix' or the '!handle!suffix' form. - - // First, try to scan a handle. - if !yaml_parser_scan_tag_handle(parser, false, start_mark, &handle) { - return false - } - - // Check if it is, indeed, handle. - if handle[0] == '!' && len(handle) > 1 && handle[len(handle)-1] == '!' { - // Scan the suffix now. - if !yaml_parser_scan_tag_uri(parser, false, nil, start_mark, &suffix) { - return false - } - } else { - // It wasn't a handle after all. Scan the rest of the tag. - if !yaml_parser_scan_tag_uri(parser, false, handle, start_mark, &suffix) { - return false - } - - // Set the handle to '!'. - handle = []byte{'!'} - - // A special case: the '!' tag. Set the handle to '' and the - // suffix to '!'. - if len(suffix) == 0 { - handle, suffix = suffix, handle - } - } - } - - // Check the character which ends the tag. - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - if !is_blankz(parser.buffer, parser.buffer_pos) { - yaml_parser_set_scanner_error(parser, "while scanning a tag", - start_mark, "did not find expected whitespace or line break") - return false - } - - end_mark := parser.mark - - // Create a token. - *token = yaml_token_t{ - typ: yaml_TAG_TOKEN, - start_mark: start_mark, - end_mark: end_mark, - value: handle, - suffix: suffix, - } - return true -} - -// Scan a tag handle. -func yaml_parser_scan_tag_handle(parser *yaml_parser_t, directive bool, start_mark yaml_mark_t, handle *[]byte) bool { - // Check the initial '!' character. - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - if parser.buffer[parser.buffer_pos] != '!' { - yaml_parser_set_scanner_tag_error(parser, directive, - start_mark, "did not find expected '!'") - return false - } - - var s []byte - - // Copy the '!' character. - s = read(parser, s) - - // Copy all subsequent alphabetical and numerical characters. - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - for is_alpha(parser.buffer, parser.buffer_pos) { - s = read(parser, s) - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - } - - // Check if the trailing character is '!' and copy it. - if parser.buffer[parser.buffer_pos] == '!' { - s = read(parser, s) - } else { - // It's either the '!' tag or not really a tag handle. If it's a %TAG - // directive, it's an error. If it's a tag token, it must be a part of URI. - if directive && string(s) != "!" { - yaml_parser_set_scanner_tag_error(parser, directive, - start_mark, "did not find expected '!'") - return false - } - } - - *handle = s - return true -} - -// Scan a tag. -func yaml_parser_scan_tag_uri(parser *yaml_parser_t, directive bool, head []byte, start_mark yaml_mark_t, uri *[]byte) bool { - //size_t length = head ? strlen((char *)head) : 0 - var s []byte - hasTag := len(head) > 0 - - // Copy the head if needed. - // - // Note that we don't copy the leading '!' character. - if len(head) > 1 { - s = append(s, head[1:]...) - } - - // Scan the tag. - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - - // The set of characters that may appear in URI is as follows: - // - // '0'-'9', 'A'-'Z', 'a'-'z', '_', '-', ';', '/', '?', ':', '@', '&', - // '=', '+', '$', ',', '.', '!', '~', '*', '\'', '(', ')', '[', ']', - // '%'. - // [Go] TODO Convert this into more reasonable logic. - for is_alpha(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == ';' || - parser.buffer[parser.buffer_pos] == '/' || parser.buffer[parser.buffer_pos] == '?' || - parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == '@' || - parser.buffer[parser.buffer_pos] == '&' || parser.buffer[parser.buffer_pos] == '=' || - parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '$' || - parser.buffer[parser.buffer_pos] == ',' || parser.buffer[parser.buffer_pos] == '.' || - parser.buffer[parser.buffer_pos] == '!' || parser.buffer[parser.buffer_pos] == '~' || - parser.buffer[parser.buffer_pos] == '*' || parser.buffer[parser.buffer_pos] == '\'' || - parser.buffer[parser.buffer_pos] == '(' || parser.buffer[parser.buffer_pos] == ')' || - parser.buffer[parser.buffer_pos] == '[' || parser.buffer[parser.buffer_pos] == ']' || - parser.buffer[parser.buffer_pos] == '%' { - // Check if it is a URI-escape sequence. - if parser.buffer[parser.buffer_pos] == '%' { - if !yaml_parser_scan_uri_escapes(parser, directive, start_mark, &s) { - return false - } - } else { - s = read(parser, s) - } - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - hasTag = true - } - - if !hasTag { - yaml_parser_set_scanner_tag_error(parser, directive, - start_mark, "did not find expected tag URI") - return false - } - *uri = s - return true -} - -// Decode an URI-escape sequence corresponding to a single UTF-8 character. -func yaml_parser_scan_uri_escapes(parser *yaml_parser_t, directive bool, start_mark yaml_mark_t, s *[]byte) bool { - - // Decode the required number of characters. - w := 1024 - for w > 0 { - // Check for a URI-escaped octet. - if parser.unread < 3 && !yaml_parser_update_buffer(parser, 3) { - return false - } - - if !(parser.buffer[parser.buffer_pos] == '%' && - is_hex(parser.buffer, parser.buffer_pos+1) && - is_hex(parser.buffer, parser.buffer_pos+2)) { - return yaml_parser_set_scanner_tag_error(parser, directive, - start_mark, "did not find URI escaped octet") - } - - // Get the octet. - octet := byte((as_hex(parser.buffer, parser.buffer_pos+1) << 4) + as_hex(parser.buffer, parser.buffer_pos+2)) - - // If it is the leading octet, determine the length of the UTF-8 sequence. - if w == 1024 { - w = width(octet) - if w == 0 { - return yaml_parser_set_scanner_tag_error(parser, directive, - start_mark, "found an incorrect leading UTF-8 octet") - } - } else { - // Check if the trailing octet is correct. - if octet&0xC0 != 0x80 { - return yaml_parser_set_scanner_tag_error(parser, directive, - start_mark, "found an incorrect trailing UTF-8 octet") - } - } - - // Copy the octet and move the pointers. - *s = append(*s, octet) - skip(parser) - skip(parser) - skip(parser) - w-- - } - return true -} - -// Scan a block scalar. -func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, literal bool) bool { - // Eat the indicator '|' or '>'. - start_mark := parser.mark - skip(parser) - - // Scan the additional block scalar indicators. - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - - // Check for a chomping indicator. - var chomping, increment int - if parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '-' { - // Set the chomping method and eat the indicator. - if parser.buffer[parser.buffer_pos] == '+' { - chomping = +1 - } else { - chomping = -1 - } - skip(parser) - - // Check for an indentation indicator. - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - if is_digit(parser.buffer, parser.buffer_pos) { - // Check that the indentation is greater than 0. - if parser.buffer[parser.buffer_pos] == '0' { - yaml_parser_set_scanner_error(parser, "while scanning a block scalar", - start_mark, "found an indentation indicator equal to 0") - return false - } - - // Get the indentation level and eat the indicator. - increment = as_digit(parser.buffer, parser.buffer_pos) - skip(parser) - } - - } else if is_digit(parser.buffer, parser.buffer_pos) { - // Do the same as above, but in the opposite order. - - if parser.buffer[parser.buffer_pos] == '0' { - yaml_parser_set_scanner_error(parser, "while scanning a block scalar", - start_mark, "found an indentation indicator equal to 0") - return false - } - increment = as_digit(parser.buffer, parser.buffer_pos) - skip(parser) - - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - if parser.buffer[parser.buffer_pos] == '+' || parser.buffer[parser.buffer_pos] == '-' { - if parser.buffer[parser.buffer_pos] == '+' { - chomping = +1 - } else { - chomping = -1 - } - skip(parser) - } - } - - // Eat whitespaces and comments to the end of the line. - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - for is_blank(parser.buffer, parser.buffer_pos) { - skip(parser) - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - } - if parser.buffer[parser.buffer_pos] == '#' { - if !yaml_parser_scan_line_comment(parser, start_mark) { - return false - } - for !is_breakz(parser.buffer, parser.buffer_pos) { - skip(parser) - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - } - } - - // Check if we are at the end of the line. - if !is_breakz(parser.buffer, parser.buffer_pos) { - yaml_parser_set_scanner_error(parser, "while scanning a block scalar", - start_mark, "did not find expected comment or line break") - return false - } - - // Eat a line break. - if is_break(parser.buffer, parser.buffer_pos) { - if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { - return false - } - skip_line(parser) - } - - end_mark := parser.mark - - // Set the indentation level if it was specified. - var indent int - if increment > 0 { - if parser.indent >= 0 { - indent = parser.indent + increment - } else { - indent = increment - } - } - - // Scan the leading line breaks and determine the indentation level if needed. - var s, leading_break, trailing_breaks []byte - if !yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark) { - return false - } - - // Scan the block scalar content. - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - var leading_blank, trailing_blank bool - for parser.mark.column == indent && !is_z(parser.buffer, parser.buffer_pos) { - // We are at the beginning of a non-empty line. - - // Is it a trailing whitespace? - trailing_blank = is_blank(parser.buffer, parser.buffer_pos) - - // Check if we need to fold the leading line break. - if !literal && !leading_blank && !trailing_blank && len(leading_break) > 0 && leading_break[0] == '\n' { - // Do we need to join the lines by space? - if len(trailing_breaks) == 0 { - s = append(s, ' ') - } - } else { - s = append(s, leading_break...) - } - leading_break = leading_break[:0] - - // Append the remaining line breaks. - s = append(s, trailing_breaks...) - trailing_breaks = trailing_breaks[:0] - - // Is it a leading whitespace? - leading_blank = is_blank(parser.buffer, parser.buffer_pos) - - // Consume the current line. - for !is_breakz(parser.buffer, parser.buffer_pos) { - s = read(parser, s) - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - } - - // Consume the line break. - if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { - return false - } - - leading_break = read_line(parser, leading_break) - - // Eat the following indentation spaces and line breaks. - if !yaml_parser_scan_block_scalar_breaks(parser, &indent, &trailing_breaks, start_mark, &end_mark) { - return false - } - } - - // Chomp the tail. - if chomping != -1 { - s = append(s, leading_break...) - } - if chomping == 1 { - s = append(s, trailing_breaks...) - } - - // Create a token. - *token = yaml_token_t{ - typ: yaml_SCALAR_TOKEN, - start_mark: start_mark, - end_mark: end_mark, - value: s, - style: yaml_LITERAL_SCALAR_STYLE, - } - if !literal { - token.style = yaml_FOLDED_SCALAR_STYLE - } - return true -} - -// Scan indentation spaces and line breaks for a block scalar. Determine the -// indentation level if needed. -func yaml_parser_scan_block_scalar_breaks(parser *yaml_parser_t, indent *int, breaks *[]byte, start_mark yaml_mark_t, end_mark *yaml_mark_t) bool { - *end_mark = parser.mark - - // Eat the indentation spaces and line breaks. - max_indent := 0 - for { - // Eat the indentation spaces. - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - for (*indent == 0 || parser.mark.column < *indent) && is_space(parser.buffer, parser.buffer_pos) { - skip(parser) - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - } - if parser.mark.column > max_indent { - max_indent = parser.mark.column - } - - // Check for a tab character messing the indentation. - if (*indent == 0 || parser.mark.column < *indent) && is_tab(parser.buffer, parser.buffer_pos) { - return yaml_parser_set_scanner_error(parser, "while scanning a block scalar", - start_mark, "found a tab character where an indentation space is expected") - } - - // Have we found a non-empty line? - if !is_break(parser.buffer, parser.buffer_pos) { - break - } - - // Consume the line break. - if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { - return false - } - // [Go] Should really be returning breaks instead. - *breaks = read_line(parser, *breaks) - *end_mark = parser.mark - } - - // Determine the indentation level if needed. - if *indent == 0 { - *indent = max_indent - if *indent < parser.indent+1 { - *indent = parser.indent + 1 - } - if *indent < 1 { - *indent = 1 - } - } - return true -} - -// Scan a quoted scalar. -func yaml_parser_scan_flow_scalar(parser *yaml_parser_t, token *yaml_token_t, single bool) bool { - // Eat the left quote. - start_mark := parser.mark - skip(parser) - - // Consume the content of the quoted scalar. - var s, leading_break, trailing_breaks, whitespaces []byte - for { - // Check that there are no document indicators at the beginning of the line. - if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) { - return false - } - - if parser.mark.column == 0 && - ((parser.buffer[parser.buffer_pos+0] == '-' && - parser.buffer[parser.buffer_pos+1] == '-' && - parser.buffer[parser.buffer_pos+2] == '-') || - (parser.buffer[parser.buffer_pos+0] == '.' && - parser.buffer[parser.buffer_pos+1] == '.' && - parser.buffer[parser.buffer_pos+2] == '.')) && - is_blankz(parser.buffer, parser.buffer_pos+3) { - yaml_parser_set_scanner_error(parser, "while scanning a quoted scalar", - start_mark, "found unexpected document indicator") - return false - } - - // Check for EOF. - if is_z(parser.buffer, parser.buffer_pos) { - yaml_parser_set_scanner_error(parser, "while scanning a quoted scalar", - start_mark, "found unexpected end of stream") - return false - } - - // Consume non-blank characters. - leading_blanks := false - for !is_blankz(parser.buffer, parser.buffer_pos) { - if single && parser.buffer[parser.buffer_pos] == '\'' && parser.buffer[parser.buffer_pos+1] == '\'' { - // Is is an escaped single quote. - s = append(s, '\'') - skip(parser) - skip(parser) - - } else if single && parser.buffer[parser.buffer_pos] == '\'' { - // It is a right single quote. - break - } else if !single && parser.buffer[parser.buffer_pos] == '"' { - // It is a right double quote. - break - - } else if !single && parser.buffer[parser.buffer_pos] == '\\' && is_break(parser.buffer, parser.buffer_pos+1) { - // It is an escaped line break. - if parser.unread < 3 && !yaml_parser_update_buffer(parser, 3) { - return false - } - skip(parser) - skip_line(parser) - leading_blanks = true - break - - } else if !single && parser.buffer[parser.buffer_pos] == '\\' { - // It is an escape sequence. - code_length := 0 - - // Check the escape character. - switch parser.buffer[parser.buffer_pos+1] { - case '0': - s = append(s, 0) - case 'a': - s = append(s, '\x07') - case 'b': - s = append(s, '\x08') - case 't', '\t': - s = append(s, '\x09') - case 'n': - s = append(s, '\x0A') - case 'v': - s = append(s, '\x0B') - case 'f': - s = append(s, '\x0C') - case 'r': - s = append(s, '\x0D') - case 'e': - s = append(s, '\x1B') - case ' ': - s = append(s, '\x20') - case '"': - s = append(s, '"') - case '\'': - s = append(s, '\'') - case '\\': - s = append(s, '\\') - case 'N': // NEL (#x85) - s = append(s, '\xC2') - s = append(s, '\x85') - case '_': // #xA0 - s = append(s, '\xC2') - s = append(s, '\xA0') - case 'L': // LS (#x2028) - s = append(s, '\xE2') - s = append(s, '\x80') - s = append(s, '\xA8') - case 'P': // PS (#x2029) - s = append(s, '\xE2') - s = append(s, '\x80') - s = append(s, '\xA9') - case 'x': - code_length = 2 - case 'u': - code_length = 4 - case 'U': - code_length = 8 - default: - yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", - start_mark, "found unknown escape character") - return false - } - - skip(parser) - skip(parser) - - // Consume an arbitrary escape code. - if code_length > 0 { - var value int - - // Scan the character value. - if parser.unread < code_length && !yaml_parser_update_buffer(parser, code_length) { - return false - } - for k := 0; k < code_length; k++ { - if !is_hex(parser.buffer, parser.buffer_pos+k) { - yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", - start_mark, "did not find expected hexdecimal number") - return false - } - value = (value << 4) + as_hex(parser.buffer, parser.buffer_pos+k) - } - - // Check the value and write the character. - if (value >= 0xD800 && value <= 0xDFFF) || value > 0x10FFFF { - yaml_parser_set_scanner_error(parser, "while parsing a quoted scalar", - start_mark, "found invalid Unicode character escape code") - return false - } - if value <= 0x7F { - s = append(s, byte(value)) - } else if value <= 0x7FF { - s = append(s, byte(0xC0+(value>>6))) - s = append(s, byte(0x80+(value&0x3F))) - } else if value <= 0xFFFF { - s = append(s, byte(0xE0+(value>>12))) - s = append(s, byte(0x80+((value>>6)&0x3F))) - s = append(s, byte(0x80+(value&0x3F))) - } else { - s = append(s, byte(0xF0+(value>>18))) - s = append(s, byte(0x80+((value>>12)&0x3F))) - s = append(s, byte(0x80+((value>>6)&0x3F))) - s = append(s, byte(0x80+(value&0x3F))) - } - - // Advance the pointer. - for k := 0; k < code_length; k++ { - skip(parser) - } - } - } else { - // It is a non-escaped non-blank character. - s = read(parser, s) - } - if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { - return false - } - } - - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - - // Check if we are at the end of the scalar. - if single { - if parser.buffer[parser.buffer_pos] == '\'' { - break - } - } else { - if parser.buffer[parser.buffer_pos] == '"' { - break - } - } - - // Consume blank characters. - for is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos) { - if is_blank(parser.buffer, parser.buffer_pos) { - // Consume a space or a tab character. - if !leading_blanks { - whitespaces = read(parser, whitespaces) - } else { - skip(parser) - } - } else { - if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { - return false - } - - // Check if it is a first line break. - if !leading_blanks { - whitespaces = whitespaces[:0] - leading_break = read_line(parser, leading_break) - leading_blanks = true - } else { - trailing_breaks = read_line(parser, trailing_breaks) - } - } - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - } - - // Join the whitespaces or fold line breaks. - if leading_blanks { - // Do we need to fold line breaks? - if len(leading_break) > 0 && leading_break[0] == '\n' { - if len(trailing_breaks) == 0 { - s = append(s, ' ') - } else { - s = append(s, trailing_breaks...) - } - } else { - s = append(s, leading_break...) - s = append(s, trailing_breaks...) - } - trailing_breaks = trailing_breaks[:0] - leading_break = leading_break[:0] - } else { - s = append(s, whitespaces...) - whitespaces = whitespaces[:0] - } - } - - // Eat the right quote. - skip(parser) - end_mark := parser.mark - - // Create a token. - *token = yaml_token_t{ - typ: yaml_SCALAR_TOKEN, - start_mark: start_mark, - end_mark: end_mark, - value: s, - style: yaml_SINGLE_QUOTED_SCALAR_STYLE, - } - if !single { - token.style = yaml_DOUBLE_QUOTED_SCALAR_STYLE - } - return true -} - -// Scan a plain scalar. -func yaml_parser_scan_plain_scalar(parser *yaml_parser_t, token *yaml_token_t) bool { - - var s, leading_break, trailing_breaks, whitespaces []byte - var leading_blanks bool - var indent = parser.indent + 1 - - start_mark := parser.mark - end_mark := parser.mark - - // Consume the content of the plain scalar. - for { - // Check for a document indicator. - if parser.unread < 4 && !yaml_parser_update_buffer(parser, 4) { - return false - } - if parser.mark.column == 0 && - ((parser.buffer[parser.buffer_pos+0] == '-' && - parser.buffer[parser.buffer_pos+1] == '-' && - parser.buffer[parser.buffer_pos+2] == '-') || - (parser.buffer[parser.buffer_pos+0] == '.' && - parser.buffer[parser.buffer_pos+1] == '.' && - parser.buffer[parser.buffer_pos+2] == '.')) && - is_blankz(parser.buffer, parser.buffer_pos+3) { - break - } - - // Check for a comment. - if parser.buffer[parser.buffer_pos] == '#' { - break - } - - // Consume non-blank characters. - for !is_blankz(parser.buffer, parser.buffer_pos) { - - // Check for indicators that may end a plain scalar. - if (parser.buffer[parser.buffer_pos] == ':' && is_blankz(parser.buffer, parser.buffer_pos+1)) || - (parser.flow_level > 0 && - (parser.buffer[parser.buffer_pos] == ',' || - parser.buffer[parser.buffer_pos] == '?' || parser.buffer[parser.buffer_pos] == '[' || - parser.buffer[parser.buffer_pos] == ']' || parser.buffer[parser.buffer_pos] == '{' || - parser.buffer[parser.buffer_pos] == '}')) { - break - } - - // Check if we need to join whitespaces and breaks. - if leading_blanks || len(whitespaces) > 0 { - if leading_blanks { - // Do we need to fold line breaks? - if leading_break[0] == '\n' { - if len(trailing_breaks) == 0 { - s = append(s, ' ') - } else { - s = append(s, trailing_breaks...) - } - } else { - s = append(s, leading_break...) - s = append(s, trailing_breaks...) - } - trailing_breaks = trailing_breaks[:0] - leading_break = leading_break[:0] - leading_blanks = false - } else { - s = append(s, whitespaces...) - whitespaces = whitespaces[:0] - } - } - - // Copy the character. - s = read(parser, s) - - end_mark = parser.mark - if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { - return false - } - } - - // Is it the end? - if !(is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos)) { - break - } - - // Consume blank characters. - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - - for is_blank(parser.buffer, parser.buffer_pos) || is_break(parser.buffer, parser.buffer_pos) { - if is_blank(parser.buffer, parser.buffer_pos) { - - // Check for tab characters that abuse indentation. - if leading_blanks && parser.mark.column < indent && is_tab(parser.buffer, parser.buffer_pos) { - yaml_parser_set_scanner_error(parser, "while scanning a plain scalar", - start_mark, "found a tab character that violates indentation") - return false - } - - // Consume a space or a tab character. - if !leading_blanks { - whitespaces = read(parser, whitespaces) - } else { - skip(parser) - } - } else { - if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { - return false - } - - // Check if it is a first line break. - if !leading_blanks { - whitespaces = whitespaces[:0] - leading_break = read_line(parser, leading_break) - leading_blanks = true - } else { - trailing_breaks = read_line(parser, trailing_breaks) - } - } - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - } - - // Check indentation level. - if parser.flow_level == 0 && parser.mark.column < indent { - break - } - } - - // Create a token. - *token = yaml_token_t{ - typ: yaml_SCALAR_TOKEN, - start_mark: start_mark, - end_mark: end_mark, - value: s, - style: yaml_PLAIN_SCALAR_STYLE, - } - - // Note that we change the 'simple_key_allowed' flag. - if leading_blanks { - parser.simple_key_allowed = true - } - return true -} - -func yaml_parser_scan_line_comment(parser *yaml_parser_t, token_mark yaml_mark_t) bool { - if parser.newlines > 0 { - return true - } - - var start_mark yaml_mark_t - var text []byte - - for peek := 0; peek < 512; peek++ { - if parser.unread < peek+1 && !yaml_parser_update_buffer(parser, peek+1) { - break - } - if is_blank(parser.buffer, parser.buffer_pos+peek) { - continue - } - if parser.buffer[parser.buffer_pos+peek] == '#' { - seen := parser.mark.index + peek - for { - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - if is_breakz(parser.buffer, parser.buffer_pos) { - if parser.mark.index >= seen { - break - } - if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { - return false - } - skip_line(parser) - } else if parser.mark.index >= seen { - if len(text) == 0 { - start_mark = parser.mark - } - text = read(parser, text) - } else { - skip(parser) - } - } - } - break - } - if len(text) > 0 { - parser.comments = append(parser.comments, yaml_comment_t{ - token_mark: token_mark, - start_mark: start_mark, - line: text, - }) - } - return true -} - -func yaml_parser_scan_comments(parser *yaml_parser_t, scan_mark yaml_mark_t) bool { - token := parser.tokens[len(parser.tokens)-1] - - if token.typ == yaml_FLOW_ENTRY_TOKEN && len(parser.tokens) > 1 { - token = parser.tokens[len(parser.tokens)-2] - } - - var token_mark = token.start_mark - var start_mark yaml_mark_t - var next_indent = parser.indent - if next_indent < 0 { - next_indent = 0 - } - - var recent_empty = false - var first_empty = parser.newlines <= 1 - - var line = parser.mark.line - var column = parser.mark.column - - var text []byte - - // The foot line is the place where a comment must start to - // still be considered as a foot of the prior content. - // If there's some content in the currently parsed line, then - // the foot is the line below it. - var foot_line = -1 - if scan_mark.line > 0 { - foot_line = parser.mark.line - parser.newlines + 1 - if parser.newlines == 0 && parser.mark.column > 1 { - foot_line++ - } - } - - var peek = 0 - for ; peek < 512; peek++ { - if parser.unread < peek+1 && !yaml_parser_update_buffer(parser, peek+1) { - break - } - column++ - if is_blank(parser.buffer, parser.buffer_pos+peek) { - continue - } - c := parser.buffer[parser.buffer_pos+peek] - var close_flow = parser.flow_level > 0 && (c == ']' || c == '}') - if close_flow || is_breakz(parser.buffer, parser.buffer_pos+peek) { - // Got line break or terminator. - if close_flow || !recent_empty { - if close_flow || first_empty && (start_mark.line == foot_line && token.typ != yaml_VALUE_TOKEN || start_mark.column-1 < next_indent) { - // This is the first empty line and there were no empty lines before, - // so this initial part of the comment is a foot of the prior token - // instead of being a head for the following one. Split it up. - // Alternatively, this might also be the last comment inside a flow - // scope, so it must be a footer. - if len(text) > 0 { - if start_mark.column-1 < next_indent { - // If dedented it's unrelated to the prior token. - token_mark = start_mark - } - parser.comments = append(parser.comments, yaml_comment_t{ - scan_mark: scan_mark, - token_mark: token_mark, - start_mark: start_mark, - end_mark: yaml_mark_t{parser.mark.index + peek, line, column}, - foot: text, - }) - scan_mark = yaml_mark_t{parser.mark.index + peek, line, column} - token_mark = scan_mark - text = nil - } - } else { - if len(text) > 0 && parser.buffer[parser.buffer_pos+peek] != 0 { - text = append(text, '\n') - } - } - } - if !is_break(parser.buffer, parser.buffer_pos+peek) { - break - } - first_empty = false - recent_empty = true - column = 0 - line++ - continue - } - - if len(text) > 0 && (close_flow || column-1 < next_indent && column != start_mark.column) { - // The comment at the different indentation is a foot of the - // preceding data rather than a head of the upcoming one. - parser.comments = append(parser.comments, yaml_comment_t{ - scan_mark: scan_mark, - token_mark: token_mark, - start_mark: start_mark, - end_mark: yaml_mark_t{parser.mark.index + peek, line, column}, - foot: text, - }) - scan_mark = yaml_mark_t{parser.mark.index + peek, line, column} - token_mark = scan_mark - text = nil - } - - if parser.buffer[parser.buffer_pos+peek] != '#' { - break - } - - if len(text) == 0 { - start_mark = yaml_mark_t{parser.mark.index + peek, line, column} - } else { - text = append(text, '\n') - } - - recent_empty = false - - // Consume until after the consumed comment line. - seen := parser.mark.index + peek - for { - if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) { - return false - } - if is_breakz(parser.buffer, parser.buffer_pos) { - if parser.mark.index >= seen { - break - } - if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) { - return false - } - skip_line(parser) - } else if parser.mark.index >= seen { - text = read(parser, text) - } else { - skip(parser) - } - } - - peek = 0 - column = 0 - line = parser.mark.line - next_indent = parser.indent - if next_indent < 0 { - next_indent = 0 - } - } - - if len(text) > 0 { - parser.comments = append(parser.comments, yaml_comment_t{ - scan_mark: scan_mark, - token_mark: start_mark, - start_mark: start_mark, - end_mark: yaml_mark_t{parser.mark.index + peek - 1, line, column}, - head: text, - }) - } - return true -} diff --git a/vendor/go.yaml.in/yaml/v3/sorter.go b/vendor/go.yaml.in/yaml/v3/sorter.go deleted file mode 100644 index 9210ece7e..000000000 --- a/vendor/go.yaml.in/yaml/v3/sorter.go +++ /dev/null @@ -1,134 +0,0 @@ -// -// Copyright (c) 2011-2019 Canonical Ltd -// -// 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 yaml - -import ( - "reflect" - "unicode" -) - -type keyList []reflect.Value - -func (l keyList) Len() int { return len(l) } -func (l keyList) Swap(i, j int) { l[i], l[j] = l[j], l[i] } -func (l keyList) Less(i, j int) bool { - a := l[i] - b := l[j] - ak := a.Kind() - bk := b.Kind() - for (ak == reflect.Interface || ak == reflect.Ptr) && !a.IsNil() { - a = a.Elem() - ak = a.Kind() - } - for (bk == reflect.Interface || bk == reflect.Ptr) && !b.IsNil() { - b = b.Elem() - bk = b.Kind() - } - af, aok := keyFloat(a) - bf, bok := keyFloat(b) - if aok && bok { - if af != bf { - return af < bf - } - if ak != bk { - return ak < bk - } - return numLess(a, b) - } - if ak != reflect.String || bk != reflect.String { - return ak < bk - } - ar, br := []rune(a.String()), []rune(b.String()) - digits := false - for i := 0; i < len(ar) && i < len(br); i++ { - if ar[i] == br[i] { - digits = unicode.IsDigit(ar[i]) - continue - } - al := unicode.IsLetter(ar[i]) - bl := unicode.IsLetter(br[i]) - if al && bl { - return ar[i] < br[i] - } - if al || bl { - if digits { - return al - } else { - return bl - } - } - var ai, bi int - var an, bn int64 - if ar[i] == '0' || br[i] == '0' { - for j := i - 1; j >= 0 && unicode.IsDigit(ar[j]); j-- { - if ar[j] != '0' { - an = 1 - bn = 1 - break - } - } - } - for ai = i; ai < len(ar) && unicode.IsDigit(ar[ai]); ai++ { - an = an*10 + int64(ar[ai]-'0') - } - for bi = i; bi < len(br) && unicode.IsDigit(br[bi]); bi++ { - bn = bn*10 + int64(br[bi]-'0') - } - if an != bn { - return an < bn - } - if ai != bi { - return ai < bi - } - return ar[i] < br[i] - } - return len(ar) < len(br) -} - -// keyFloat returns a float value for v if it is a number/bool -// and whether it is a number/bool or not. -func keyFloat(v reflect.Value) (f float64, ok bool) { - switch v.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return float64(v.Int()), true - case reflect.Float32, reflect.Float64: - return v.Float(), true - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return float64(v.Uint()), true - case reflect.Bool: - if v.Bool() { - return 1, true - } - return 0, true - } - return 0, false -} - -// numLess returns whether a < b. -// a and b must necessarily have the same kind. -func numLess(a, b reflect.Value) bool { - switch a.Kind() { - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return a.Int() < b.Int() - case reflect.Float32, reflect.Float64: - return a.Float() < b.Float() - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return a.Uint() < b.Uint() - case reflect.Bool: - return !a.Bool() && b.Bool() - } - panic("not a number") -} diff --git a/vendor/go.yaml.in/yaml/v3/writerc.go b/vendor/go.yaml.in/yaml/v3/writerc.go deleted file mode 100644 index 266d0b092..000000000 --- a/vendor/go.yaml.in/yaml/v3/writerc.go +++ /dev/null @@ -1,48 +0,0 @@ -// -// Copyright (c) 2011-2019 Canonical Ltd -// Copyright (c) 2006-2010 Kirill Simonov -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package yaml - -// Set the writer error and return false. -func yaml_emitter_set_writer_error(emitter *yaml_emitter_t, problem string) bool { - emitter.error = yaml_WRITER_ERROR - emitter.problem = problem - return false -} - -// Flush the output buffer. -func yaml_emitter_flush(emitter *yaml_emitter_t) bool { - if emitter.write_handler == nil { - panic("write handler not set") - } - - // Check if the buffer is empty. - if emitter.buffer_pos == 0 { - return true - } - - if err := emitter.write_handler(emitter, emitter.buffer[:emitter.buffer_pos]); err != nil { - return yaml_emitter_set_writer_error(emitter, "write error: "+err.Error()) - } - emitter.buffer_pos = 0 - return true -} diff --git a/vendor/go.yaml.in/yaml/v3/yaml.go b/vendor/go.yaml.in/yaml/v3/yaml.go deleted file mode 100644 index 0b101cd20..000000000 --- a/vendor/go.yaml.in/yaml/v3/yaml.go +++ /dev/null @@ -1,703 +0,0 @@ -// -// Copyright (c) 2011-2019 Canonical Ltd -// -// 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 yaml implements YAML support for the Go language. -// -// Source code and other details for the project are available at GitHub: -// -// https://github.com/yaml/go-yaml -package yaml - -import ( - "errors" - "fmt" - "io" - "reflect" - "strings" - "sync" - "unicode/utf8" -) - -// The Unmarshaler interface may be implemented by types to customize their -// behavior when being unmarshaled from a YAML document. -type Unmarshaler interface { - UnmarshalYAML(value *Node) error -} - -type obsoleteUnmarshaler interface { - UnmarshalYAML(unmarshal func(interface{}) error) error -} - -// The Marshaler interface may be implemented by types to customize their -// behavior when being marshaled into a YAML document. The returned value -// is marshaled in place of the original value implementing Marshaler. -// -// If an error is returned by MarshalYAML, the marshaling procedure stops -// and returns with the provided error. -type Marshaler interface { - MarshalYAML() (interface{}, error) -} - -// Unmarshal decodes the first document found within the in byte slice -// and assigns decoded values into the out value. -// -// Maps and pointers (to a struct, string, int, etc) are accepted as out -// values. If an internal pointer within a struct is not initialized, -// the yaml package will initialize it if necessary for unmarshalling -// the provided data. The out parameter must not be nil. -// -// The type of the decoded values should be compatible with the respective -// values in out. If one or more values cannot be decoded due to a type -// mismatches, decoding continues partially until the end of the YAML -// content, and a *yaml.TypeError is returned with details for all -// missed values. -// -// Struct fields are only unmarshalled if they are exported (have an -// upper case first letter), and are unmarshalled using the field name -// lowercased as the default key. Custom keys may be defined via the -// "yaml" name in the field tag: the content preceding the first comma -// is used as the key, and the following comma-separated options are -// used to tweak the marshalling process (see Marshal). -// Conflicting names result in a runtime error. -// -// For example: -// -// type T struct { -// F int `yaml:"a,omitempty"` -// B int -// } -// var t T -// yaml.Unmarshal([]byte("a: 1\nb: 2"), &t) -// -// See the documentation of Marshal for the format of tags and a list of -// supported tag options. -func Unmarshal(in []byte, out interface{}) (err error) { - return unmarshal(in, out, false) -} - -// A Decoder reads and decodes YAML values from an input stream. -type Decoder struct { - parser *parser - knownFields bool -} - -// NewDecoder returns a new decoder that reads from r. -// -// The decoder introduces its own buffering and may read -// data from r beyond the YAML values requested. -func NewDecoder(r io.Reader) *Decoder { - return &Decoder{ - parser: newParserFromReader(r), - } -} - -// KnownFields ensures that the keys in decoded mappings to -// exist as fields in the struct being decoded into. -func (dec *Decoder) KnownFields(enable bool) { - dec.knownFields = enable -} - -// Decode reads the next YAML-encoded value from its input -// and stores it in the value pointed to by v. -// -// See the documentation for Unmarshal for details about the -// conversion of YAML into a Go value. -func (dec *Decoder) Decode(v interface{}) (err error) { - d := newDecoder() - d.knownFields = dec.knownFields - defer handleErr(&err) - node := dec.parser.parse() - if node == nil { - return io.EOF - } - out := reflect.ValueOf(v) - if out.Kind() == reflect.Ptr && !out.IsNil() { - out = out.Elem() - } - d.unmarshal(node, out) - if len(d.terrors) > 0 { - return &TypeError{d.terrors} - } - return nil -} - -// Decode decodes the node and stores its data into the value pointed to by v. -// -// See the documentation for Unmarshal for details about the -// conversion of YAML into a Go value. -func (n *Node) Decode(v interface{}) (err error) { - d := newDecoder() - defer handleErr(&err) - out := reflect.ValueOf(v) - if out.Kind() == reflect.Ptr && !out.IsNil() { - out = out.Elem() - } - d.unmarshal(n, out) - if len(d.terrors) > 0 { - return &TypeError{d.terrors} - } - return nil -} - -func unmarshal(in []byte, out interface{}, strict bool) (err error) { - defer handleErr(&err) - d := newDecoder() - p := newParser(in) - defer p.destroy() - node := p.parse() - if node != nil { - v := reflect.ValueOf(out) - if v.Kind() == reflect.Ptr && !v.IsNil() { - v = v.Elem() - } - d.unmarshal(node, v) - } - if len(d.terrors) > 0 { - return &TypeError{d.terrors} - } - return nil -} - -// Marshal serializes the value provided into a YAML document. The structure -// of the generated document will reflect the structure of the value itself. -// Maps and pointers (to struct, string, int, etc) are accepted as the in value. -// -// Struct fields are only marshalled if they are exported (have an upper case -// first letter), and are marshalled using the field name lowercased as the -// default key. Custom keys may be defined via the "yaml" name in the field -// tag: the content preceding the first comma is used as the key, and the -// following comma-separated options are used to tweak the marshalling process. -// Conflicting names result in a runtime error. -// -// The field tag format accepted is: -// -// `(...) yaml:"[][,[,]]" (...)` -// -// The following flags are currently supported: -// -// omitempty Only include the field if it's not set to the zero -// value for the type or to empty slices or maps. -// Zero valued structs will be omitted if all their public -// fields are zero, unless they implement an IsZero -// method (see the IsZeroer interface type), in which -// case the field will be excluded if IsZero returns true. -// -// flow Marshal using a flow style (useful for structs, -// sequences and maps). -// -// inline Inline the field, which must be a struct or a map, -// causing all of its fields or keys to be processed as if -// they were part of the outer struct. For maps, keys must -// not conflict with the yaml keys of other struct fields. -// -// In addition, if the key is "-", the field is ignored. -// -// For example: -// -// type T struct { -// F int `yaml:"a,omitempty"` -// B int -// } -// yaml.Marshal(&T{B: 2}) // Returns "b: 2\n" -// yaml.Marshal(&T{F: 1}} // Returns "a: 1\nb: 0\n" -func Marshal(in interface{}) (out []byte, err error) { - defer handleErr(&err) - e := newEncoder() - defer e.destroy() - e.marshalDoc("", reflect.ValueOf(in)) - e.finish() - out = e.out - return -} - -// An Encoder writes YAML values to an output stream. -type Encoder struct { - encoder *encoder -} - -// NewEncoder returns a new encoder that writes to w. -// The Encoder should be closed after use to flush all data -// to w. -func NewEncoder(w io.Writer) *Encoder { - return &Encoder{ - encoder: newEncoderWithWriter(w), - } -} - -// Encode writes the YAML encoding of v to the stream. -// If multiple items are encoded to the stream, the -// second and subsequent document will be preceded -// with a "---" document separator, but the first will not. -// -// See the documentation for Marshal for details about the conversion of Go -// values to YAML. -func (e *Encoder) Encode(v interface{}) (err error) { - defer handleErr(&err) - e.encoder.marshalDoc("", reflect.ValueOf(v)) - return nil -} - -// Encode encodes value v and stores its representation in n. -// -// See the documentation for Marshal for details about the -// conversion of Go values into YAML. -func (n *Node) Encode(v interface{}) (err error) { - defer handleErr(&err) - e := newEncoder() - defer e.destroy() - e.marshalDoc("", reflect.ValueOf(v)) - e.finish() - p := newParser(e.out) - p.textless = true - defer p.destroy() - doc := p.parse() - *n = *doc.Content[0] - return nil -} - -// SetIndent changes the used indentation used when encoding. -func (e *Encoder) SetIndent(spaces int) { - if spaces < 0 { - panic("yaml: cannot indent to a negative number of spaces") - } - e.encoder.indent = spaces -} - -// CompactSeqIndent makes it so that '- ' is considered part of the indentation. -func (e *Encoder) CompactSeqIndent() { - e.encoder.emitter.compact_sequence_indent = true -} - -// DefaultSeqIndent makes it so that '- ' is not considered part of the indentation. -func (e *Encoder) DefaultSeqIndent() { - e.encoder.emitter.compact_sequence_indent = false -} - -// Close closes the encoder by writing any remaining data. -// It does not write a stream terminating string "...". -func (e *Encoder) Close() (err error) { - defer handleErr(&err) - e.encoder.finish() - return nil -} - -func handleErr(err *error) { - if v := recover(); v != nil { - if e, ok := v.(yamlError); ok { - *err = e.err - } else { - panic(v) - } - } -} - -type yamlError struct { - err error -} - -func fail(err error) { - panic(yamlError{err}) -} - -func failf(format string, args ...interface{}) { - panic(yamlError{fmt.Errorf("yaml: "+format, args...)}) -} - -// A TypeError is returned by Unmarshal when one or more fields in -// the YAML document cannot be properly decoded into the requested -// types. When this error is returned, the value is still -// unmarshaled partially. -type TypeError struct { - Errors []string -} - -func (e *TypeError) Error() string { - return fmt.Sprintf("yaml: unmarshal errors:\n %s", strings.Join(e.Errors, "\n ")) -} - -type Kind uint32 - -const ( - DocumentNode Kind = 1 << iota - SequenceNode - MappingNode - ScalarNode - AliasNode -) - -type Style uint32 - -const ( - TaggedStyle Style = 1 << iota - DoubleQuotedStyle - SingleQuotedStyle - LiteralStyle - FoldedStyle - FlowStyle -) - -// Node represents an element in the YAML document hierarchy. While documents -// are typically encoded and decoded into higher level types, such as structs -// and maps, Node is an intermediate representation that allows detailed -// control over the content being decoded or encoded. -// -// It's worth noting that although Node offers access into details such as -// line numbers, colums, and comments, the content when re-encoded will not -// have its original textual representation preserved. An effort is made to -// render the data plesantly, and to preserve comments near the data they -// describe, though. -// -// Values that make use of the Node type interact with the yaml package in the -// same way any other type would do, by encoding and decoding yaml data -// directly or indirectly into them. -// -// For example: -// -// var person struct { -// Name string -// Address yaml.Node -// } -// err := yaml.Unmarshal(data, &person) -// -// Or by itself: -// -// var person Node -// err := yaml.Unmarshal(data, &person) -type Node struct { - // Kind defines whether the node is a document, a mapping, a sequence, - // a scalar value, or an alias to another node. The specific data type of - // scalar nodes may be obtained via the ShortTag and LongTag methods. - Kind Kind - - // Style allows customizing the apperance of the node in the tree. - Style Style - - // Tag holds the YAML tag defining the data type for the value. - // When decoding, this field will always be set to the resolved tag, - // even when it wasn't explicitly provided in the YAML content. - // When encoding, if this field is unset the value type will be - // implied from the node properties, and if it is set, it will only - // be serialized into the representation if TaggedStyle is used or - // the implicit tag diverges from the provided one. - Tag string - - // Value holds the unescaped and unquoted represenation of the value. - Value string - - // Anchor holds the anchor name for this node, which allows aliases to point to it. - Anchor string - - // Alias holds the node that this alias points to. Only valid when Kind is AliasNode. - Alias *Node - - // Content holds contained nodes for documents, mappings, and sequences. - Content []*Node - - // HeadComment holds any comments in the lines preceding the node and - // not separated by an empty line. - HeadComment string - - // LineComment holds any comments at the end of the line where the node is in. - LineComment string - - // FootComment holds any comments following the node and before empty lines. - FootComment string - - // Line and Column hold the node position in the decoded YAML text. - // These fields are not respected when encoding the node. - Line int - Column int -} - -// IsZero returns whether the node has all of its fields unset. -func (n *Node) IsZero() bool { - return n.Kind == 0 && n.Style == 0 && n.Tag == "" && n.Value == "" && n.Anchor == "" && n.Alias == nil && n.Content == nil && - n.HeadComment == "" && n.LineComment == "" && n.FootComment == "" && n.Line == 0 && n.Column == 0 -} - -// LongTag returns the long form of the tag that indicates the data type for -// the node. If the Tag field isn't explicitly defined, one will be computed -// based on the node properties. -func (n *Node) LongTag() string { - return longTag(n.ShortTag()) -} - -// ShortTag returns the short form of the YAML tag that indicates data type for -// the node. If the Tag field isn't explicitly defined, one will be computed -// based on the node properties. -func (n *Node) ShortTag() string { - if n.indicatedString() { - return strTag - } - if n.Tag == "" || n.Tag == "!" { - switch n.Kind { - case MappingNode: - return mapTag - case SequenceNode: - return seqTag - case AliasNode: - if n.Alias != nil { - return n.Alias.ShortTag() - } - case ScalarNode: - tag, _ := resolve("", n.Value) - return tag - case 0: - // Special case to make the zero value convenient. - if n.IsZero() { - return nullTag - } - } - return "" - } - return shortTag(n.Tag) -} - -func (n *Node) indicatedString() bool { - return n.Kind == ScalarNode && - (shortTag(n.Tag) == strTag || - (n.Tag == "" || n.Tag == "!") && n.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0) -} - -// SetString is a convenience function that sets the node to a string value -// and defines its style in a pleasant way depending on its content. -func (n *Node) SetString(s string) { - n.Kind = ScalarNode - if utf8.ValidString(s) { - n.Value = s - n.Tag = strTag - } else { - n.Value = encodeBase64(s) - n.Tag = binaryTag - } - if strings.Contains(n.Value, "\n") { - n.Style = LiteralStyle - } -} - -// -------------------------------------------------------------------------- -// Maintain a mapping of keys to structure field indexes - -// The code in this section was copied from mgo/bson. - -// structInfo holds details for the serialization of fields of -// a given struct. -type structInfo struct { - FieldsMap map[string]fieldInfo - FieldsList []fieldInfo - - // InlineMap is the number of the field in the struct that - // contains an ,inline map, or -1 if there's none. - InlineMap int - - // InlineUnmarshalers holds indexes to inlined fields that - // contain unmarshaler values. - InlineUnmarshalers [][]int -} - -type fieldInfo struct { - Key string - Num int - OmitEmpty bool - Flow bool - // Id holds the unique field identifier, so we can cheaply - // check for field duplicates without maintaining an extra map. - Id int - - // Inline holds the field index if the field is part of an inlined struct. - Inline []int -} - -var structMap = make(map[reflect.Type]*structInfo) -var fieldMapMutex sync.RWMutex -var unmarshalerType reflect.Type - -func init() { - var v Unmarshaler - unmarshalerType = reflect.ValueOf(&v).Elem().Type() -} - -func getStructInfo(st reflect.Type) (*structInfo, error) { - fieldMapMutex.RLock() - sinfo, found := structMap[st] - fieldMapMutex.RUnlock() - if found { - return sinfo, nil - } - - n := st.NumField() - fieldsMap := make(map[string]fieldInfo) - fieldsList := make([]fieldInfo, 0, n) - inlineMap := -1 - inlineUnmarshalers := [][]int(nil) - for i := 0; i != n; i++ { - field := st.Field(i) - if field.PkgPath != "" && !field.Anonymous { - continue // Private field - } - - info := fieldInfo{Num: i} - - tag := field.Tag.Get("yaml") - if tag == "" && strings.Index(string(field.Tag), ":") < 0 { - tag = string(field.Tag) - } - if tag == "-" { - continue - } - - inline := false - fields := strings.Split(tag, ",") - if len(fields) > 1 { - for _, flag := range fields[1:] { - switch flag { - case "omitempty": - info.OmitEmpty = true - case "flow": - info.Flow = true - case "inline": - inline = true - default: - return nil, errors.New(fmt.Sprintf("unsupported flag %q in tag %q of type %s", flag, tag, st)) - } - } - tag = fields[0] - } - - if inline { - switch field.Type.Kind() { - case reflect.Map: - if inlineMap >= 0 { - return nil, errors.New("multiple ,inline maps in struct " + st.String()) - } - if field.Type.Key() != reflect.TypeOf("") { - return nil, errors.New("option ,inline needs a map with string keys in struct " + st.String()) - } - inlineMap = info.Num - case reflect.Struct, reflect.Ptr: - ftype := field.Type - for ftype.Kind() == reflect.Ptr { - ftype = ftype.Elem() - } - if ftype.Kind() != reflect.Struct { - return nil, errors.New("option ,inline may only be used on a struct or map field") - } - if reflect.PtrTo(ftype).Implements(unmarshalerType) { - inlineUnmarshalers = append(inlineUnmarshalers, []int{i}) - } else { - sinfo, err := getStructInfo(ftype) - if err != nil { - return nil, err - } - for _, index := range sinfo.InlineUnmarshalers { - inlineUnmarshalers = append(inlineUnmarshalers, append([]int{i}, index...)) - } - for _, finfo := range sinfo.FieldsList { - if _, found := fieldsMap[finfo.Key]; found { - msg := "duplicated key '" + finfo.Key + "' in struct " + st.String() - return nil, errors.New(msg) - } - if finfo.Inline == nil { - finfo.Inline = []int{i, finfo.Num} - } else { - finfo.Inline = append([]int{i}, finfo.Inline...) - } - finfo.Id = len(fieldsList) - fieldsMap[finfo.Key] = finfo - fieldsList = append(fieldsList, finfo) - } - } - default: - return nil, errors.New("option ,inline may only be used on a struct or map field") - } - continue - } - - if tag != "" { - info.Key = tag - } else { - info.Key = strings.ToLower(field.Name) - } - - if _, found = fieldsMap[info.Key]; found { - msg := "duplicated key '" + info.Key + "' in struct " + st.String() - return nil, errors.New(msg) - } - - info.Id = len(fieldsList) - fieldsList = append(fieldsList, info) - fieldsMap[info.Key] = info - } - - sinfo = &structInfo{ - FieldsMap: fieldsMap, - FieldsList: fieldsList, - InlineMap: inlineMap, - InlineUnmarshalers: inlineUnmarshalers, - } - - fieldMapMutex.Lock() - structMap[st] = sinfo - fieldMapMutex.Unlock() - return sinfo, nil -} - -// IsZeroer is used to check whether an object is zero to -// determine whether it should be omitted when marshaling -// with the omitempty flag. One notable implementation -// is time.Time. -type IsZeroer interface { - IsZero() bool -} - -func isZero(v reflect.Value) bool { - kind := v.Kind() - if z, ok := v.Interface().(IsZeroer); ok { - if (kind == reflect.Ptr || kind == reflect.Interface) && v.IsNil() { - return true - } - return z.IsZero() - } - switch kind { - case reflect.String: - return len(v.String()) == 0 - case reflect.Interface, reflect.Ptr: - return v.IsNil() - case reflect.Slice: - return v.Len() == 0 - case reflect.Map: - return v.Len() == 0 - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return v.Int() == 0 - case reflect.Float32, reflect.Float64: - return v.Float() == 0 - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return v.Uint() == 0 - case reflect.Bool: - return !v.Bool() - case reflect.Struct: - vt := v.Type() - for i := v.NumField() - 1; i >= 0; i-- { - if vt.Field(i).PkgPath != "" { - continue // Private field - } - if !isZero(v.Field(i)) { - return false - } - } - return true - } - return false -} diff --git a/vendor/go.yaml.in/yaml/v3/yamlh.go b/vendor/go.yaml.in/yaml/v3/yamlh.go deleted file mode 100644 index 07c442361..000000000 --- a/vendor/go.yaml.in/yaml/v3/yamlh.go +++ /dev/null @@ -1,807 +0,0 @@ -// -// Copyright (c) 2011-2019 Canonical Ltd -// Copyright (c) 2006-2010 Kirill Simonov -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package yaml - -import ( - "fmt" - "io" -) - -// The version directive data. -type yaml_version_directive_t struct { - major int8 // The major version number. - minor int8 // The minor version number. -} - -// The tag directive data. -type yaml_tag_directive_t struct { - handle []byte // The tag handle. - prefix []byte // The tag prefix. -} - -type yaml_encoding_t int - -// The stream encoding. -const ( - // Let the parser choose the encoding. - yaml_ANY_ENCODING yaml_encoding_t = iota - - yaml_UTF8_ENCODING // The default UTF-8 encoding. - yaml_UTF16LE_ENCODING // The UTF-16-LE encoding with BOM. - yaml_UTF16BE_ENCODING // The UTF-16-BE encoding with BOM. -) - -type yaml_break_t int - -// Line break types. -const ( - // Let the parser choose the break type. - yaml_ANY_BREAK yaml_break_t = iota - - yaml_CR_BREAK // Use CR for line breaks (Mac style). - yaml_LN_BREAK // Use LN for line breaks (Unix style). - yaml_CRLN_BREAK // Use CR LN for line breaks (DOS style). -) - -type yaml_error_type_t int - -// Many bad things could happen with the parser and emitter. -const ( - // No error is produced. - yaml_NO_ERROR yaml_error_type_t = iota - - yaml_MEMORY_ERROR // Cannot allocate or reallocate a block of memory. - yaml_READER_ERROR // Cannot read or decode the input stream. - yaml_SCANNER_ERROR // Cannot scan the input stream. - yaml_PARSER_ERROR // Cannot parse the input stream. - yaml_COMPOSER_ERROR // Cannot compose a YAML document. - yaml_WRITER_ERROR // Cannot write to the output stream. - yaml_EMITTER_ERROR // Cannot emit a YAML stream. -) - -// The pointer position. -type yaml_mark_t struct { - index int // The position index. - line int // The position line. - column int // The position column. -} - -// Node Styles - -type yaml_style_t int8 - -type yaml_scalar_style_t yaml_style_t - -// Scalar styles. -const ( - // Let the emitter choose the style. - yaml_ANY_SCALAR_STYLE yaml_scalar_style_t = 0 - - yaml_PLAIN_SCALAR_STYLE yaml_scalar_style_t = 1 << iota // The plain scalar style. - yaml_SINGLE_QUOTED_SCALAR_STYLE // The single-quoted scalar style. - yaml_DOUBLE_QUOTED_SCALAR_STYLE // The double-quoted scalar style. - yaml_LITERAL_SCALAR_STYLE // The literal scalar style. - yaml_FOLDED_SCALAR_STYLE // The folded scalar style. -) - -type yaml_sequence_style_t yaml_style_t - -// Sequence styles. -const ( - // Let the emitter choose the style. - yaml_ANY_SEQUENCE_STYLE yaml_sequence_style_t = iota - - yaml_BLOCK_SEQUENCE_STYLE // The block sequence style. - yaml_FLOW_SEQUENCE_STYLE // The flow sequence style. -) - -type yaml_mapping_style_t yaml_style_t - -// Mapping styles. -const ( - // Let the emitter choose the style. - yaml_ANY_MAPPING_STYLE yaml_mapping_style_t = iota - - yaml_BLOCK_MAPPING_STYLE // The block mapping style. - yaml_FLOW_MAPPING_STYLE // The flow mapping style. -) - -// Tokens - -type yaml_token_type_t int - -// Token types. -const ( - // An empty token. - yaml_NO_TOKEN yaml_token_type_t = iota - - yaml_STREAM_START_TOKEN // A STREAM-START token. - yaml_STREAM_END_TOKEN // A STREAM-END token. - - yaml_VERSION_DIRECTIVE_TOKEN // A VERSION-DIRECTIVE token. - yaml_TAG_DIRECTIVE_TOKEN // A TAG-DIRECTIVE token. - yaml_DOCUMENT_START_TOKEN // A DOCUMENT-START token. - yaml_DOCUMENT_END_TOKEN // A DOCUMENT-END token. - - yaml_BLOCK_SEQUENCE_START_TOKEN // A BLOCK-SEQUENCE-START token. - yaml_BLOCK_MAPPING_START_TOKEN // A BLOCK-SEQUENCE-END token. - yaml_BLOCK_END_TOKEN // A BLOCK-END token. - - yaml_FLOW_SEQUENCE_START_TOKEN // A FLOW-SEQUENCE-START token. - yaml_FLOW_SEQUENCE_END_TOKEN // A FLOW-SEQUENCE-END token. - yaml_FLOW_MAPPING_START_TOKEN // A FLOW-MAPPING-START token. - yaml_FLOW_MAPPING_END_TOKEN // A FLOW-MAPPING-END token. - - yaml_BLOCK_ENTRY_TOKEN // A BLOCK-ENTRY token. - yaml_FLOW_ENTRY_TOKEN // A FLOW-ENTRY token. - yaml_KEY_TOKEN // A KEY token. - yaml_VALUE_TOKEN // A VALUE token. - - yaml_ALIAS_TOKEN // An ALIAS token. - yaml_ANCHOR_TOKEN // An ANCHOR token. - yaml_TAG_TOKEN // A TAG token. - yaml_SCALAR_TOKEN // A SCALAR token. -) - -func (tt yaml_token_type_t) String() string { - switch tt { - case yaml_NO_TOKEN: - return "yaml_NO_TOKEN" - case yaml_STREAM_START_TOKEN: - return "yaml_STREAM_START_TOKEN" - case yaml_STREAM_END_TOKEN: - return "yaml_STREAM_END_TOKEN" - case yaml_VERSION_DIRECTIVE_TOKEN: - return "yaml_VERSION_DIRECTIVE_TOKEN" - case yaml_TAG_DIRECTIVE_TOKEN: - return "yaml_TAG_DIRECTIVE_TOKEN" - case yaml_DOCUMENT_START_TOKEN: - return "yaml_DOCUMENT_START_TOKEN" - case yaml_DOCUMENT_END_TOKEN: - return "yaml_DOCUMENT_END_TOKEN" - case yaml_BLOCK_SEQUENCE_START_TOKEN: - return "yaml_BLOCK_SEQUENCE_START_TOKEN" - case yaml_BLOCK_MAPPING_START_TOKEN: - return "yaml_BLOCK_MAPPING_START_TOKEN" - case yaml_BLOCK_END_TOKEN: - return "yaml_BLOCK_END_TOKEN" - case yaml_FLOW_SEQUENCE_START_TOKEN: - return "yaml_FLOW_SEQUENCE_START_TOKEN" - case yaml_FLOW_SEQUENCE_END_TOKEN: - return "yaml_FLOW_SEQUENCE_END_TOKEN" - case yaml_FLOW_MAPPING_START_TOKEN: - return "yaml_FLOW_MAPPING_START_TOKEN" - case yaml_FLOW_MAPPING_END_TOKEN: - return "yaml_FLOW_MAPPING_END_TOKEN" - case yaml_BLOCK_ENTRY_TOKEN: - return "yaml_BLOCK_ENTRY_TOKEN" - case yaml_FLOW_ENTRY_TOKEN: - return "yaml_FLOW_ENTRY_TOKEN" - case yaml_KEY_TOKEN: - return "yaml_KEY_TOKEN" - case yaml_VALUE_TOKEN: - return "yaml_VALUE_TOKEN" - case yaml_ALIAS_TOKEN: - return "yaml_ALIAS_TOKEN" - case yaml_ANCHOR_TOKEN: - return "yaml_ANCHOR_TOKEN" - case yaml_TAG_TOKEN: - return "yaml_TAG_TOKEN" - case yaml_SCALAR_TOKEN: - return "yaml_SCALAR_TOKEN" - } - return "" -} - -// The token structure. -type yaml_token_t struct { - // The token type. - typ yaml_token_type_t - - // The start/end of the token. - start_mark, end_mark yaml_mark_t - - // The stream encoding (for yaml_STREAM_START_TOKEN). - encoding yaml_encoding_t - - // The alias/anchor/scalar value or tag/tag directive handle - // (for yaml_ALIAS_TOKEN, yaml_ANCHOR_TOKEN, yaml_SCALAR_TOKEN, yaml_TAG_TOKEN, yaml_TAG_DIRECTIVE_TOKEN). - value []byte - - // The tag suffix (for yaml_TAG_TOKEN). - suffix []byte - - // The tag directive prefix (for yaml_TAG_DIRECTIVE_TOKEN). - prefix []byte - - // The scalar style (for yaml_SCALAR_TOKEN). - style yaml_scalar_style_t - - // The version directive major/minor (for yaml_VERSION_DIRECTIVE_TOKEN). - major, minor int8 -} - -// Events - -type yaml_event_type_t int8 - -// Event types. -const ( - // An empty event. - yaml_NO_EVENT yaml_event_type_t = iota - - yaml_STREAM_START_EVENT // A STREAM-START event. - yaml_STREAM_END_EVENT // A STREAM-END event. - yaml_DOCUMENT_START_EVENT // A DOCUMENT-START event. - yaml_DOCUMENT_END_EVENT // A DOCUMENT-END event. - yaml_ALIAS_EVENT // An ALIAS event. - yaml_SCALAR_EVENT // A SCALAR event. - yaml_SEQUENCE_START_EVENT // A SEQUENCE-START event. - yaml_SEQUENCE_END_EVENT // A SEQUENCE-END event. - yaml_MAPPING_START_EVENT // A MAPPING-START event. - yaml_MAPPING_END_EVENT // A MAPPING-END event. - yaml_TAIL_COMMENT_EVENT -) - -var eventStrings = []string{ - yaml_NO_EVENT: "none", - yaml_STREAM_START_EVENT: "stream start", - yaml_STREAM_END_EVENT: "stream end", - yaml_DOCUMENT_START_EVENT: "document start", - yaml_DOCUMENT_END_EVENT: "document end", - yaml_ALIAS_EVENT: "alias", - yaml_SCALAR_EVENT: "scalar", - yaml_SEQUENCE_START_EVENT: "sequence start", - yaml_SEQUENCE_END_EVENT: "sequence end", - yaml_MAPPING_START_EVENT: "mapping start", - yaml_MAPPING_END_EVENT: "mapping end", - yaml_TAIL_COMMENT_EVENT: "tail comment", -} - -func (e yaml_event_type_t) String() string { - if e < 0 || int(e) >= len(eventStrings) { - return fmt.Sprintf("unknown event %d", e) - } - return eventStrings[e] -} - -// The event structure. -type yaml_event_t struct { - - // The event type. - typ yaml_event_type_t - - // The start and end of the event. - start_mark, end_mark yaml_mark_t - - // The document encoding (for yaml_STREAM_START_EVENT). - encoding yaml_encoding_t - - // The version directive (for yaml_DOCUMENT_START_EVENT). - version_directive *yaml_version_directive_t - - // The list of tag directives (for yaml_DOCUMENT_START_EVENT). - tag_directives []yaml_tag_directive_t - - // The comments - head_comment []byte - line_comment []byte - foot_comment []byte - tail_comment []byte - - // The anchor (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_ALIAS_EVENT). - anchor []byte - - // The tag (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT). - tag []byte - - // The scalar value (for yaml_SCALAR_EVENT). - value []byte - - // Is the document start/end indicator implicit, or the tag optional? - // (for yaml_DOCUMENT_START_EVENT, yaml_DOCUMENT_END_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_SCALAR_EVENT). - implicit bool - - // Is the tag optional for any non-plain style? (for yaml_SCALAR_EVENT). - quoted_implicit bool - - // The style (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT). - style yaml_style_t -} - -func (e *yaml_event_t) scalar_style() yaml_scalar_style_t { return yaml_scalar_style_t(e.style) } -func (e *yaml_event_t) sequence_style() yaml_sequence_style_t { return yaml_sequence_style_t(e.style) } -func (e *yaml_event_t) mapping_style() yaml_mapping_style_t { return yaml_mapping_style_t(e.style) } - -// Nodes - -const ( - yaml_NULL_TAG = "tag:yaml.org,2002:null" // The tag !!null with the only possible value: null. - yaml_BOOL_TAG = "tag:yaml.org,2002:bool" // The tag !!bool with the values: true and false. - yaml_STR_TAG = "tag:yaml.org,2002:str" // The tag !!str for string values. - yaml_INT_TAG = "tag:yaml.org,2002:int" // The tag !!int for integer values. - yaml_FLOAT_TAG = "tag:yaml.org,2002:float" // The tag !!float for float values. - yaml_TIMESTAMP_TAG = "tag:yaml.org,2002:timestamp" // The tag !!timestamp for date and time values. - - yaml_SEQ_TAG = "tag:yaml.org,2002:seq" // The tag !!seq is used to denote sequences. - yaml_MAP_TAG = "tag:yaml.org,2002:map" // The tag !!map is used to denote mapping. - - // Not in original libyaml. - yaml_BINARY_TAG = "tag:yaml.org,2002:binary" - yaml_MERGE_TAG = "tag:yaml.org,2002:merge" - - yaml_DEFAULT_SCALAR_TAG = yaml_STR_TAG // The default scalar tag is !!str. - yaml_DEFAULT_SEQUENCE_TAG = yaml_SEQ_TAG // The default sequence tag is !!seq. - yaml_DEFAULT_MAPPING_TAG = yaml_MAP_TAG // The default mapping tag is !!map. -) - -type yaml_node_type_t int - -// Node types. -const ( - // An empty node. - yaml_NO_NODE yaml_node_type_t = iota - - yaml_SCALAR_NODE // A scalar node. - yaml_SEQUENCE_NODE // A sequence node. - yaml_MAPPING_NODE // A mapping node. -) - -// An element of a sequence node. -type yaml_node_item_t int - -// An element of a mapping node. -type yaml_node_pair_t struct { - key int // The key of the element. - value int // The value of the element. -} - -// The node structure. -type yaml_node_t struct { - typ yaml_node_type_t // The node type. - tag []byte // The node tag. - - // The node data. - - // The scalar parameters (for yaml_SCALAR_NODE). - scalar struct { - value []byte // The scalar value. - length int // The length of the scalar value. - style yaml_scalar_style_t // The scalar style. - } - - // The sequence parameters (for YAML_SEQUENCE_NODE). - sequence struct { - items_data []yaml_node_item_t // The stack of sequence items. - style yaml_sequence_style_t // The sequence style. - } - - // The mapping parameters (for yaml_MAPPING_NODE). - mapping struct { - pairs_data []yaml_node_pair_t // The stack of mapping pairs (key, value). - pairs_start *yaml_node_pair_t // The beginning of the stack. - pairs_end *yaml_node_pair_t // The end of the stack. - pairs_top *yaml_node_pair_t // The top of the stack. - style yaml_mapping_style_t // The mapping style. - } - - start_mark yaml_mark_t // The beginning of the node. - end_mark yaml_mark_t // The end of the node. - -} - -// The document structure. -type yaml_document_t struct { - - // The document nodes. - nodes []yaml_node_t - - // The version directive. - version_directive *yaml_version_directive_t - - // The list of tag directives. - tag_directives_data []yaml_tag_directive_t - tag_directives_start int // The beginning of the tag directives list. - tag_directives_end int // The end of the tag directives list. - - start_implicit int // Is the document start indicator implicit? - end_implicit int // Is the document end indicator implicit? - - // The start/end of the document. - start_mark, end_mark yaml_mark_t -} - -// The prototype of a read handler. -// -// The read handler is called when the parser needs to read more bytes from the -// source. The handler should write not more than size bytes to the buffer. -// The number of written bytes should be set to the size_read variable. -// -// [in,out] data A pointer to an application data specified by -// yaml_parser_set_input(). -// [out] buffer The buffer to write the data from the source. -// [in] size The size of the buffer. -// [out] size_read The actual number of bytes read from the source. -// -// On success, the handler should return 1. If the handler failed, -// the returned value should be 0. On EOF, the handler should set the -// size_read to 0 and return 1. -type yaml_read_handler_t func(parser *yaml_parser_t, buffer []byte) (n int, err error) - -// This structure holds information about a potential simple key. -type yaml_simple_key_t struct { - possible bool // Is a simple key possible? - required bool // Is a simple key required? - token_number int // The number of the token. - mark yaml_mark_t // The position mark. -} - -// The states of the parser. -type yaml_parser_state_t int - -const ( - yaml_PARSE_STREAM_START_STATE yaml_parser_state_t = iota - - yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE // Expect the beginning of an implicit document. - yaml_PARSE_DOCUMENT_START_STATE // Expect DOCUMENT-START. - yaml_PARSE_DOCUMENT_CONTENT_STATE // Expect the content of a document. - yaml_PARSE_DOCUMENT_END_STATE // Expect DOCUMENT-END. - yaml_PARSE_BLOCK_NODE_STATE // Expect a block node. - yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE // Expect a block node or indentless sequence. - yaml_PARSE_FLOW_NODE_STATE // Expect a flow node. - yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a block sequence. - yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE // Expect an entry of a block sequence. - yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE // Expect an entry of an indentless sequence. - yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping. - yaml_PARSE_BLOCK_MAPPING_KEY_STATE // Expect a block mapping key. - yaml_PARSE_BLOCK_MAPPING_VALUE_STATE // Expect a block mapping value. - yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE // Expect the first entry of a flow sequence. - yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE // Expect an entry of a flow sequence. - yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE // Expect a key of an ordered mapping. - yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE // Expect a value of an ordered mapping. - yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE // Expect the and of an ordered mapping entry. - yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping. - yaml_PARSE_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping. - yaml_PARSE_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping. - yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE // Expect an empty value of a flow mapping. - yaml_PARSE_END_STATE // Expect nothing. -) - -func (ps yaml_parser_state_t) String() string { - switch ps { - case yaml_PARSE_STREAM_START_STATE: - return "yaml_PARSE_STREAM_START_STATE" - case yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE: - return "yaml_PARSE_IMPLICIT_DOCUMENT_START_STATE" - case yaml_PARSE_DOCUMENT_START_STATE: - return "yaml_PARSE_DOCUMENT_START_STATE" - case yaml_PARSE_DOCUMENT_CONTENT_STATE: - return "yaml_PARSE_DOCUMENT_CONTENT_STATE" - case yaml_PARSE_DOCUMENT_END_STATE: - return "yaml_PARSE_DOCUMENT_END_STATE" - case yaml_PARSE_BLOCK_NODE_STATE: - return "yaml_PARSE_BLOCK_NODE_STATE" - case yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE: - return "yaml_PARSE_BLOCK_NODE_OR_INDENTLESS_SEQUENCE_STATE" - case yaml_PARSE_FLOW_NODE_STATE: - return "yaml_PARSE_FLOW_NODE_STATE" - case yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE: - return "yaml_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE" - case yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE: - return "yaml_PARSE_BLOCK_SEQUENCE_ENTRY_STATE" - case yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE: - return "yaml_PARSE_INDENTLESS_SEQUENCE_ENTRY_STATE" - case yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE: - return "yaml_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE" - case yaml_PARSE_BLOCK_MAPPING_KEY_STATE: - return "yaml_PARSE_BLOCK_MAPPING_KEY_STATE" - case yaml_PARSE_BLOCK_MAPPING_VALUE_STATE: - return "yaml_PARSE_BLOCK_MAPPING_VALUE_STATE" - case yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE: - return "yaml_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE" - case yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE: - return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_STATE" - case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE: - return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_KEY_STATE" - case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE: - return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_VALUE_STATE" - case yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE: - return "yaml_PARSE_FLOW_SEQUENCE_ENTRY_MAPPING_END_STATE" - case yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE: - return "yaml_PARSE_FLOW_MAPPING_FIRST_KEY_STATE" - case yaml_PARSE_FLOW_MAPPING_KEY_STATE: - return "yaml_PARSE_FLOW_MAPPING_KEY_STATE" - case yaml_PARSE_FLOW_MAPPING_VALUE_STATE: - return "yaml_PARSE_FLOW_MAPPING_VALUE_STATE" - case yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE: - return "yaml_PARSE_FLOW_MAPPING_EMPTY_VALUE_STATE" - case yaml_PARSE_END_STATE: - return "yaml_PARSE_END_STATE" - } - return "" -} - -// This structure holds aliases data. -type yaml_alias_data_t struct { - anchor []byte // The anchor. - index int // The node id. - mark yaml_mark_t // The anchor mark. -} - -// The parser structure. -// -// All members are internal. Manage the structure using the -// yaml_parser_ family of functions. -type yaml_parser_t struct { - - // Error handling - - error yaml_error_type_t // Error type. - - problem string // Error description. - - // The byte about which the problem occurred. - problem_offset int - problem_value int - problem_mark yaml_mark_t - - // The error context. - context string - context_mark yaml_mark_t - - // Reader stuff - - read_handler yaml_read_handler_t // Read handler. - - input_reader io.Reader // File input data. - input []byte // String input data. - input_pos int - - eof bool // EOF flag - - buffer []byte // The working buffer. - buffer_pos int // The current position of the buffer. - - unread int // The number of unread characters in the buffer. - - newlines int // The number of line breaks since last non-break/non-blank character - - raw_buffer []byte // The raw buffer. - raw_buffer_pos int // The current position of the buffer. - - encoding yaml_encoding_t // The input encoding. - - offset int // The offset of the current position (in bytes). - mark yaml_mark_t // The mark of the current position. - - // Comments - - head_comment []byte // The current head comments - line_comment []byte // The current line comments - foot_comment []byte // The current foot comments - tail_comment []byte // Foot comment that happens at the end of a block. - stem_comment []byte // Comment in item preceding a nested structure (list inside list item, etc) - - comments []yaml_comment_t // The folded comments for all parsed tokens - comments_head int - - // Scanner stuff - - stream_start_produced bool // Have we started to scan the input stream? - stream_end_produced bool // Have we reached the end of the input stream? - - flow_level int // The number of unclosed '[' and '{' indicators. - - tokens []yaml_token_t // The tokens queue. - tokens_head int // The head of the tokens queue. - tokens_parsed int // The number of tokens fetched from the queue. - token_available bool // Does the tokens queue contain a token ready for dequeueing. - - indent int // The current indentation level. - indents []int // The indentation levels stack. - - simple_key_allowed bool // May a simple key occur at the current position? - simple_keys []yaml_simple_key_t // The stack of simple keys. - simple_keys_by_tok map[int]int // possible simple_key indexes indexed by token_number - - // Parser stuff - - state yaml_parser_state_t // The current parser state. - states []yaml_parser_state_t // The parser states stack. - marks []yaml_mark_t // The stack of marks. - tag_directives []yaml_tag_directive_t // The list of TAG directives. - - // Dumper stuff - - aliases []yaml_alias_data_t // The alias data. - - document *yaml_document_t // The currently parsed document. -} - -type yaml_comment_t struct { - scan_mark yaml_mark_t // Position where scanning for comments started - token_mark yaml_mark_t // Position after which tokens will be associated with this comment - start_mark yaml_mark_t // Position of '#' comment mark - end_mark yaml_mark_t // Position where comment terminated - - head []byte - line []byte - foot []byte -} - -// Emitter Definitions - -// The prototype of a write handler. -// -// The write handler is called when the emitter needs to flush the accumulated -// characters to the output. The handler should write @a size bytes of the -// @a buffer to the output. -// -// @param[in,out] data A pointer to an application data specified by -// yaml_emitter_set_output(). -// @param[in] buffer The buffer with bytes to be written. -// @param[in] size The size of the buffer. -// -// @returns On success, the handler should return @c 1. If the handler failed, -// the returned value should be @c 0. -type yaml_write_handler_t func(emitter *yaml_emitter_t, buffer []byte) error - -type yaml_emitter_state_t int - -// The emitter states. -const ( - // Expect STREAM-START. - yaml_EMIT_STREAM_START_STATE yaml_emitter_state_t = iota - - yaml_EMIT_FIRST_DOCUMENT_START_STATE // Expect the first DOCUMENT-START or STREAM-END. - yaml_EMIT_DOCUMENT_START_STATE // Expect DOCUMENT-START or STREAM-END. - yaml_EMIT_DOCUMENT_CONTENT_STATE // Expect the content of a document. - yaml_EMIT_DOCUMENT_END_STATE // Expect DOCUMENT-END. - yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a flow sequence. - yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE // Expect the next item of a flow sequence, with the comma already written out - yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE // Expect an item of a flow sequence. - yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE // Expect the first key of a flow mapping. - yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE // Expect the next key of a flow mapping, with the comma already written out - yaml_EMIT_FLOW_MAPPING_KEY_STATE // Expect a key of a flow mapping. - yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a flow mapping. - yaml_EMIT_FLOW_MAPPING_VALUE_STATE // Expect a value of a flow mapping. - yaml_EMIT_BLOCK_SEQUENCE_FIRST_ITEM_STATE // Expect the first item of a block sequence. - yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE // Expect an item of a block sequence. - yaml_EMIT_BLOCK_MAPPING_FIRST_KEY_STATE // Expect the first key of a block mapping. - yaml_EMIT_BLOCK_MAPPING_KEY_STATE // Expect the key of a block mapping. - yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE // Expect a value for a simple key of a block mapping. - yaml_EMIT_BLOCK_MAPPING_VALUE_STATE // Expect a value of a block mapping. - yaml_EMIT_END_STATE // Expect nothing. -) - -// The emitter structure. -// -// All members are internal. Manage the structure using the @c yaml_emitter_ -// family of functions. -type yaml_emitter_t struct { - - // Error handling - - error yaml_error_type_t // Error type. - problem string // Error description. - - // Writer stuff - - write_handler yaml_write_handler_t // Write handler. - - output_buffer *[]byte // String output data. - output_writer io.Writer // File output data. - - buffer []byte // The working buffer. - buffer_pos int // The current position of the buffer. - - raw_buffer []byte // The raw buffer. - raw_buffer_pos int // The current position of the buffer. - - encoding yaml_encoding_t // The stream encoding. - - // Emitter stuff - - canonical bool // If the output is in the canonical style? - best_indent int // The number of indentation spaces. - best_width int // The preferred width of the output lines. - unicode bool // Allow unescaped non-ASCII characters? - line_break yaml_break_t // The preferred line break. - - state yaml_emitter_state_t // The current emitter state. - states []yaml_emitter_state_t // The stack of states. - - events []yaml_event_t // The event queue. - events_head int // The head of the event queue. - - indents []int // The stack of indentation levels. - - tag_directives []yaml_tag_directive_t // The list of tag directives. - - indent int // The current indentation level. - - compact_sequence_indent bool // Is '- ' is considered part of the indentation for sequence elements? - - flow_level int // The current flow level. - - root_context bool // Is it the document root context? - sequence_context bool // Is it a sequence context? - mapping_context bool // Is it a mapping context? - simple_key_context bool // Is it a simple mapping key context? - - line int // The current line. - column int // The current column. - whitespace bool // If the last character was a whitespace? - indention bool // If the last character was an indentation character (' ', '-', '?', ':')? - open_ended bool // If an explicit document end is required? - - space_above bool // Is there's an empty line above? - foot_indent int // The indent used to write the foot comment above, or -1 if none. - - // Anchor analysis. - anchor_data struct { - anchor []byte // The anchor value. - alias bool // Is it an alias? - } - - // Tag analysis. - tag_data struct { - handle []byte // The tag handle. - suffix []byte // The tag suffix. - } - - // Scalar analysis. - scalar_data struct { - value []byte // The scalar value. - multiline bool // Does the scalar contain line breaks? - flow_plain_allowed bool // Can the scalar be expessed in the flow plain style? - block_plain_allowed bool // Can the scalar be expressed in the block plain style? - single_quoted_allowed bool // Can the scalar be expressed in the single quoted style? - block_allowed bool // Can the scalar be expressed in the literal or folded styles? - style yaml_scalar_style_t // The output style. - } - - // Comments - head_comment []byte - line_comment []byte - foot_comment []byte - tail_comment []byte - - key_line_comment []byte - - // Dumper stuff - - opened bool // If the stream was already opened? - closed bool // If the stream was already closed? - - // The information associated with the document nodes. - anchors *struct { - references int // The number of references. - anchor int // The anchor id. - serialized bool // If the node has been emitted? - } - - last_anchor_id int // The last assigned anchor id. - - document *yaml_document_t // The currently emitted document. -} diff --git a/vendor/go.yaml.in/yaml/v3/yamlprivateh.go b/vendor/go.yaml.in/yaml/v3/yamlprivateh.go deleted file mode 100644 index dea1ba961..000000000 --- a/vendor/go.yaml.in/yaml/v3/yamlprivateh.go +++ /dev/null @@ -1,198 +0,0 @@ -// -// Copyright (c) 2011-2019 Canonical Ltd -// Copyright (c) 2006-2010 Kirill Simonov -// -// Permission is hereby granted, free of charge, to any person obtaining a copy of -// this software and associated documentation files (the "Software"), to deal in -// the Software without restriction, including without limitation the rights to -// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies -// of the Software, and to permit persons to whom the Software is furnished to do -// so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -package yaml - -const ( - // The size of the input raw buffer. - input_raw_buffer_size = 512 - - // The size of the input buffer. - // It should be possible to decode the whole raw buffer. - input_buffer_size = input_raw_buffer_size * 3 - - // The size of the output buffer. - output_buffer_size = 128 - - // The size of the output raw buffer. - // It should be possible to encode the whole output buffer. - output_raw_buffer_size = (output_buffer_size*2 + 2) - - // The size of other stacks and queues. - initial_stack_size = 16 - initial_queue_size = 16 - initial_string_size = 16 -) - -// Check if the character at the specified position is an alphabetical -// character, a digit, '_', or '-'. -func is_alpha(b []byte, i int) bool { - return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'Z' || b[i] >= 'a' && b[i] <= 'z' || b[i] == '_' || b[i] == '-' -} - -// Check if the character at the specified position is a digit. -func is_digit(b []byte, i int) bool { - return b[i] >= '0' && b[i] <= '9' -} - -// Get the value of a digit. -func as_digit(b []byte, i int) int { - return int(b[i]) - '0' -} - -// Check if the character at the specified position is a hex-digit. -func is_hex(b []byte, i int) bool { - return b[i] >= '0' && b[i] <= '9' || b[i] >= 'A' && b[i] <= 'F' || b[i] >= 'a' && b[i] <= 'f' -} - -// Get the value of a hex-digit. -func as_hex(b []byte, i int) int { - bi := b[i] - if bi >= 'A' && bi <= 'F' { - return int(bi) - 'A' + 10 - } - if bi >= 'a' && bi <= 'f' { - return int(bi) - 'a' + 10 - } - return int(bi) - '0' -} - -// Check if the character is ASCII. -func is_ascii(b []byte, i int) bool { - return b[i] <= 0x7F -} - -// Check if the character at the start of the buffer can be printed unescaped. -func is_printable(b []byte, i int) bool { - return ((b[i] == 0x0A) || // . == #x0A - (b[i] >= 0x20 && b[i] <= 0x7E) || // #x20 <= . <= #x7E - (b[i] == 0xC2 && b[i+1] >= 0xA0) || // #0xA0 <= . <= #xD7FF - (b[i] > 0xC2 && b[i] < 0xED) || - (b[i] == 0xED && b[i+1] < 0xA0) || - (b[i] == 0xEE) || - (b[i] == 0xEF && // #xE000 <= . <= #xFFFD - !(b[i+1] == 0xBB && b[i+2] == 0xBF) && // && . != #xFEFF - !(b[i+1] == 0xBF && (b[i+2] == 0xBE || b[i+2] == 0xBF)))) -} - -// Check if the character at the specified position is NUL. -func is_z(b []byte, i int) bool { - return b[i] == 0x00 -} - -// Check if the beginning of the buffer is a BOM. -func is_bom(b []byte, i int) bool { - return b[0] == 0xEF && b[1] == 0xBB && b[2] == 0xBF -} - -// Check if the character at the specified position is space. -func is_space(b []byte, i int) bool { - return b[i] == ' ' -} - -// Check if the character at the specified position is tab. -func is_tab(b []byte, i int) bool { - return b[i] == '\t' -} - -// Check if the character at the specified position is blank (space or tab). -func is_blank(b []byte, i int) bool { - //return is_space(b, i) || is_tab(b, i) - return b[i] == ' ' || b[i] == '\t' -} - -// Check if the character at the specified position is a line break. -func is_break(b []byte, i int) bool { - return (b[i] == '\r' || // CR (#xD) - b[i] == '\n' || // LF (#xA) - b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) - b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) - b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9) // PS (#x2029) -} - -func is_crlf(b []byte, i int) bool { - return b[i] == '\r' && b[i+1] == '\n' -} - -// Check if the character is a line break or NUL. -func is_breakz(b []byte, i int) bool { - //return is_break(b, i) || is_z(b, i) - return ( - // is_break: - b[i] == '\r' || // CR (#xD) - b[i] == '\n' || // LF (#xA) - b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) - b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) - b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) - // is_z: - b[i] == 0) -} - -// Check if the character is a line break, space, or NUL. -func is_spacez(b []byte, i int) bool { - //return is_space(b, i) || is_breakz(b, i) - return ( - // is_space: - b[i] == ' ' || - // is_breakz: - b[i] == '\r' || // CR (#xD) - b[i] == '\n' || // LF (#xA) - b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) - b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) - b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) - b[i] == 0) -} - -// Check if the character is a line break, space, tab, or NUL. -func is_blankz(b []byte, i int) bool { - //return is_blank(b, i) || is_breakz(b, i) - return ( - // is_blank: - b[i] == ' ' || b[i] == '\t' || - // is_breakz: - b[i] == '\r' || // CR (#xD) - b[i] == '\n' || // LF (#xA) - b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85) - b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA8 || // LS (#x2028) - b[i] == 0xE2 && b[i+1] == 0x80 && b[i+2] == 0xA9 || // PS (#x2029) - b[i] == 0) -} - -// Determine the width of the character. -func width(b byte) int { - // Don't replace these by a switch without first - // confirming that it is being inlined. - if b&0x80 == 0x00 { - return 1 - } - if b&0xE0 == 0xC0 { - return 2 - } - if b&0xF0 == 0xE0 { - return 3 - } - if b&0xF8 == 0xF0 { - return 4 - } - return 0 - -} diff --git a/vendor/modules.txt b/vendor/modules.txt index c8362d967..293603dbb 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -1,9 +1,6 @@ # cel.dev/expr v0.25.2 ## explicit; go 1.23.0 cel.dev/expr -# github.com/cilium/kafka v0.0.0-20180809090225-01ce283b732b -## explicit -github.com/cilium/kafka/proto # github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 ## explicit; go 1.24.6 github.com/cncf/xds/go/udpa/annotations @@ -34,9 +31,6 @@ github.com/envoyproxy/protoc-gen-validate/templates/goshared github.com/envoyproxy/protoc-gen-validate/templates/java github.com/envoyproxy/protoc-gen-validate/templates/shared github.com/envoyproxy/protoc-gen-validate/validate -# github.com/golang/snappy v0.0.4 -## explicit -github.com/golang/snappy # github.com/iancoleman/strcase v0.3.0 ## explicit; go 1.16 github.com/iancoleman/strcase @@ -52,9 +46,6 @@ github.com/planetscale/vtprotobuf/types/known/durationpb github.com/planetscale/vtprotobuf/types/known/emptypb github.com/planetscale/vtprotobuf/types/known/structpb github.com/planetscale/vtprotobuf/types/known/wrapperspb -# github.com/sirupsen/logrus v1.10.2 -## explicit; go 1.23 -github.com/sirupsen/logrus # github.com/spf13/afero v1.15.0 ## explicit; go 1.23.0 github.com/spf13/afero @@ -62,14 +53,6 @@ github.com/spf13/afero/internal/common github.com/spf13/afero/mem # github.com/stretchr/testify v1.12.1 ## explicit; go 1.17 -github.com/stretchr/testify/assert -github.com/stretchr/testify/assert/yaml -github.com/stretchr/testify/internal/difflib -github.com/stretchr/testify/internal/spew -github.com/stretchr/testify/require -# go.yaml.in/yaml/v3 v3.0.5 -## explicit; go 1.16 -go.yaml.in/yaml/v3 # golang.org/x/mod v0.38.0 ## explicit; go 1.25.0 golang.org/x/mod/internal/lazyregexp