From eb39237a777a8477932ec2f3f1e5b3ecf57d2898 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 21 Mar 2025 01:12:47 +0000 Subject: [PATCH 1/2] chore(deps): update dependency cilium/cilium to v1.17.2 Signed-off-by: renovate[bot] --- .github/workflows/aks-byocni.yaml | 2 +- .github/workflows/eks-tunnel.yaml | 2 +- .github/workflows/eks.yaml | 2 +- .github/workflows/gke.yaml | 2 +- .github/workflows/kind.yaml | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/aks-byocni.yaml b/.github/workflows/aks-byocni.yaml index 4fb818c3d7..d8ff455644 100644 --- a/.github/workflows/aks-byocni.yaml +++ b/.github/workflows/aks-byocni.yaml @@ -39,7 +39,7 @@ env: location: westus2 cost_reduction: --node-vm-size Standard_B2s --node-osdisk-size 30 # renovate: datasource=github-releases depName=cilium/cilium - cilium_version: v1.16.8 + cilium_version: v1.17.2 kubectl_version: v1.23.6 jobs: diff --git a/.github/workflows/eks-tunnel.yaml b/.github/workflows/eks-tunnel.yaml index 5bb82ad94a..1606191876 100644 --- a/.github/workflows/eks-tunnel.yaml +++ b/.github/workflows/eks-tunnel.yaml @@ -29,7 +29,7 @@ env: region: us-east-2 eksctl_version: v0.147.0 # renovate: datasource=github-releases depName=cilium/cilium - cilium_version: v1.16.8 + cilium_version: v1.17.2 kubectl_version: v1.23.6 jobs: diff --git a/.github/workflows/eks.yaml b/.github/workflows/eks.yaml index f5cfbf07e7..023d4acb83 100644 --- a/.github/workflows/eks.yaml +++ b/.github/workflows/eks.yaml @@ -29,7 +29,7 @@ env: region: us-east-2 eksctl_version: v0.147.0 # renovate: datasource=github-releases depName=cilium/cilium - cilium_version: v1.16.8 + cilium_version: v1.17.2 kubectl_version: v1.23.6 jobs: diff --git a/.github/workflows/gke.yaml b/.github/workflows/gke.yaml index 74b7a1a626..409595d793 100644 --- a/.github/workflows/gke.yaml +++ b/.github/workflows/gke.yaml @@ -36,7 +36,7 @@ concurrency: env: zone: us-west2-a # renovate: datasource=github-releases depName=cilium/cilium - cilium_version: v1.16.8 + cilium_version: v1.17.2 kubectl_version: v1.23.6 USE_GKE_GCLOUD_AUTH_PLUGIN: True diff --git a/.github/workflows/kind.yaml b/.github/workflows/kind.yaml index 167a0b6ef5..0c910b8b90 100644 --- a/.github/workflows/kind.yaml +++ b/.github/workflows/kind.yaml @@ -17,7 +17,7 @@ env: TIMEOUT: 2m LOG_TIME: 30m # renovate: datasource=github-releases depName=cilium/cilium - cilium_version: v1.16.8 + cilium_version: v1.17.2 kubectl_version: v1.23.6 jobs: From a8759d044a6a6519822929f291094d4b48bd54c6 Mon Sep 17 00:00:00 2001 From: Michi Mutsuzaki Date: Fri, 21 Mar 2025 22:22:25 +0000 Subject: [PATCH 2/2] Pick up the latest Cilium from main Signed-off-by: Michi Mutsuzaki --- go.mod | 114 +- go.sum | 267 +- vendor/github.com/cilium/charts/README.md | 7 + .../cilium/charts/cilium-1.15.14.tgz | Bin 0 -> 180304 bytes .../cilium/charts/cilium-1.15.15.tgz | Bin 0 -> 180321 bytes .../cilium/charts/cilium-1.16.7.tgz | Bin 0 -> 205285 bytes .../cilium/charts/cilium-1.16.8.tgz | Bin 0 -> 205318 bytes .../cilium/charts/cilium-1.17.1.tgz | Bin 0 -> 218517 bytes .../cilium/charts/cilium-1.17.2.tgz | Bin 0 -> 219438 bytes .../cilium/charts/cilium-1.18.0-pre.0.tgz | Bin 0 -> 223919 bytes vendor/github.com/cilium/charts/index.yaml | 975 ++++- vendor/github.com/cilium/cilium/.authors.aux | 9 - vendor/github.com/cilium/cilium/.clang-format | 180 - .../github.com/cilium/cilium/.clomonitor.yml | 22 - .../github.com/cilium/cilium/.gitattributes | 18 - vendor/github.com/cilium/cilium/.gitignore | 118 - .../github.com/cilium/cilium/.golangci.yaml | 228 - vendor/github.com/cilium/cilium/.mailmap | 170 - vendor/github.com/cilium/cilium/CODEOWNERS | 677 --- .../cilium/cilium/CODE_OF_CONDUCT.md | 46 - .../github.com/cilium/cilium/CONTRIBUTING.md | 23 - .../cilium/cilium/FURTHER_READINGS.rst | 87 - .../github.com/cilium/cilium/MAINTAINERS.md | 132 - vendor/github.com/cilium/cilium/Makefile | 539 --- vendor/github.com/cilium/cilium/Makefile.defs | 227 - .../github.com/cilium/cilium/Makefile.docker | 153 - vendor/github.com/cilium/cilium/Makefile.kind | 494 --- .../github.com/cilium/cilium/Makefile.quiet | 30 - vendor/github.com/cilium/cilium/README.rst | 359 -- .../cilium/cilium/SECURITY-INSIGHTS.yml | 69 - vendor/github.com/cilium/cilium/SECURITY.md | 29 - vendor/github.com/cilium/cilium/USERS.md | 930 ----- vendor/github.com/cilium/cilium/VERSION | 1 - .../cilium/cilium/api/v1/flow/flow.pb.go | 2 +- .../cilium/api/v1/observer/observer.pb.go | 2 +- .../api/v1/observer/observer_grpc.pb.go | 2 +- .../cilium/cilium/api/v1/relay/relay.pb.go | 2 +- vendor/github.com/cilium/cilium/assets.go | 12 - .../cilium/cilium-cli/cli/connectivity.go | 31 +- .../cilium-cli/clustermesh/clustermesh.go | 8 +- .../connectivity/builder/builder.go | 11 +- .../builder/client_egress_tls_sni.go | 21 + .../client-egress-l7-tls-other-sni.yaml | 31 + ...deny-ingress-source-egress-other-node.yaml | 12 + .../builder/outside_to_ingress_service.go | 40 +- ...ide_to_ingress_service_deny_all_ingress.go | 25 - .../outside_to_ingress_service_deny_cidr.go | 25 - ..._to_ingress_service_deny_world_identity.go | 30 - .../builder/pod_to_ingress_service.go | 62 +- ..._ingress_service_allow_ingress_identity.go | 25 - .../pod_to_ingress_service_deny_all.go | 22 - ...to_ingress_service_deny_backend_service.go | 27 - ...o_ingress_service_deny_ingress_identity.go | 27 - .../cilium-cli/connectivity/check/check.go | 58 +- .../cilium-cli/connectivity/check/context.go | 53 +- .../connectivity/check/deployment.go | 459 ++- .../cilium-cli/connectivity/check/features.go | 2 +- .../cilium-cli/connectivity/check/junit.go | 6 +- .../cilium-cli/connectivity/check/logger.go | 212 +- .../cilium-cli/connectivity/check/logging.go | 35 +- .../cilium-cli/connectivity/check/peer.go | 13 +- .../cilium-cli/connectivity/check/policy.go | 9 +- .../cilium-cli/connectivity/check/test.go | 19 +- .../cilium-cli/connectivity/check/wait.go | 80 + .../perf/benchmarks/netperf/perfpod.go | 93 +- .../connectivity/perf/common/metrics.go | 1 + .../connectivity/tests/egressgateway.go | 140 +- .../connectivity/tests/encryption.go | 7 - .../cilium-cli/connectivity/tests/errors.go | 4 +- .../connectivity/tests/multicast.go | 8 +- .../cilium-cli/connectivity/tests/pod.go | 67 +- .../cilium-cli/connectivity/tests/upgrade.go | 22 +- .../cilium-cli/connectivity/tests/world.go | 62 +- .../cilium/cilium-cli/encrypt/status.go | 6 +- .../cilium/cilium-cli/features/summary.go | 2 +- .../cilium/cilium-cli/internal/helm/helm.go | 6 +- .../cilium/cilium/cilium-cli/status/k8s.go | 51 +- .../cilium-cli/utils/codeowners/codeowners.go | 42 + .../cilium-cli/utils/features/features.go | 4 +- .../cilium/cilium/cilium-cli/utils/log/log.go | 57 + .../cilium/cilium/daemon/k8s/init.go | 3 +- vendor/github.com/cilium/cilium/netlify.toml | 4 - .../cilium/cilium/operator/option/config.go | 26 - .../cilium/cilium/pkg/allocator/allocator.go | 21 +- .../cilium/cilium/pkg/allocator/localkeys.go | 39 +- .../cilium/cilium/pkg/client/client.go | 9 +- .../cilium/cilium/pkg/client/config.go | 6 +- .../cilium/pkg/comparator/comparator.go | 9 +- .../cilium/pkg/container/versioned/value.go | 39 +- .../cilium/cilium/pkg/controller/manager.go | 9 +- .../cilium/cilium/pkg/counter/counter.go | 6 +- .../certificate_manager_mock.go | 51 + .../cilium/cilium/pkg/debug/subsystem.go | 7 +- .../cilium/cilium/pkg/defaults/defaults.go | 7 +- .../envoy/policy/envoy_l7_rules_translator.go | 274 ++ .../cilium/cilium/pkg/envoy/policy/sort.go | 319 ++ .../cilium/cilium/pkg/health/client/tree.go | 9 +- vendor/github.com/cilium/cilium/pkg/ip/ip.go | 8 +- .../cilium/cilium/pkg/k8s/annotate.go | 31 +- .../cilium/pkg/k8s/apis/cilium.io/register.go | 2 +- .../pkg/k8s/apis/cilium.io/utils/utils.go | 25 +- .../k8s/apis/cilium.io/v2/bgp_advert_types.go | 10 + .../pkg/k8s/apis/cilium.io/v2/ccnp_types.go | 7 +- .../pkg/k8s/apis/cilium.io/v2/cec_types.go | 10 +- .../pkg/k8s/apis/cilium.io/v2/cnp_types.go | 9 +- .../pkg/k8s/apis/cilium.io/v2/logfields.go | 11 - .../cilium.io/v2/zz_generated.deepcopy.go | 10 + .../cilium.io/v2/zz_generated.deepequal.go | 16 + .../cilium.io/v2alpha1/bgp_advert_types.go | 11 + .../cilium.io/v2alpha1/bgp_cluster_types.go | 1 + .../v2alpha1/bgp_node_override_types.go | 1 + .../apis/cilium.io/v2alpha1/bgp_node_types.go | 1 + .../apis/cilium.io/v2alpha1/bgp_peer_types.go | 1 + .../v2alpha1/zz_generated.deepcopy.go | 10 + .../v2alpha1/zz_generated.deepequal.go | 16 + .../cilium/cilium/pkg/k8s/client/cell.go | 39 +- .../pkg/k8s/client/restConfig_provider.go | 91 +- .../cilium/cilium/pkg/k8s/endpoints.go | 40 +- .../cilium/cilium/pkg/k8s/error_helpers.go | 20 +- .../cilium/cilium/pkg/k8s/informer/cast.go | 11 +- .../cilium/pkg/k8s/informer/informer.go | 5 +- .../cilium/cilium/pkg/k8s/labels.go | 14 +- .../cilium/cilium/pkg/k8s/logfields.go | 20 - .../cilium/cilium/pkg/k8s/network_policy.go | 30 +- .../github.com/cilium/cilium/pkg/k8s/node.go | 125 +- .../cilium/pkg/k8s/resource/resource.go | 119 +- .../cilium/cilium/pkg/k8s/resource/store.go | 12 +- .../cilium/cilium/pkg/k8s/resource_ctors.go | 26 +- .../cilium/cilium/pkg/k8s/service.go | 71 +- .../cilium/cilium/pkg/k8s/service_cache.go | 59 +- .../pkg/k8s/slim/k8s/apis/labels/labels.go | 11 +- .../pkg/k8s/slim/k8s/apis/labels/selector.go | 14 +- .../pkg/k8s/slim/k8s/apis/meta/v1/helpers.go | 13 +- .../cilium/cilium/pkg/k8s/synced/cell.go | 7 +- .../cilium/cilium/pkg/k8s/synced/crd.go | 35 +- .../cilium/cilium/pkg/k8s/synced/logfields.go | 20 - .../cilium/cilium/pkg/k8s/synced/resources.go | 30 +- .../cilium/cilium/pkg/k8s/version/version.go | 26 +- .../pkg/k8s/watchers/resources/resources.go | 4 + .../cilium/cilium/pkg/kvstore/backend.go | 13 +- .../cilium/cilium/pkg/kvstore/cell.go | 16 +- .../cilium/cilium/pkg/kvstore/client.go | 43 +- .../cilium/cilium/pkg/kvstore/config.go | 24 +- .../cilium/cilium/pkg/kvstore/dummy.go | 5 +- .../cilium/cilium/pkg/kvstore/etcd.go | 351 +- .../cilium/cilium/pkg/kvstore/etcd_lease.go | 23 +- .../cilium/cilium/pkg/kvstore/lock.go | 27 +- .../cilium/cilium/pkg/kvstore/logfields.go | 7 +- .../cilium/cilium/pkg/kvstore/store/cell.go | 12 +- .../cilium/cilium/pkg/kvstore/store/store.go | 70 +- .../cilium/pkg/kvstore/store/syncstore.go | 50 +- .../cilium/pkg/kvstore/store/watchstore.go | 49 +- .../cilium/pkg/kvstore/store/watchstoremgr.go | 28 +- .../cilium/cilium/pkg/kvstore/trace.go | 9 +- .../cilium/cilium/pkg/labels/labels.go | 5 +- .../cilium/cilium/pkg/labels/oplabels.go | 25 +- .../cilium/pkg/logging/logfields/logfields.go | 114 + .../cilium/cilium/pkg/logging/logging.go | 9 +- .../cilium/cilium/pkg/metrics/metrics.go | 13 + .../{metrics_unix.go => metrics_linux.go} | 17 +- .../{metrics_windows.go => metrics_other.go} | 2 + .../cilium/cilium/pkg/monitor/api/types.go | 9 +- .../cilium/cilium/pkg/node/address.go | 4 +- .../cilium/cilium/pkg/option/config.go | 76 +- .../cilium/cilium/pkg/option/daemon.go | 6 +- .../cilium/cilium/pkg/option/endpoint.go | 8 +- .../cilium/cilium/pkg/option/option.go | 15 +- .../cilium/cilium/pkg/policy/api/http.go | 5 +- .../cilium/pkg/policy/api/rule_validation.go | 7 +- .../cilium/cilium/pkg/policy/api/selector.go | 13 +- .../cilium/cilium/pkg/policy/api/utils.go | 25 +- .../cilium/cilium/pkg/policy/config.go | 3 - .../github.com/cilium/cilium/pkg/policy/l4.go | 324 +- .../cilium/cilium/pkg/policy/lookup.go | 16 +- .../cilium/cilium/pkg/policy/mapstate.go | 62 +- .../cilium/cilium/pkg/policy/policy.go | 2 - .../cilium/cilium/pkg/policy/repository.go | 65 +- .../cilium/cilium/pkg/policy/resolve.go | 40 +- .../cilium/cilium/pkg/policy/rule.go | 86 +- .../cilium/cilium/pkg/policy/rules.go | 10 +- .../cilium/cilium/pkg/policy/selectorcache.go | 89 +- .../pkg/policy/selectorcache_selector.go | 30 +- .../cilium/cilium/pkg/policy/trigger.go | 16 +- .../cilium/cilium/pkg/policy/types/entry.go | 34 +- .../cilium/pkg/policy/types/selector.go | 5 +- .../cilium/cilium/pkg/promise/promise.go | 11 - .../cilium/cilium/pkg/safetime/safetime.go | 25 +- .../cilium/cilium/pkg/spanstat/spanstat.go | 8 +- vendor/github.com/cilium/cilium/stable.txt | 1 - vendor/github.com/cilium/ebpf/.golangci.yaml | 12 + vendor/github.com/cilium/ebpf/asm/func.go | 6 - .../github.com/cilium/ebpf/asm/func_string.go | 5 +- .../cilium/ebpf/attachtype_string.go | 5 +- vendor/github.com/cilium/ebpf/btf/btf.go | 8 +- vendor/github.com/cilium/ebpf/btf/ext_info.go | 19 +- vendor/github.com/cilium/ebpf/collection.go | 31 +- vendor/github.com/cilium/ebpf/elf_sections.go | 2 + vendor/github.com/cilium/ebpf/features/map.go | 2 + .../github.com/cilium/ebpf/features/misc.go | 2 + .../github.com/cilium/ebpf/features/prog.go | 46 +- .../cilium/ebpf/features/version.go | 2 + vendor/github.com/cilium/ebpf/info.go | 23 +- .../cilium/ebpf/internal/feature.go | 2 +- .../github.com/cilium/ebpf/internal/goos.go | 7 + .../cilium/ebpf/internal/kallsyms/kallsyms.go | 12 + .../cilium/ebpf/internal/linux/auxv.go | 26 +- .../cilium/ebpf/internal/sys/syscall.go | 11 +- .../cilium/ebpf/internal/sys/types.go | 51 +- .../cilium/ebpf/internal/tracefs/kprobe.go | 4 + .../cilium/ebpf/internal/unix/errno_linux.go | 29 + .../cilium/ebpf/internal/unix/errno_other.go | 29 + .../internal/unix/errno_string_windows.go | 59 + .../ebpf/internal/unix/errno_windows.go | 78 + .../cilium/ebpf/internal/unix/error.go | 23 + .../ebpf/internal/unix/strings_other.go | 11 + .../ebpf/internal/unix/strings_windows.go | 19 + .../cilium/ebpf/internal/unix/types_linux.go | 24 +- .../cilium/ebpf/internal/unix/types_other.go | 86 +- .../cilium/ebpf/link/kprobe_multi.go | 78 +- vendor/github.com/cilium/ebpf/linker.go | 10 +- vendor/github.com/cilium/ebpf/netlify.toml | 1 + vendor/github.com/cilium/ebpf/prog.go | 2 +- vendor/github.com/cilium/ebpf/types.go | 1 + vendor/github.com/cilium/hive/cell/invoke.go | 2 +- vendor/github.com/cilium/hive/command.go | 18 +- vendor/github.com/cilium/hive/hive.go | 42 +- vendor/github.com/cilium/hive/hivetest/doc.go | 5 + .../cilium/hive/hivetest/lifecycle.go | 63 + .../github.com/cilium/hive/hivetest/slog.go | 109 + vendor/github.com/cilium/hive/script.go | 4 +- vendor/github.com/cilium/hive/script/cmds.go | 7 +- .../github.com/cilium/hive/script/engine.go | 27 +- vendor/github.com/cilium/hive/script/state.go | 1 + .../proxy/go/cilium/api/bpf_metadata.pb.go | 23 +- .../go/cilium/api/bpf_metadata.pb.validate.go | 2 + vendor/github.com/cilium/statedb/any_table.go | 3 +- vendor/github.com/cilium/statedb/http.go | 6 +- vendor/github.com/cilium/statedb/iterator.go | 13 +- vendor/github.com/cilium/statedb/table.go | 3 +- vendor/github.com/cilium/statedb/txn.go | 106 +- vendor/github.com/cilium/statedb/types.go | 14 +- .../cilium/workerpool/.golangci.yml | 41 +- vendor/github.com/cilium/workerpool/README.md | 1 - .../cilium/workerpool/workerpool.go | 2 +- .../containerd/containerd/errdefs/grpc.go | 16 +- .../containerd/containerd/filters/parser.go | 14 +- .../containerd/containerd/images/image.go | 2 +- .../containerd/images/mediatypes.go | 13 + .../containerd/containerd/remotes/handlers.go | 2 + .../containerd/containerd/version/version.go | 2 +- vendor/github.com/docker/docker/AUTHORS | 32 +- .../docker/docker/api/types/filters/errors.go | 13 - .../docker/docker/api/types/filters/parse.go | 16 +- .../docker/api/types/registry/authconfig.go | 18 +- .../docker/api/types/registry/registry.go | 28 +- .../docker/api/types/registry/search.go | 9 +- .../docker/docker/errdefs/helpers.go | 52 +- .../docker/docker/errdefs/http_helpers.go | 29 +- vendor/github.com/docker/docker/errdefs/is.go | 26 +- .../docker/internal/lazyregexp/lazyregexp.go | 90 + .../docker/docker/pkg/ioutils/buffer.go | 51 - .../docker/docker/pkg/ioutils/bytespipe.go | 193 - .../docker/docker/pkg/ioutils/fswriters.go | 163 - .../docker/docker/pkg/ioutils/readers.go | 172 - .../docker/docker/pkg/ioutils/writeflusher.go | 98 - .../docker/docker/pkg/ioutils/writers.go | 81 - .../github.com/docker/docker/registry/auth.go | 33 +- .../docker/docker/registry/config.go | 114 +- .../docker/docker/registry/registry.go | 21 +- .../docker/docker/registry/search_session.go | 33 +- .../docker/docker/registry/service.go | 26 +- .../docker/docker/registry/service_v2.go | 59 +- .../docker/docker/registry/types.go | 21 - .../go-openapi/errors/.golangci.yml | 31 +- vendor/github.com/go-openapi/errors/api.go | 2 +- .../github.com/go-openapi/errors/headers.go | 2 +- .../go-openapi/errors/middleware.go | 2 +- .../github.com/go-openapi/errors/parsing.go | 3 +- vendor/github.com/go-openapi/errors/schema.go | 110 +- .../github.com/go-openapi/swag/.golangci.yml | 34 +- vendor/github.com/go-openapi/swag/errors.go | 15 + vendor/github.com/go-openapi/swag/json.go | 3 +- vendor/github.com/go-openapi/swag/loading.go | 2 +- vendor/github.com/go-openapi/swag/yaml.go | 32 +- .../mapstructure/v2}/.editorconfig | 6 +- .../go-viper/mapstructure/v2/.gitignore | 6 + .../go-viper/mapstructure/v2/.golangci.yaml | 23 + .../go-viper/mapstructure/v2/CHANGELOG.md | 104 + .../go-viper/mapstructure/v2/LICENSE | 21 + .../go-viper/mapstructure/v2/README.md | 80 + .../go-viper/mapstructure/v2/decode_hooks.go | 630 +++ .../go-viper/mapstructure/v2/flake.lock | 472 +++ .../mapstructure/v2}/flake.nix | 36 +- .../mapstructure/v2/internal/errors/errors.go | 11 + .../mapstructure/v2/internal/errors/join.go | 9 + .../v2/internal/errors/join_go1_19.go | 61 + .../go-viper/mapstructure/v2/mapstructure.go | 1620 ++++++++ .../mapstructure/v2/reflect_go1_19.go | 44 + .../mapstructure/v2/reflect_go1_20.go | 10 + .../google/go-cmp/cmp/cmpopts/sort.go | 64 +- .../go-cmp/cmp/internal/function/func.go | 7 + .../github.com/google/go-cmp/cmp/options.go | 10 +- .../google/go-github/v68/github/orgs_rules.go | 115 - .../google/go-github/v68/github/packages.go | 143 - .../go-github/v68/github/repos_rules.go | 995 ----- .../google/go-github/{v68 => v70}/AUTHORS | 0 .../google/go-github/{v68 => v70}/LICENSE | 0 .../go-github/{v68 => v70}/github/actions.go | 0 .../{v68 => v70}/github/actions_artifacts.go | 30 +- .../{v68 => v70}/github/actions_cache.go | 0 .../v70/github/actions_hosted_runners.go | 376 ++ .../{v68 => v70}/github/actions_oidc.go | 0 .../github/actions_permissions_enterprise.go | 0 .../github/actions_permissions_orgs.go | 0 .../github/actions_required_workflows.go | 0 .../github/actions_runner_groups.go | 0 .../{v68 => v70}/github/actions_runners.go | 0 .../{v68 => v70}/github/actions_secrets.go | 0 .../{v68 => v70}/github/actions_variables.go | 0 .../github/actions_workflow_jobs.go | 30 +- .../github/actions_workflow_runs.go | 73 +- .../{v68 => v70}/github/actions_workflows.go | 0 .../go-github/{v68 => v70}/github/activity.go | 0 .../{v68 => v70}/github/activity_events.go | 0 .../github/activity_notifications.go | 0 .../{v68 => v70}/github/activity_star.go | 0 .../{v68 => v70}/github/activity_watching.go | 0 .../go-github/{v68 => v70}/github/admin.go | 0 .../{v68 => v70}/github/admin_orgs.go | 0 .../{v68 => v70}/github/admin_stats.go | 10 +- .../{v68 => v70}/github/admin_users.go | 0 .../go-github/{v68 => v70}/github/apps.go | 0 .../{v68 => v70}/github/apps_hooks.go | 0 .../github/apps_hooks_deliveries.go | 0 .../{v68 => v70}/github/apps_installation.go | 0 .../{v68 => v70}/github/apps_manifest.go | 0 .../{v68 => v70}/github/apps_marketplace.go | 0 .../{v68 => v70}/github/attestations.go | 0 .../{v68 => v70}/github/authorizations.go | 0 .../go-github/{v68 => v70}/github/billing.go | 0 .../go-github/{v68 => v70}/github/checks.go | 4 +- .../{v68 => v70}/github/code_scanning.go | 11 +- .../{v68 => v70}/github/codesofconduct.go | 0 .../{v68 => v70}/github/codespaces.go | 0 .../{v68 => v70}/github/codespaces_secrets.go | 0 .../go-github/{v68 => v70}/github/copilot.go | 6 +- .../{v68 => v70}/github/dependabot.go | 0 .../{v68 => v70}/github/dependabot_alerts.go | 0 .../{v68 => v70}/github/dependabot_secrets.go | 0 .../{v68 => v70}/github/dependency_graph.go | 0 .../github/dependency_graph_snapshots.go | 0 .../go-github/{v68 => v70}/github/doc.go | 18 +- .../go-github/{v68 => v70}/github/emojis.go | 0 .../{v68 => v70}/github/enterprise.go | 0 .../enterprise_actions_hosted_runners.go | 234 ++ .../enterprise_actions_runner_groups.go | 0 .../github/enterprise_actions_runners.go | 0 .../github/enterprise_audit_log.go | 0 .../enterprise_code_security_and_analysis.go | 0 .../v70/github/enterprise_manage_ghes.go | 163 + .../github/enterprise_manage_ghes_config.go | 516 +++ .../enterprise_manage_ghes_maintenance.go | 94 + .../v70/github/enterprise_manage_ghes_ssh.go | 99 + .../enterprise_network_configurations.go | 139 + .../github/enterprise_properties.go | 0 .../go-github/v70/github/enterprise_rules.go | 118 + .../go-github/{v68 => v70}/github/event.go | 0 .../{v68 => v70}/github/event_types.go | 79 +- .../go-github/{v68 => v70}/github/gists.go | 2 +- .../{v68 => v70}/github/gists_comments.go | 0 .../go-github/{v68 => v70}/github/git.go | 0 .../{v68 => v70}/github/git_blobs.go | 0 .../{v68 => v70}/github/git_commits.go | 0 .../go-github/{v68 => v70}/github/git_refs.go | 0 .../go-github/{v68 => v70}/github/git_tags.go | 0 .../{v68 => v70}/github/git_trees.go | 0 .../{v68 => v70}/github/github-accessors.go | 3654 +++++++++++++---- .../go-github/{v68 => v70}/github/github.go | 216 +- .../{v68 => v70}/github/gitignore.go | 0 .../{v68 => v70}/github/interactions.go | 0 .../{v68 => v70}/github/interactions_orgs.go | 0 .../{v68 => v70}/github/interactions_repos.go | 0 .../{v68 => v70}/github/issue_import.go | 0 .../go-github/{v68 => v70}/github/issues.go | 13 + .../{v68 => v70}/github/issues_assignees.go | 0 .../{v68 => v70}/github/issues_comments.go | 0 .../{v68 => v70}/github/issues_events.go | 0 .../{v68 => v70}/github/issues_labels.go | 0 .../{v68 => v70}/github/issues_milestones.go | 0 .../{v68 => v70}/github/issues_timeline.go | 0 .../go-github/{v68 => v70}/github/licenses.go | 0 .../go-github/{v68 => v70}/github/markdown.go | 0 .../go-github/{v68 => v70}/github/messages.go | 0 .../go-github/{v68 => v70}/github/meta.go | 0 .../{v68 => v70}/github/migrations.go | 0 .../github/migrations_source_import.go | 0 .../{v68 => v70}/github/migrations_user.go | 0 .../go-github/{v68 => v70}/github/orgs.go | 0 .../github/orgs_actions_allowed.go | 0 .../github/orgs_actions_permissions.go | 0 .../{v68 => v70}/github/orgs_attestations.go | 0 .../{v68 => v70}/github/orgs_audit_log.go | 0 .../orgs_codesecurity_configurations.go | 0 .../github/orgs_credential_authorizations.go | 0 .../github/orgs_custom_repository_roles.go | 0 .../{v68 => v70}/github/orgs_hooks.go | 0 .../github/orgs_hooks_configuration.go | 0 .../github/orgs_hooks_deliveries.go | 0 .../{v68 => v70}/github/orgs_members.go | 0 .../v70/github/orgs_network_configurations.go | 236 ++ .../github/orgs_organization_roles.go | 0 .../github/orgs_outside_collaborators.go | 0 .../{v68 => v70}/github/orgs_packages.go | 0 .../github/orgs_personal_access_tokens.go | 6 + .../{v68 => v70}/github/orgs_properties.go | 0 .../google/go-github/v70/github/orgs_rules.go | 140 + .../github/orgs_security_managers.go | 6 + .../github/orgs_users_blocking.go | 0 .../google/go-github/v70/github/packages.go | 318 ++ .../go-github/{v68 => v70}/github/pulls.go | 96 +- .../{v68 => v70}/github/pulls_comments.go | 0 .../{v68 => v70}/github/pulls_reviewers.go | 7 +- .../{v68 => v70}/github/pulls_reviews.go | 0 .../{v68 => v70}/github/pulls_threads.go | 0 .../{v68 => v70}/github/rate_limit.go | 16 +- .../{v68 => v70}/github/reactions.go | 3 +- .../go-github/{v68 => v70}/github/repos.go | 6 +- .../github/repos_actions_access.go | 0 .../github/repos_actions_allowed.go | 0 .../github/repos_actions_permissions.go | 0 .../{v68 => v70}/github/repos_attestations.go | 0 .../{v68 => v70}/github/repos_autolinks.go | 0 .../{v68 => v70}/github/repos_codeowners.go | 0 .../github/repos_collaborators.go | 0 .../{v68 => v70}/github/repos_comments.go | 0 .../{v68 => v70}/github/repos_commits.go | 0 .../github/repos_community_health.go | 0 .../{v68 => v70}/github/repos_contents.go | 33 +- .../repos_deployment_branch_policies.go | 0 .../repos_deployment_protection_rules.go | 0 .../{v68 => v70}/github/repos_deployments.go | 0 .../{v68 => v70}/github/repos_environments.go | 0 .../{v68 => v70}/github/repos_forks.go | 0 .../{v68 => v70}/github/repos_hooks.go | 0 .../github/repos_hooks_configuration.go | 0 .../github/repos_hooks_deliveries.go | 0 .../{v68 => v70}/github/repos_invitations.go | 0 .../{v68 => v70}/github/repos_keys.go | 0 .../{v68 => v70}/github/repos_lfs.go | 0 .../{v68 => v70}/github/repos_merging.go | 0 .../{v68 => v70}/github/repos_pages.go | 29 + .../github/repos_prereceive_hooks.go | 0 .../{v68 => v70}/github/repos_properties.go | 0 .../{v68 => v70}/github/repos_releases.go | 0 .../go-github/v70/github/repos_rules.go | 227 + .../{v68 => v70}/github/repos_stats.go | 0 .../{v68 => v70}/github/repos_statuses.go | 0 .../{v68 => v70}/github/repos_tags.go | 0 .../{v68 => v70}/github/repos_traffic.go | 0 .../google/go-github/v70/github/rules.go | 1208 ++++++ .../go-github/{v68 => v70}/github/scim.go | 51 + .../go-github/{v68 => v70}/github/search.go | 0 .../{v68 => v70}/github/secret_scanning.go | 10 + .../github/security_advisories.go | 0 .../go-github/{v68 => v70}/github/strings.go | 0 .../go-github/{v68 => v70}/github/teams.go | 3 + .../github/teams_discussion_comments.go | 4 +- .../{v68 => v70}/github/teams_discussions.go | 0 .../{v68 => v70}/github/teams_members.go | 0 .../{v68 => v70}/github/timestamp.go | 2 +- .../go-github/{v68 => v70}/github/users.go | 2 +- .../github/users_administration.go | 0 .../{v68 => v70}/github/users_attestations.go | 0 .../{v68 => v70}/github/users_blocking.go | 0 .../{v68 => v70}/github/users_emails.go | 0 .../{v68 => v70}/github/users_followers.go | 0 .../{v68 => v70}/github/users_gpg_keys.go | 0 .../{v68 => v70}/github/users_keys.go | 0 .../{v68 => v70}/github/users_packages.go | 0 .../github/users_ssh_signing_keys.go | 0 .../{v68 => v70}/github/with_appengine.go | 0 .../{v68 => v70}/github/without_appengine.go | 0 vendor/github.com/hashicorp/hcl/.gitignore | 9 - vendor/github.com/hashicorp/hcl/.travis.yml | 13 - vendor/github.com/hashicorp/hcl/LICENSE | 354 -- vendor/github.com/hashicorp/hcl/Makefile | 18 - vendor/github.com/hashicorp/hcl/README.md | 125 - vendor/github.com/hashicorp/hcl/appveyor.yml | 19 - vendor/github.com/hashicorp/hcl/decoder.go | 769 ---- vendor/github.com/hashicorp/hcl/hcl.go | 11 - .../github.com/hashicorp/hcl/hcl/ast/ast.go | 226 - .../github.com/hashicorp/hcl/hcl/ast/walk.go | 52 - .../hashicorp/hcl/hcl/parser/error.go | 17 - .../hashicorp/hcl/hcl/parser/parser.go | 532 --- .../hashicorp/hcl/hcl/printer/nodes.go | 789 ---- .../hashicorp/hcl/hcl/printer/printer.go | 66 - .../hashicorp/hcl/hcl/scanner/scanner.go | 652 --- .../hashicorp/hcl/hcl/strconv/quote.go | 241 -- .../hashicorp/hcl/hcl/token/position.go | 46 - .../hashicorp/hcl/hcl/token/token.go | 219 - .../hashicorp/hcl/json/parser/flatten.go | 117 - .../hashicorp/hcl/json/parser/parser.go | 313 -- .../hashicorp/hcl/json/scanner/scanner.go | 451 -- .../hashicorp/hcl/json/token/position.go | 46 - .../hashicorp/hcl/json/token/token.go | 118 - vendor/github.com/hashicorp/hcl/lex.go | 38 - vendor/github.com/hashicorp/hcl/parse.go | 39 - .../klauspost/compress/.goreleaser.yml | 6 +- .../github.com/klauspost/compress/README.md | 29 +- .../klauspost/compress/fse/decompress.go | 2 +- .../klauspost/compress/huff0/decompress.go | 4 +- .../klauspost/compress/zstd/blockdec.go | 4 +- .../klauspost/compress/zstd/enc_better.go | 32 +- .../klauspost/compress/zstd/enc_dfast.go | 16 +- .../klauspost/compress/zstd/encoder.go | 45 +- .../klauspost/compress/zstd/framedec.go | 4 +- .../klauspost/compress/zstd/seqdec_amd64.go | 4 +- .../klauspost/compress/zstd/seqdec_amd64.s | 8 +- .../klauspost/compress/zstd/zstd.go | 4 + .../magiconair/properties/.gitignore | 6 - .../magiconair/properties/CHANGELOG.md | 205 - .../magiconair/properties/LICENSE.md | 24 - .../magiconair/properties/README.md | 128 - .../magiconair/properties/decode.go | 289 -- .../github.com/magiconair/properties/doc.go | 155 - .../magiconair/properties/integrate.go | 35 - .../github.com/magiconair/properties/lex.go | 395 -- .../github.com/magiconair/properties/load.go | 293 -- .../magiconair/properties/parser.go | 86 - .../magiconair/properties/properties.go | 848 ---- .../magiconair/properties/rangecheck.go | 31 - .../mailru/easyjson/jlexer/bytestostr.go | 5 +- .../mailru/easyjson/jlexer/lexer.go | 113 +- .../mailru/easyjson/jwriter/writer.go | 12 + .../osrg/gobgp/v3/pkg/packet/bgp/bgp.go | 6 +- .../pelletier/go-toml/v2/.goreleaser.yaml | 1 + .../github.com/pelletier/go-toml/v2/README.md | 2 +- .../pelletier/go-toml/v2/marshaler.go | 24 +- .../pelletier/go-toml/v2/unmarshaler.go | 45 +- .../client_golang/prometheus/desc.go | 15 +- .../prometheus/go_collector_latest.go | 2 +- .../client_golang/prometheus/histogram.go | 249 +- .../prometheus/internal/difflib.go | 19 +- .../prometheus/internal/go_runtime_metrics.go | 3 +- .../client_golang/prometheus/metric.go | 24 +- .../prometheus/process_collector.go | 31 +- .../prometheus/process_collector_darwin.go | 130 + .../process_collector_mem_cgo_darwin.c | 84 + .../process_collector_mem_cgo_darwin.go | 51 + .../process_collector_mem_nocgo_darwin.go | 39 + ....go => process_collector_not_supported.go} | 17 +- ....go => process_collector_procfsenabled.go} | 20 +- .../prometheus/process_collector_wasip1.go | 26 - .../prometheus/process_collector_windows.go | 21 +- .../client_golang/prometheus/promhttp/http.go | 23 +- .../client_golang/prometheus/summary.go | 7 +- .../prometheus/common/model/alert.go | 2 +- .../prometheus/common/model/labels.go | 2 +- .../prometheus/common/model/metric.go | 28 +- .../prometheus/procfs/.golangci.yml | 11 +- .../prometheus/procfs/Makefile.common | 8 +- vendor/github.com/prometheus/procfs/README.md | 6 +- vendor/github.com/prometheus/procfs/arp.go | 4 +- vendor/github.com/prometheus/procfs/fs.go | 10 +- .../prometheus/procfs/fs_statfs_notype.go | 4 +- .../github.com/prometheus/procfs/fscache.go | 6 +- .../prometheus/procfs/internal/fs/fs.go | 3 + .../prometheus/procfs/internal/util/parse.go | 14 + .../prometheus/procfs/mountstats.go | 4 +- .../prometheus/procfs/net_dev_snmp6.go | 96 + .../prometheus/procfs/net_ip_socket.go | 8 +- .../github.com/prometheus/procfs/net_tcp.go | 4 + .../github.com/prometheus/procfs/net_unix.go | 8 +- .../prometheus/procfs/proc_cgroup.go | 2 +- .../github.com/prometheus/procfs/proc_io.go | 2 +- .../prometheus/procfs/proc_smaps.go | 4 +- .../prometheus/procfs/proc_status.go | 18 +- .../sagikazarmark/locafero/finder.go | 6 +- .../sagikazarmark/locafero/flake.lock | 303 +- .../sagikazarmark/locafero/flake.nix | 21 +- .../github.com/sagikazarmark/locafero/glob.go | 5 + .../sagikazarmark/locafero/glob_windows.go | 8 + .../sagikazarmark/slog-shim/.gitignore | 4 - .../sagikazarmark/slog-shim/LICENSE | 27 - .../sagikazarmark/slog-shim/README.md | 81 - .../sagikazarmark/slog-shim/attr.go | 74 - .../sagikazarmark/slog-shim/attr_120.go | 75 - .../sagikazarmark/slog-shim/flake.lock | 273 -- .../sagikazarmark/slog-shim/handler.go | 45 - .../sagikazarmark/slog-shim/handler_120.go | 45 - .../sagikazarmark/slog-shim/json_handler.go | 23 - .../slog-shim/json_handler_120.go | 24 - .../sagikazarmark/slog-shim/level.go | 61 - .../sagikazarmark/slog-shim/level_120.go | 61 - .../sagikazarmark/slog-shim/logger.go | 98 - .../sagikazarmark/slog-shim/logger_120.go | 99 - .../sagikazarmark/slog-shim/record.go | 31 - .../sagikazarmark/slog-shim/record_120.go | 32 - .../sagikazarmark/slog-shim/text_handler.go | 23 - .../slog-shim/text_handler_120.go | 24 - .../sagikazarmark/slog-shim/value.go | 109 - .../sagikazarmark/slog-shim/value_120.go | 110 - vendor/github.com/spf13/afero/README.md | 39 +- vendor/github.com/spf13/cobra/README.md | 5 +- vendor/github.com/spf13/cobra/active_help.go | 2 +- .../spf13/cobra/bash_completionsV2.go | 152 +- vendor/github.com/spf13/cobra/cobra.go | 16 +- vendor/github.com/spf13/cobra/command.go | 331 +- vendor/github.com/spf13/cobra/completions.go | 126 +- .../spf13/cobra/powershell_completions.go | 35 +- vendor/github.com/spf13/viper/.golangci.yaml | 3 - vendor/github.com/spf13/viper/README.md | 21 +- vendor/github.com/spf13/viper/UPDATES.md | 126 + vendor/github.com/spf13/viper/encoding.go | 181 + vendor/github.com/spf13/viper/experimental.go | 8 + vendor/github.com/spf13/viper/file.go | 54 +- vendor/github.com/spf13/viper/file_finder.go | 38 - vendor/github.com/spf13/viper/finder.go | 55 + vendor/github.com/spf13/viper/flake.lock | 303 +- vendor/github.com/spf13/viper/flake.nix | 2 +- .../spf13/viper/internal/encoding/decoder.go | 61 - .../spf13/viper/internal/encoding/encoder.go | 60 - .../spf13/viper/internal/encoding/error.go | 7 - .../viper/internal/encoding/hcl/codec.go | 40 - .../viper/internal/encoding/ini/codec.go | 99 - .../viper/internal/encoding/ini/map_utils.go | 74 - .../internal/encoding/javaproperties/codec.go | 86 - .../encoding/javaproperties/map_utils.go | 74 - .../spf13/viper/internal/features/finder.go | 5 + .../viper/internal/features/finder_default.go | 5 + vendor/github.com/spf13/viper/logger.go | 39 +- vendor/github.com/spf13/viper/remote.go | 256 ++ vendor/github.com/spf13/viper/util.go | 11 +- vendor/github.com/spf13/viper/viper.go | 539 +-- .../go.etcd.io/etcd/api/v3/version/version.go | 2 +- vendor/go.opentelemetry.io/otel/.gitignore | 1 + vendor/go.opentelemetry.io/otel/.golangci.yml | 103 +- vendor/go.opentelemetry.io/otel/CHANGELOG.md | 44 +- .../go.opentelemetry.io/otel/CONTRIBUTING.md | 12 + vendor/go.opentelemetry.io/otel/Makefile | 39 +- vendor/go.opentelemetry.io/otel/README.md | 13 +- vendor/go.opentelemetry.io/otel/RELEASING.md | 11 +- .../otel/dependencies.Dockerfile | 3 + vendor/go.opentelemetry.io/otel/renovate.json | 6 +- .../go.opentelemetry.io/otel/requirements.txt | 2 +- vendor/go.opentelemetry.io/otel/trace/auto.go | 661 +++ .../otel/trace/internal/telemetry/attr.go | 58 + .../otel/trace/internal/telemetry/doc.go | 8 + .../otel/trace/internal/telemetry/id.go | 103 + .../otel/trace/internal/telemetry/number.go | 67 + .../otel/trace/internal/telemetry/resource.go | 66 + .../otel/trace/internal/telemetry/scope.go | 67 + .../otel/trace/internal/telemetry/span.go | 460 +++ .../otel/trace/internal/telemetry/status.go | 40 + .../otel/trace/internal/telemetry/traces.go | 189 + .../otel/trace/internal/telemetry/value.go | 453 ++ vendor/go.opentelemetry.io/otel/trace/noop.go | 20 +- vendor/go.opentelemetry.io/otel/version.go | 2 +- vendor/go.opentelemetry.io/otel/versions.yaml | 7 +- vendor/golang.org/x/exp/LICENSE | 27 - vendor/golang.org/x/exp/PATENTS | 22 - .../x/exp/constraints/constraints.go | 50 - vendor/golang.org/x/exp/slices/cmp.go | 44 - vendor/golang.org/x/exp/slices/slices.go | 515 --- vendor/golang.org/x/exp/slices/sort.go | 197 - .../golang.org/x/exp/slices/zsortanyfunc.go | 479 --- .../golang.org/x/exp/slices/zsortordered.go | 481 --- vendor/golang.org/x/exp/slog/attr.go | 102 - vendor/golang.org/x/exp/slog/doc.go | 316 -- vendor/golang.org/x/exp/slog/handler.go | 577 --- .../x/exp/slog/internal/buffer/buffer.go | 84 - .../x/exp/slog/internal/ignorepc.go | 9 - vendor/golang.org/x/exp/slog/json_handler.go | 336 -- vendor/golang.org/x/exp/slog/level.go | 201 - vendor/golang.org/x/exp/slog/logger.go | 343 -- vendor/golang.org/x/exp/slog/noplog.bench | 36 - vendor/golang.org/x/exp/slog/record.go | 207 - vendor/golang.org/x/exp/slog/text_handler.go | 161 - vendor/golang.org/x/exp/slog/value.go | 456 -- vendor/golang.org/x/exp/slog/value_119.go | 53 - vendor/golang.org/x/exp/slog/value_120.go | 39 - .../x/net/context/ctxhttp/ctxhttp.go | 2 +- vendor/golang.org/x/net/http2/server.go | 121 +- vendor/golang.org/x/net/http2/transport.go | 52 +- .../x/net/internal/httpcommon/headermap.go | 6 +- .../x/net/internal/httpcommon/request.go | 166 +- vendor/golang.org/x/net/proxy/per_host.go | 8 +- vendor/golang.org/x/oauth2/oauth2.go | 8 +- vendor/golang.org/x/oauth2/pkce.go | 4 +- vendor/golang.org/x/sync/errgroup/errgroup.go | 2 +- vendor/golang.org/x/sync/errgroup/go120.go | 13 - .../golang.org/x/sync/errgroup/pre_go120.go | 14 - vendor/golang.org/x/time/rate/rate.go | 17 +- .../api/annotations/annotations.pb.go | 2 +- .../googleapis/api/annotations/client.pb.go | 2 +- .../api/annotations/field_behavior.pb.go | 2 +- .../api/annotations/field_info.pb.go | 2 +- .../googleapis/api/annotations/http.pb.go | 2 +- .../googleapis/api/annotations/resource.pb.go | 2 +- .../googleapis/api/annotations/routing.pb.go | 2 +- .../api/expr/v1alpha1/checked.pb.go | 2 +- .../googleapis/api/expr/v1alpha1/eval.pb.go | 2 +- .../api/expr/v1alpha1/explain.pb.go | 2 +- .../googleapis/api/expr/v1alpha1/syntax.pb.go | 2 +- .../googleapis/api/expr/v1alpha1/value.pb.go | 2 +- .../googleapis/api/launch_stage.pb.go | 2 +- .../googleapis/rpc/status/status.pb.go | 2 +- .../grpc/balancer/balancer.go | 22 +- .../endpointsharding/endpointsharding.go | 358 ++ .../pickfirst/pickfirstleaf/pickfirstleaf.go | 2 +- .../grpc/balancer/roundrobin/roundrobin.go | 70 +- .../grpc/balancer/subconn.go | 2 +- .../grpc/balancer_wrapper.go | 9 +- .../grpc_binarylog_v1/binarylog.pb.go | 118 +- vendor/google.golang.org/grpc/clientconn.go | 47 +- vendor/google.golang.org/grpc/dialoptions.go | 34 +- .../balancer/gracefulswitch/gracefulswitch.go | 10 +- .../grpc/internal/envconfig/xds.go | 10 +- .../grpc/internal/grpcsync/oncefunc.go | 32 - .../grpc/internal/internal.go | 23 + .../proxyattributes/proxyattributes.go | 54 + .../delegatingresolver/delegatingresolver.go | 329 ++ .../grpc/internal/transport/http2_client.go | 9 +- .../grpc/internal/transport/proxy.go | 62 +- .../grpc/internal/transport/transport.go | 2 - .../google.golang.org/grpc/picker_wrapper.go | 2 +- .../grpc/resolver/resolver.go | 3 + .../grpc/resolver_wrapper.go | 13 +- vendor/google.golang.org/grpc/rpc_util.go | 83 +- vendor/google.golang.org/grpc/server.go | 23 +- vendor/google.golang.org/grpc/stream.go | 300 +- vendor/google.golang.org/grpc/version.go | 2 +- vendor/gopkg.in/ini.v1/.editorconfig | 12 - vendor/gopkg.in/ini.v1/.gitignore | 7 - vendor/gopkg.in/ini.v1/.golangci.yml | 27 - vendor/gopkg.in/ini.v1/LICENSE | 191 - vendor/gopkg.in/ini.v1/Makefile | 15 - vendor/gopkg.in/ini.v1/README.md | 43 - vendor/gopkg.in/ini.v1/codecov.yml | 16 - vendor/gopkg.in/ini.v1/data_source.go | 76 - vendor/gopkg.in/ini.v1/deprecated.go | 22 - vendor/gopkg.in/ini.v1/error.go | 49 - vendor/gopkg.in/ini.v1/file.go | 541 --- vendor/gopkg.in/ini.v1/helper.go | 24 - vendor/gopkg.in/ini.v1/ini.go | 176 - vendor/gopkg.in/ini.v1/key.go | 837 ---- vendor/gopkg.in/ini.v1/parser.go | 520 --- vendor/gopkg.in/ini.v1/section.go | 256 -- vendor/gopkg.in/ini.v1/struct.go | 747 ---- .../helm.sh/helm/v3/pkg/chartutil/coalesce.go | 12 + vendor/helm.sh/helm/v3/pkg/kube/resource.go | 2 +- vendor/k8s.io/api/resource/v1alpha3/types.go | 4 + vendor/k8s.io/api/resource/v1beta1/types.go | 4 + vendor/modules.txt | 211 +- .../structured-merge-diff/v4/merge/update.go | 50 +- .../structured-merge-diff/v4/typed/typed.go | 47 +- .../structured-merge-diff/v4/value/scalar.go | 2 +- 757 files changed, 23763 insertions(+), 30263 deletions(-) create mode 100644 vendor/github.com/cilium/charts/cilium-1.15.14.tgz create mode 100644 vendor/github.com/cilium/charts/cilium-1.15.15.tgz create mode 100644 vendor/github.com/cilium/charts/cilium-1.16.7.tgz create mode 100644 vendor/github.com/cilium/charts/cilium-1.16.8.tgz create mode 100644 vendor/github.com/cilium/charts/cilium-1.17.1.tgz create mode 100644 vendor/github.com/cilium/charts/cilium-1.17.2.tgz create mode 100644 vendor/github.com/cilium/charts/cilium-1.18.0-pre.0.tgz delete mode 100644 vendor/github.com/cilium/cilium/.authors.aux delete mode 100644 vendor/github.com/cilium/cilium/.clang-format delete mode 100644 vendor/github.com/cilium/cilium/.clomonitor.yml delete mode 100644 vendor/github.com/cilium/cilium/.gitattributes delete mode 100644 vendor/github.com/cilium/cilium/.gitignore delete mode 100644 vendor/github.com/cilium/cilium/.golangci.yaml delete mode 100644 vendor/github.com/cilium/cilium/.mailmap delete mode 100644 vendor/github.com/cilium/cilium/CODEOWNERS delete mode 100644 vendor/github.com/cilium/cilium/CODE_OF_CONDUCT.md delete mode 100644 vendor/github.com/cilium/cilium/CONTRIBUTING.md delete mode 100644 vendor/github.com/cilium/cilium/FURTHER_READINGS.rst delete mode 100644 vendor/github.com/cilium/cilium/MAINTAINERS.md delete mode 100644 vendor/github.com/cilium/cilium/Makefile delete mode 100644 vendor/github.com/cilium/cilium/Makefile.defs delete mode 100644 vendor/github.com/cilium/cilium/Makefile.docker delete mode 100644 vendor/github.com/cilium/cilium/Makefile.kind delete mode 100644 vendor/github.com/cilium/cilium/Makefile.quiet delete mode 100644 vendor/github.com/cilium/cilium/README.rst delete mode 100644 vendor/github.com/cilium/cilium/SECURITY-INSIGHTS.yml delete mode 100644 vendor/github.com/cilium/cilium/SECURITY.md delete mode 100644 vendor/github.com/cilium/cilium/USERS.md delete mode 100644 vendor/github.com/cilium/cilium/VERSION delete mode 100644 vendor/github.com/cilium/cilium/assets.go create mode 100644 vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/manifests/client-egress-l7-tls-other-sni.yaml create mode 100644 vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/manifests/deny-ingress-source-egress-other-node.yaml delete mode 100644 vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/outside_to_ingress_service_deny_all_ingress.go delete mode 100644 vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/outside_to_ingress_service_deny_cidr.go delete mode 100644 vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/outside_to_ingress_service_deny_world_identity.go delete mode 100644 vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/pod_to_ingress_service_allow_ingress_identity.go delete mode 100644 vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/pod_to_ingress_service_deny_all.go delete mode 100644 vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/pod_to_ingress_service_deny_backend_service.go delete mode 100644 vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/pod_to_ingress_service_deny_ingress_identity.go create mode 100644 vendor/github.com/cilium/cilium/cilium-cli/utils/codeowners/codeowners.go create mode 100644 vendor/github.com/cilium/cilium/cilium-cli/utils/log/log.go delete mode 100644 vendor/github.com/cilium/cilium/netlify.toml create mode 100644 vendor/github.com/cilium/cilium/pkg/crypto/certificatemanager/certificate_manager_mock.go create mode 100644 vendor/github.com/cilium/cilium/pkg/envoy/policy/envoy_l7_rules_translator.go create mode 100644 vendor/github.com/cilium/cilium/pkg/envoy/policy/sort.go delete mode 100644 vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2/logfields.go delete mode 100644 vendor/github.com/cilium/cilium/pkg/k8s/logfields.go delete mode 100644 vendor/github.com/cilium/cilium/pkg/k8s/synced/logfields.go rename vendor/github.com/cilium/cilium/pkg/metrics/{metrics_unix.go => metrics_linux.go} (62%) rename vendor/github.com/cilium/cilium/pkg/metrics/{metrics_windows.go => metrics_other.go} (87%) delete mode 100644 vendor/github.com/cilium/cilium/stable.txt create mode 100644 vendor/github.com/cilium/ebpf/internal/goos.go create mode 100644 vendor/github.com/cilium/ebpf/internal/unix/errno_linux.go create mode 100644 vendor/github.com/cilium/ebpf/internal/unix/errno_other.go create mode 100644 vendor/github.com/cilium/ebpf/internal/unix/errno_string_windows.go create mode 100644 vendor/github.com/cilium/ebpf/internal/unix/errno_windows.go create mode 100644 vendor/github.com/cilium/ebpf/internal/unix/error.go create mode 100644 vendor/github.com/cilium/ebpf/internal/unix/strings_other.go create mode 100644 vendor/github.com/cilium/ebpf/internal/unix/strings_windows.go create mode 100644 vendor/github.com/cilium/hive/hivetest/doc.go create mode 100644 vendor/github.com/cilium/hive/hivetest/lifecycle.go create mode 100644 vendor/github.com/cilium/hive/hivetest/slog.go create mode 100644 vendor/github.com/docker/docker/internal/lazyregexp/lazyregexp.go delete mode 100644 vendor/github.com/docker/docker/pkg/ioutils/buffer.go delete mode 100644 vendor/github.com/docker/docker/pkg/ioutils/bytespipe.go delete mode 100644 vendor/github.com/docker/docker/pkg/ioutils/fswriters.go delete mode 100644 vendor/github.com/docker/docker/pkg/ioutils/readers.go delete mode 100644 vendor/github.com/docker/docker/pkg/ioutils/writeflusher.go delete mode 100644 vendor/github.com/docker/docker/pkg/ioutils/writers.go create mode 100644 vendor/github.com/go-openapi/swag/errors.go rename vendor/github.com/{sagikazarmark/slog-shim => go-viper/mapstructure/v2}/.editorconfig (86%) create mode 100644 vendor/github.com/go-viper/mapstructure/v2/.gitignore create mode 100644 vendor/github.com/go-viper/mapstructure/v2/.golangci.yaml create mode 100644 vendor/github.com/go-viper/mapstructure/v2/CHANGELOG.md create mode 100644 vendor/github.com/go-viper/mapstructure/v2/LICENSE create mode 100644 vendor/github.com/go-viper/mapstructure/v2/README.md create mode 100644 vendor/github.com/go-viper/mapstructure/v2/decode_hooks.go create mode 100644 vendor/github.com/go-viper/mapstructure/v2/flake.lock rename vendor/github.com/{sagikazarmark/slog-shim => go-viper/mapstructure/v2}/flake.nix (54%) create mode 100644 vendor/github.com/go-viper/mapstructure/v2/internal/errors/errors.go create mode 100644 vendor/github.com/go-viper/mapstructure/v2/internal/errors/join.go create mode 100644 vendor/github.com/go-viper/mapstructure/v2/internal/errors/join_go1_19.go create mode 100644 vendor/github.com/go-viper/mapstructure/v2/mapstructure.go create mode 100644 vendor/github.com/go-viper/mapstructure/v2/reflect_go1_19.go create mode 100644 vendor/github.com/go-viper/mapstructure/v2/reflect_go1_20.go delete mode 100644 vendor/github.com/google/go-github/v68/github/orgs_rules.go delete mode 100644 vendor/github.com/google/go-github/v68/github/packages.go delete mode 100644 vendor/github.com/google/go-github/v68/github/repos_rules.go rename vendor/github.com/google/go-github/{v68 => v70}/AUTHORS (100%) rename vendor/github.com/google/go-github/{v68 => v70}/LICENSE (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/actions.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/actions_artifacts.go (87%) rename vendor/github.com/google/go-github/{v68 => v70}/github/actions_cache.go (100%) create mode 100644 vendor/github.com/google/go-github/v70/github/actions_hosted_runners.go rename vendor/github.com/google/go-github/{v68 => v70}/github/actions_oidc.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/actions_permissions_enterprise.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/actions_permissions_orgs.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/actions_required_workflows.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/actions_runner_groups.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/actions_runners.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/actions_secrets.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/actions_variables.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/actions_workflow_jobs.go (86%) rename vendor/github.com/google/go-github/{v68 => v70}/github/actions_workflow_runs.go (82%) rename vendor/github.com/google/go-github/{v68 => v70}/github/actions_workflows.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/activity.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/activity_events.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/activity_notifications.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/activity_star.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/activity_watching.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/admin.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/admin_orgs.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/admin_stats.go (94%) rename vendor/github.com/google/go-github/{v68 => v70}/github/admin_users.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/apps.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/apps_hooks.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/apps_hooks_deliveries.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/apps_installation.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/apps_manifest.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/apps_marketplace.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/attestations.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/authorizations.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/billing.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/checks.go (99%) rename vendor/github.com/google/go-github/{v68 => v70}/github/code_scanning.go (98%) rename vendor/github.com/google/go-github/{v68 => v70}/github/codesofconduct.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/codespaces.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/codespaces_secrets.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/copilot.go (97%) rename vendor/github.com/google/go-github/{v68 => v70}/github/dependabot.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/dependabot_alerts.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/dependabot_secrets.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/dependency_graph.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/dependency_graph_snapshots.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/doc.go (89%) rename vendor/github.com/google/go-github/{v68 => v70}/github/emojis.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/enterprise.go (100%) create mode 100644 vendor/github.com/google/go-github/v70/github/enterprise_actions_hosted_runners.go rename vendor/github.com/google/go-github/{v68 => v70}/github/enterprise_actions_runner_groups.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/enterprise_actions_runners.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/enterprise_audit_log.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/enterprise_code_security_and_analysis.go (100%) create mode 100644 vendor/github.com/google/go-github/v70/github/enterprise_manage_ghes.go create mode 100644 vendor/github.com/google/go-github/v70/github/enterprise_manage_ghes_config.go create mode 100644 vendor/github.com/google/go-github/v70/github/enterprise_manage_ghes_maintenance.go create mode 100644 vendor/github.com/google/go-github/v70/github/enterprise_manage_ghes_ssh.go create mode 100644 vendor/github.com/google/go-github/v70/github/enterprise_network_configurations.go rename vendor/github.com/google/go-github/{v68 => v70}/github/enterprise_properties.go (100%) create mode 100644 vendor/github.com/google/go-github/v70/github/enterprise_rules.go rename vendor/github.com/google/go-github/{v68 => v70}/github/event.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/event_types.go (96%) rename vendor/github.com/google/go-github/{v68 => v70}/github/gists.go (99%) rename vendor/github.com/google/go-github/{v68 => v70}/github/gists_comments.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/git.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/git_blobs.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/git_commits.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/git_refs.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/git_tags.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/git_trees.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/github-accessors.go (90%) rename vendor/github.com/google/go-github/{v68 => v70}/github/github.go (86%) rename vendor/github.com/google/go-github/{v68 => v70}/github/gitignore.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/interactions.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/interactions_orgs.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/interactions_repos.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/issue_import.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/issues.go (96%) rename vendor/github.com/google/go-github/{v68 => v70}/github/issues_assignees.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/issues_comments.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/issues_events.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/issues_labels.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/issues_milestones.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/issues_timeline.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/licenses.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/markdown.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/messages.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/meta.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/migrations.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/migrations_source_import.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/migrations_user.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/orgs.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/orgs_actions_allowed.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/orgs_actions_permissions.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/orgs_attestations.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/orgs_audit_log.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/orgs_codesecurity_configurations.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/orgs_credential_authorizations.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/orgs_custom_repository_roles.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/orgs_hooks.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/orgs_hooks_configuration.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/orgs_hooks_deliveries.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/orgs_members.go (100%) create mode 100644 vendor/github.com/google/go-github/v70/github/orgs_network_configurations.go rename vendor/github.com/google/go-github/{v68 => v70}/github/orgs_organization_roles.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/orgs_outside_collaborators.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/orgs_packages.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/orgs_personal_access_tokens.go (98%) rename vendor/github.com/google/go-github/{v68 => v70}/github/orgs_properties.go (100%) create mode 100644 vendor/github.com/google/go-github/v70/github/orgs_rules.go rename vendor/github.com/google/go-github/{v68 => v70}/github/orgs_security_managers.go (88%) rename vendor/github.com/google/go-github/{v68 => v70}/github/orgs_users_blocking.go (100%) create mode 100644 vendor/github.com/google/go-github/v70/github/packages.go rename vendor/github.com/google/go-github/{v68 => v70}/github/pulls.go (83%) rename vendor/github.com/google/go-github/{v68 => v70}/github/pulls_comments.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/pulls_reviewers.go (95%) rename vendor/github.com/google/go-github/{v68 => v70}/github/pulls_reviews.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/pulls_threads.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/rate_limit.go (85%) rename vendor/github.com/google/go-github/{v68 => v70}/github/reactions.go (99%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos.go (99%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_actions_access.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_actions_allowed.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_actions_permissions.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_attestations.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_autolinks.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_codeowners.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_collaborators.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_comments.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_commits.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_community_health.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_contents.go (92%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_deployment_branch_policies.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_deployment_protection_rules.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_deployments.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_environments.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_forks.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_hooks.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_hooks_configuration.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_hooks_deliveries.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_invitations.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_keys.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_lfs.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_merging.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_pages.go (91%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_prereceive_hooks.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_properties.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_releases.go (100%) create mode 100644 vendor/github.com/google/go-github/v70/github/repos_rules.go rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_stats.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_statuses.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_tags.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/repos_traffic.go (100%) create mode 100644 vendor/github.com/google/go-github/v70/github/rules.go rename vendor/github.com/google/go-github/{v68 => v70}/github/scim.go (78%) rename vendor/github.com/google/go-github/{v68 => v70}/github/search.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/secret_scanning.go (95%) rename vendor/github.com/google/go-github/{v68 => v70}/github/security_advisories.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/strings.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/teams.go (99%) rename vendor/github.com/google/go-github/{v68 => v70}/github/teams_discussion_comments.go (99%) rename vendor/github.com/google/go-github/{v68 => v70}/github/teams_discussions.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/teams_members.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/timestamp.go (94%) rename vendor/github.com/google/go-github/{v68 => v70}/github/users.go (99%) rename vendor/github.com/google/go-github/{v68 => v70}/github/users_administration.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/users_attestations.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/users_blocking.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/users_emails.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/users_followers.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/users_gpg_keys.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/users_keys.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/users_packages.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/users_ssh_signing_keys.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/with_appengine.go (100%) rename vendor/github.com/google/go-github/{v68 => v70}/github/without_appengine.go (100%) delete mode 100644 vendor/github.com/hashicorp/hcl/.gitignore delete mode 100644 vendor/github.com/hashicorp/hcl/.travis.yml delete mode 100644 vendor/github.com/hashicorp/hcl/LICENSE delete mode 100644 vendor/github.com/hashicorp/hcl/Makefile delete mode 100644 vendor/github.com/hashicorp/hcl/README.md delete mode 100644 vendor/github.com/hashicorp/hcl/appveyor.yml delete mode 100644 vendor/github.com/hashicorp/hcl/decoder.go delete mode 100644 vendor/github.com/hashicorp/hcl/hcl.go delete mode 100644 vendor/github.com/hashicorp/hcl/hcl/ast/ast.go delete mode 100644 vendor/github.com/hashicorp/hcl/hcl/ast/walk.go delete mode 100644 vendor/github.com/hashicorp/hcl/hcl/parser/error.go delete mode 100644 vendor/github.com/hashicorp/hcl/hcl/parser/parser.go delete mode 100644 vendor/github.com/hashicorp/hcl/hcl/printer/nodes.go delete mode 100644 vendor/github.com/hashicorp/hcl/hcl/printer/printer.go delete mode 100644 vendor/github.com/hashicorp/hcl/hcl/scanner/scanner.go delete mode 100644 vendor/github.com/hashicorp/hcl/hcl/strconv/quote.go delete mode 100644 vendor/github.com/hashicorp/hcl/hcl/token/position.go delete mode 100644 vendor/github.com/hashicorp/hcl/hcl/token/token.go delete mode 100644 vendor/github.com/hashicorp/hcl/json/parser/flatten.go delete mode 100644 vendor/github.com/hashicorp/hcl/json/parser/parser.go delete mode 100644 vendor/github.com/hashicorp/hcl/json/scanner/scanner.go delete mode 100644 vendor/github.com/hashicorp/hcl/json/token/position.go delete mode 100644 vendor/github.com/hashicorp/hcl/json/token/token.go delete mode 100644 vendor/github.com/hashicorp/hcl/lex.go delete mode 100644 vendor/github.com/hashicorp/hcl/parse.go delete mode 100644 vendor/github.com/magiconair/properties/.gitignore delete mode 100644 vendor/github.com/magiconair/properties/CHANGELOG.md delete mode 100644 vendor/github.com/magiconair/properties/LICENSE.md delete mode 100644 vendor/github.com/magiconair/properties/README.md delete mode 100644 vendor/github.com/magiconair/properties/decode.go delete mode 100644 vendor/github.com/magiconair/properties/doc.go delete mode 100644 vendor/github.com/magiconair/properties/integrate.go delete mode 100644 vendor/github.com/magiconair/properties/lex.go delete mode 100644 vendor/github.com/magiconair/properties/load.go delete mode 100644 vendor/github.com/magiconair/properties/parser.go delete mode 100644 vendor/github.com/magiconair/properties/properties.go delete mode 100644 vendor/github.com/magiconair/properties/rangecheck.go create mode 100644 vendor/github.com/prometheus/client_golang/prometheus/process_collector_darwin.go create mode 100644 vendor/github.com/prometheus/client_golang/prometheus/process_collector_mem_cgo_darwin.c create mode 100644 vendor/github.com/prometheus/client_golang/prometheus/process_collector_mem_cgo_darwin.go create mode 100644 vendor/github.com/prometheus/client_golang/prometheus/process_collector_mem_nocgo_darwin.go rename vendor/github.com/prometheus/client_golang/prometheus/{process_collector_js.go => process_collector_not_supported.go} (56%) rename vendor/github.com/prometheus/client_golang/prometheus/{process_collector_other.go => process_collector_procfsenabled.go} (77%) delete mode 100644 vendor/github.com/prometheus/client_golang/prometheus/process_collector_wasip1.go create mode 100644 vendor/github.com/prometheus/procfs/net_dev_snmp6.go create mode 100644 vendor/github.com/sagikazarmark/locafero/glob.go create mode 100644 vendor/github.com/sagikazarmark/locafero/glob_windows.go delete mode 100644 vendor/github.com/sagikazarmark/slog-shim/.gitignore delete mode 100644 vendor/github.com/sagikazarmark/slog-shim/LICENSE delete mode 100644 vendor/github.com/sagikazarmark/slog-shim/README.md delete mode 100644 vendor/github.com/sagikazarmark/slog-shim/attr.go delete mode 100644 vendor/github.com/sagikazarmark/slog-shim/attr_120.go delete mode 100644 vendor/github.com/sagikazarmark/slog-shim/flake.lock delete mode 100644 vendor/github.com/sagikazarmark/slog-shim/handler.go delete mode 100644 vendor/github.com/sagikazarmark/slog-shim/handler_120.go delete mode 100644 vendor/github.com/sagikazarmark/slog-shim/json_handler.go delete mode 100644 vendor/github.com/sagikazarmark/slog-shim/json_handler_120.go delete mode 100644 vendor/github.com/sagikazarmark/slog-shim/level.go delete mode 100644 vendor/github.com/sagikazarmark/slog-shim/level_120.go delete mode 100644 vendor/github.com/sagikazarmark/slog-shim/logger.go delete mode 100644 vendor/github.com/sagikazarmark/slog-shim/logger_120.go delete mode 100644 vendor/github.com/sagikazarmark/slog-shim/record.go delete mode 100644 vendor/github.com/sagikazarmark/slog-shim/record_120.go delete mode 100644 vendor/github.com/sagikazarmark/slog-shim/text_handler.go delete mode 100644 vendor/github.com/sagikazarmark/slog-shim/text_handler_120.go delete mode 100644 vendor/github.com/sagikazarmark/slog-shim/value.go delete mode 100644 vendor/github.com/sagikazarmark/slog-shim/value_120.go create mode 100644 vendor/github.com/spf13/viper/UPDATES.md create mode 100644 vendor/github.com/spf13/viper/encoding.go create mode 100644 vendor/github.com/spf13/viper/experimental.go delete mode 100644 vendor/github.com/spf13/viper/file_finder.go create mode 100644 vendor/github.com/spf13/viper/finder.go delete mode 100644 vendor/github.com/spf13/viper/internal/encoding/decoder.go delete mode 100644 vendor/github.com/spf13/viper/internal/encoding/encoder.go delete mode 100644 vendor/github.com/spf13/viper/internal/encoding/error.go delete mode 100644 vendor/github.com/spf13/viper/internal/encoding/hcl/codec.go delete mode 100644 vendor/github.com/spf13/viper/internal/encoding/ini/codec.go delete mode 100644 vendor/github.com/spf13/viper/internal/encoding/ini/map_utils.go delete mode 100644 vendor/github.com/spf13/viper/internal/encoding/javaproperties/codec.go delete mode 100644 vendor/github.com/spf13/viper/internal/encoding/javaproperties/map_utils.go create mode 100644 vendor/github.com/spf13/viper/internal/features/finder.go create mode 100644 vendor/github.com/spf13/viper/internal/features/finder_default.go create mode 100644 vendor/github.com/spf13/viper/remote.go create mode 100644 vendor/go.opentelemetry.io/otel/dependencies.Dockerfile create mode 100644 vendor/go.opentelemetry.io/otel/trace/auto.go create mode 100644 vendor/go.opentelemetry.io/otel/trace/internal/telemetry/attr.go create mode 100644 vendor/go.opentelemetry.io/otel/trace/internal/telemetry/doc.go create mode 100644 vendor/go.opentelemetry.io/otel/trace/internal/telemetry/id.go create mode 100644 vendor/go.opentelemetry.io/otel/trace/internal/telemetry/number.go create mode 100644 vendor/go.opentelemetry.io/otel/trace/internal/telemetry/resource.go create mode 100644 vendor/go.opentelemetry.io/otel/trace/internal/telemetry/scope.go create mode 100644 vendor/go.opentelemetry.io/otel/trace/internal/telemetry/span.go create mode 100644 vendor/go.opentelemetry.io/otel/trace/internal/telemetry/status.go create mode 100644 vendor/go.opentelemetry.io/otel/trace/internal/telemetry/traces.go create mode 100644 vendor/go.opentelemetry.io/otel/trace/internal/telemetry/value.go delete mode 100644 vendor/golang.org/x/exp/LICENSE delete mode 100644 vendor/golang.org/x/exp/PATENTS delete mode 100644 vendor/golang.org/x/exp/constraints/constraints.go delete mode 100644 vendor/golang.org/x/exp/slices/cmp.go delete mode 100644 vendor/golang.org/x/exp/slices/slices.go delete mode 100644 vendor/golang.org/x/exp/slices/sort.go delete mode 100644 vendor/golang.org/x/exp/slices/zsortanyfunc.go delete mode 100644 vendor/golang.org/x/exp/slices/zsortordered.go delete mode 100644 vendor/golang.org/x/exp/slog/attr.go delete mode 100644 vendor/golang.org/x/exp/slog/doc.go delete mode 100644 vendor/golang.org/x/exp/slog/handler.go delete mode 100644 vendor/golang.org/x/exp/slog/internal/buffer/buffer.go delete mode 100644 vendor/golang.org/x/exp/slog/internal/ignorepc.go delete mode 100644 vendor/golang.org/x/exp/slog/json_handler.go delete mode 100644 vendor/golang.org/x/exp/slog/level.go delete mode 100644 vendor/golang.org/x/exp/slog/logger.go delete mode 100644 vendor/golang.org/x/exp/slog/noplog.bench delete mode 100644 vendor/golang.org/x/exp/slog/record.go delete mode 100644 vendor/golang.org/x/exp/slog/text_handler.go delete mode 100644 vendor/golang.org/x/exp/slog/value.go delete mode 100644 vendor/golang.org/x/exp/slog/value_119.go delete mode 100644 vendor/golang.org/x/exp/slog/value_120.go delete mode 100644 vendor/golang.org/x/sync/errgroup/go120.go delete mode 100644 vendor/golang.org/x/sync/errgroup/pre_go120.go create mode 100644 vendor/google.golang.org/grpc/balancer/endpointsharding/endpointsharding.go delete mode 100644 vendor/google.golang.org/grpc/internal/grpcsync/oncefunc.go create mode 100644 vendor/google.golang.org/grpc/internal/proxyattributes/proxyattributes.go create mode 100644 vendor/google.golang.org/grpc/internal/resolver/delegatingresolver/delegatingresolver.go delete mode 100644 vendor/gopkg.in/ini.v1/.editorconfig delete mode 100644 vendor/gopkg.in/ini.v1/.gitignore delete mode 100644 vendor/gopkg.in/ini.v1/.golangci.yml delete mode 100644 vendor/gopkg.in/ini.v1/LICENSE delete mode 100644 vendor/gopkg.in/ini.v1/Makefile delete mode 100644 vendor/gopkg.in/ini.v1/README.md delete mode 100644 vendor/gopkg.in/ini.v1/codecov.yml delete mode 100644 vendor/gopkg.in/ini.v1/data_source.go delete mode 100644 vendor/gopkg.in/ini.v1/deprecated.go delete mode 100644 vendor/gopkg.in/ini.v1/error.go delete mode 100644 vendor/gopkg.in/ini.v1/file.go delete mode 100644 vendor/gopkg.in/ini.v1/helper.go delete mode 100644 vendor/gopkg.in/ini.v1/ini.go delete mode 100644 vendor/gopkg.in/ini.v1/key.go delete mode 100644 vendor/gopkg.in/ini.v1/parser.go delete mode 100644 vendor/gopkg.in/ini.v1/section.go delete mode 100644 vendor/gopkg.in/ini.v1/struct.go diff --git a/go.mod b/go.mod index cac74467ec..8849922865 100644 --- a/go.mod +++ b/go.mod @@ -9,7 +9,7 @@ go 1.24.0 // as to why we are using a private fork. replace sigs.k8s.io/controller-tools => github.com/cilium/controller-tools v0.16.5-1 -require github.com/cilium/cilium v1.18.0-pre.0.0.20250307211928-06901cc99eca +require github.com/cilium/cilium v1.18.0-pre.0.0.20250321220641-9d59c1208ae1 require ( cel.dev/expr v0.19.1 // indirect @@ -28,16 +28,16 @@ require ( github.com/census-instrumentation/opencensus-proto v0.4.1 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect - github.com/cilium/charts v0.0.0-20250204154402-8a35f8210901 // indirect - github.com/cilium/ebpf v0.17.1 // indirect - github.com/cilium/hive v0.0.0-20250217113459-914947d44393 // indirect - github.com/cilium/proxy v0.0.0-20250214115704-3e4b99dc5d1f // indirect - github.com/cilium/statedb v0.3.6 // indirect + github.com/cilium/charts v0.0.0-20250315135936-15c20a953df6 // indirect + github.com/cilium/ebpf v0.17.3 // indirect + github.com/cilium/hive v0.0.0-20250311080423-b13b66490d76 // indirect + github.com/cilium/proxy v0.0.0-20250318065604-173988fc0adb // indirect + github.com/cilium/statedb v0.3.7 // indirect github.com/cilium/stream v0.0.0-20241203114243-53c3e5d79744 // indirect - github.com/cilium/workerpool v1.2.0 // indirect + github.com/cilium/workerpool v1.3.0 // indirect github.com/cloudflare/cfssl v1.6.5 // indirect github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 // indirect - github.com/containerd/containerd v1.7.24 // indirect + github.com/containerd/containerd v1.7.27 // indirect github.com/containerd/errdefs v0.3.0 // indirect github.com/containerd/log v0.1.0 // indirect github.com/containerd/platforms v0.2.1 // indirect @@ -48,7 +48,7 @@ require ( github.com/distribution/reference v0.6.0 // indirect github.com/docker/cli v25.0.1+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect - github.com/docker/docker v27.5.1+incompatible // indirect + github.com/docker/docker v28.0.1+incompatible // indirect github.com/docker/docker-credential-helpers v0.7.0 // indirect github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-metrics v0.0.1 // indirect @@ -65,15 +65,16 @@ require ( github.com/go-logr/logr v1.4.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/analysis v0.23.0 // indirect - github.com/go-openapi/errors v0.22.0 // indirect + github.com/go-openapi/errors v0.22.1 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect github.com/go-openapi/jsonreference v0.21.0 // indirect github.com/go-openapi/loads v0.22.0 // indirect github.com/go-openapi/runtime v0.28.0 // indirect github.com/go-openapi/spec v0.21.0 // indirect github.com/go-openapi/strfmt v0.23.0 // indirect - github.com/go-openapi/swag v0.23.0 // indirect + github.com/go-openapi/swag v0.23.1 // indirect github.com/go-openapi/validate v0.24.0 // indirect + github.com/go-viper/mapstructure/v2 v2.2.1 // indirect github.com/gobwas/glob v0.2.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect @@ -81,8 +82,8 @@ require ( github.com/google/btree v1.1.3 // indirect github.com/google/certificate-transparency-go v1.1.7 // indirect github.com/google/gnostic-models v0.6.8 // indirect - github.com/google/go-cmp v0.6.0 // indirect - github.com/google/go-github/v68 v68.0.0 // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/google/go-github/v70 v70.0.0 // indirect github.com/google/go-querystring v1.1.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/gops v0.3.28 // indirect @@ -96,21 +97,19 @@ require ( github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-hclog v1.6.3 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect - github.com/hashicorp/hcl v1.0.1-vault-5 // indirect github.com/hmarr/codeowners v1.2.1 // indirect github.com/huandu/xstrings v1.5.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmoiron/sqlx v1.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.9 // indirect + github.com/klauspost/compress v1.17.11 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/lib/pq v1.10.9 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/mackerelio/go-osstat v0.2.5 // indirect - github.com/magiconair/properties v1.8.7 // indirect - github.com/mailru/easyjson v0.7.7 // indirect + github.com/mailru/easyjson v0.9.0 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.13 // indirect @@ -130,31 +129,30 @@ require ( github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect - github.com/osrg/gobgp/v3 v3.34.0 // indirect + github.com/osrg/gobgp/v3 v3.35.0 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_golang v1.20.5 // indirect + github.com/prometheus/client_golang v1.21.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.62.0 // indirect - github.com/prometheus/procfs v0.15.1 // indirect + github.com/prometheus/common v0.63.0 // indirect + github.com/prometheus/procfs v0.16.0 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/rubenv/sql-migrate v1.7.1 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect - github.com/sagikazarmark/locafero v0.4.0 // indirect - github.com/sagikazarmark/slog-shim v0.1.0 // indirect + github.com/sagikazarmark/locafero v0.7.0 // indirect github.com/sasha-s/go-deadlock v0.3.5 // indirect github.com/shopspring/decimal v1.4.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect github.com/sourcegraph/conc v0.3.0 // indirect - github.com/spf13/afero v1.12.0 // indirect + github.com/spf13/afero v1.14.0 // indirect github.com/spf13/cast v1.7.1 // indirect - github.com/spf13/cobra v1.8.1 // indirect + github.com/spf13/cobra v1.9.1 // indirect github.com/spf13/pflag v1.0.6 // indirect - github.com/spf13/viper v1.19.0 // indirect + github.com/spf13/viper v1.20.0 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/vishvananda/netlink v1.3.1-0.20250303224720-0e7078ed04c8 // indirect github.com/vishvananda/netns v0.0.5 // indirect @@ -166,57 +164,55 @@ require ( github.com/xlab/treeprint v1.2.0 // indirect github.com/zmap/zcrypto v0.0.0-20230310154051-c8b263fd8300 // indirect github.com/zmap/zlint/v3 v3.5.0 // indirect - go.etcd.io/etcd/api/v3 v3.5.18 // indirect - go.etcd.io/etcd/client/pkg/v3 v3.5.18 // indirect - go.etcd.io/etcd/client/v3 v3.5.18 // indirect + go.etcd.io/etcd/api/v3 v3.5.19 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.5.19 // indirect + go.etcd.io/etcd/client/v3 v3.5.19 // indirect go.mongodb.org/mongo-driver v1.14.0 // indirect go.opentelemetry.io/auto/sdk v1.1.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect - go.opentelemetry.io/otel v1.34.0 // indirect - go.opentelemetry.io/otel/metric v1.34.0 // indirect - go.opentelemetry.io/otel/trace v1.34.0 // indirect + go.opentelemetry.io/otel v1.35.0 // indirect + go.opentelemetry.io/otel/metric v1.35.0 // indirect + go.opentelemetry.io/otel/trace v1.35.0 // indirect go.opentelemetry.io/proto/otlp v1.5.0 // indirect go.uber.org/dig v1.17.1 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.27.0 // indirect go4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect - golang.org/x/crypto v0.33.0 // indirect - golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa // indirect - golang.org/x/net v0.35.0 // indirect - golang.org/x/oauth2 v0.26.0 // indirect - golang.org/x/sync v0.11.0 // indirect - golang.org/x/sys v0.30.0 // indirect - golang.org/x/term v0.29.0 // indirect - golang.org/x/text v0.22.0 // indirect - golang.org/x/time v0.10.0 // indirect - golang.org/x/tools v0.30.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250207221924-e9438ea467c6 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 // indirect - google.golang.org/grpc v1.70.0 // indirect + golang.org/x/crypto v0.36.0 // indirect + golang.org/x/net v0.37.0 // indirect + golang.org/x/oauth2 v0.28.0 // indirect + golang.org/x/sync v0.12.0 // indirect + golang.org/x/sys v0.31.0 // indirect + golang.org/x/term v0.30.0 // indirect + golang.org/x/text v0.23.0 // indirect + golang.org/x/time v0.11.0 // indirect + golang.org/x/tools v0.31.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect + google.golang.org/grpc v1.71.0 // indirect google.golang.org/protobuf v1.36.5 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - helm.sh/helm/v3 v3.17.0 // indirect - k8s.io/api v0.32.2 // indirect - k8s.io/apiextensions-apiserver v0.32.2 // indirect - k8s.io/apimachinery v0.32.2 // indirect - k8s.io/apiserver v0.32.2 // indirect - k8s.io/cli-runtime v0.32.2 // indirect - k8s.io/client-go v0.32.2 // indirect - k8s.io/component-base v0.32.2 // indirect + helm.sh/helm/v3 v3.17.2 // indirect + k8s.io/api v0.32.3 // indirect + k8s.io/apiextensions-apiserver v0.32.3 // indirect + k8s.io/apimachinery v0.32.3 // indirect + k8s.io/apiserver v0.32.3 // indirect + k8s.io/cli-runtime v0.32.3 // indirect + k8s.io/client-go v0.32.3 // indirect + k8s.io/component-base v0.32.3 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect - k8s.io/kubectl v0.32.2 // indirect + k8s.io/kubectl v0.32.3 // indirect k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect oras.land/oras-go v1.2.5 // indirect - sigs.k8s.io/controller-runtime v0.20.1 // indirect + sigs.k8s.io/controller-runtime v0.20.3 // indirect sigs.k8s.io/gateway-api v1.2.1 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect sigs.k8s.io/kustomize/api v0.18.0 // indirect sigs.k8s.io/kustomize/kyaml v0.18.1 // indirect - sigs.k8s.io/mcs-api v0.1.1-0.20250129110323-a7986579439f // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect + sigs.k8s.io/mcs-api v0.1.1-0.20250224121229-6c631f4730d0 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index fe8b592b55..b9330ca97f 100644 --- a/go.sum +++ b/go.sum @@ -54,32 +54,32 @@ github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UF github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v1.0.2 h1:1Lwwip6Q2QGsAdl/ZKPCwTe9fe0CjlUbqj5bFNSjIRk= github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= -github.com/cilium/charts v0.0.0-20250204154402-8a35f8210901 h1:5RH3uJeR2kGU2uOdEHh/dzFgFMaSi5wKQbGolcYUFBc= -github.com/cilium/charts v0.0.0-20250204154402-8a35f8210901/go.mod h1:M3C9VOlFvRzuV+a01t07Tw4uFLSfkCH3L542IWjf6BU= -github.com/cilium/cilium v1.18.0-pre.0.0.20250307211928-06901cc99eca h1:H5/i4MDt3w06UMSx1K7liKa5PUKmn3Gx38xSyEXbrBE= -github.com/cilium/cilium v1.18.0-pre.0.0.20250307211928-06901cc99eca/go.mod h1:zNicgMLoL/BiK/qKBkounEJbF2pGEdsi4naIz+V56QE= -github.com/cilium/ebpf v0.17.1 h1:G8mzU81R2JA1nE5/8SRubzqvBMmAmri2VL8BIZPWvV0= -github.com/cilium/ebpf v0.17.1/go.mod h1:vay2FaYSmIlv3r8dNACd4mW/OCaZLJKJOo+IHBvCIO8= -github.com/cilium/hive v0.0.0-20250217113459-914947d44393 h1:x2VYGSK1hnX6N7j2V6rtIDN0E+dO6ozTyYz8iYOugD8= -github.com/cilium/hive v0.0.0-20250217113459-914947d44393/go.mod h1:pI2GJ1n3SLKIQVFrKF7W6A6gb6BQkZ+3Hp4PAEo5SuI= -github.com/cilium/proxy v0.0.0-20250214115704-3e4b99dc5d1f h1:e+c0sFbzPfKjDtsG06uZah+aqpDycdpGF/StqtaUg7Y= -github.com/cilium/proxy v0.0.0-20250214115704-3e4b99dc5d1f/go.mod h1:WcTUEfsCIVY9uvjRLUvl0G+G7RiK5BfOVdg/LknXMpk= -github.com/cilium/statedb v0.3.6 h1:dGwzZTJgVWlnG7io0Wl0XsI7ULsz2TbNqH8Ag+dP6is= -github.com/cilium/statedb v0.3.6/go.mod h1:n2lNVxi8vz5Up1Y1rRD++aQP2izQA932fUwTkedKSV0= +github.com/cilium/charts v0.0.0-20250315135936-15c20a953df6 h1:Fam72veX66q5u+ECGM+aKsw6ICxW4VH9yx97tb/90Ac= +github.com/cilium/charts v0.0.0-20250315135936-15c20a953df6/go.mod h1:M3C9VOlFvRzuV+a01t07Tw4uFLSfkCH3L542IWjf6BU= +github.com/cilium/cilium v1.18.0-pre.0.0.20250321220641-9d59c1208ae1 h1:xHxxZrWvhmfV6cDrkX+ZJ7MxTR/wrNg5iGSvpB6E7K4= +github.com/cilium/cilium v1.18.0-pre.0.0.20250321220641-9d59c1208ae1/go.mod h1:TRn7XKcUe1YA8TOTcHRDX8hq7oOIFPAkyCbpYuLfHfQ= +github.com/cilium/ebpf v0.17.3 h1:FnP4r16PWYSE4ux6zN+//jMcW4nMVRvuTLVTvCjyyjg= +github.com/cilium/ebpf v0.17.3/go.mod h1:G5EDHij8yiLzaqn0WjyfJHvRa+3aDlReIaLVRMvOyJk= +github.com/cilium/hive v0.0.0-20250311080423-b13b66490d76 h1:IZRB3yicRJumH06g9kkGwpnbZkv0+U+zELqEw8pIQys= +github.com/cilium/hive v0.0.0-20250311080423-b13b66490d76/go.mod h1:pI2GJ1n3SLKIQVFrKF7W6A6gb6BQkZ+3Hp4PAEo5SuI= +github.com/cilium/proxy v0.0.0-20250318065604-173988fc0adb h1:pV58137rRui6vUJT2HM2h0HcPfaAh+6BVH42oKXytH8= +github.com/cilium/proxy v0.0.0-20250318065604-173988fc0adb/go.mod h1:ZCm83Mm0aE17bm2vnYY3kS40VIT7ymoqZhNc8YXK1us= +github.com/cilium/statedb v0.3.7 h1:htzjXktKe37FNLGDQjM899G8BK4Pcp+GM1cFQVV3HGA= +github.com/cilium/statedb v0.3.7/go.mod h1:n2lNVxi8vz5Up1Y1rRD++aQP2izQA932fUwTkedKSV0= github.com/cilium/stream v0.0.0-20241203114243-53c3e5d79744 h1:f+CgYUy2YyZ2EX31QSqf3vwFiJJQSAMIQLn4d3QQYno= github.com/cilium/stream v0.0.0-20241203114243-53c3e5d79744/go.mod h1:/e83AwqvNKpyg4n3C41qmnmj1x2G9DwzI+jb7GkF4lI= -github.com/cilium/workerpool v1.2.0 h1:Wc2iOPTvCgWKQXeq4L5tnx4QFEI+z5q1+bSpSS0cnAY= -github.com/cilium/workerpool v1.2.0/go.mod h1:GOYJhwlnIjR+jWSDNBb5kw47G1H/XA9X4WOBpgr4pQU= +github.com/cilium/workerpool v1.3.0 h1:7BhHxoqNtpqtmce6MxZdgWODze4lYHbWkEUQ+3xEu8M= +github.com/cilium/workerpool v1.3.0/go.mod h1:0evs6P39nORTphjRtTtHLXTyCPQUwelXCK4wBJmVP7g= github.com/cloudflare/cfssl v1.6.5 h1:46zpNkm6dlNkMZH/wMW22ejih6gIaJbzL2du6vD7ZeI= github.com/cloudflare/cfssl v1.6.5/go.mod h1:Bk1si7sq8h2+yVEDrFJiz3d7Aw+pfjjJSZVaD+Taky4= github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42 h1:Om6kYQYDUk5wWbT0t0q6pvyM49i9XZAv9dDrkDA7gjk= github.com/cncf/xds/go v0.0.0-20250121191232-2f005788dc42/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= -github.com/containerd/containerd v1.7.24 h1:zxszGrGjrra1yYJW/6rhm9cJ1ZQ8rkKBR48brqsa7nA= -github.com/containerd/containerd v1.7.24/go.mod h1:7QUzfURqZWCZV7RLNEn1XjUCQLEf0bkaK4GjUaZehxw= -github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= -github.com/containerd/continuity v0.4.2/go.mod h1:F6PTNCKepoxEaXLQp3wDAjygEnImnZ/7o4JzpodfroQ= +github.com/containerd/containerd v1.7.27 h1:yFyEyojddO3MIGVER2xJLWoCIn+Up4GaHFquP7hsFII= +github.com/containerd/containerd v1.7.27/go.mod h1:xZmPnl75Vc+BLGt4MIfu6bp+fy03gdHAn9bz+FreFR0= +github.com/containerd/continuity v0.4.4 h1:/fNVfTJ7wIl/YPMHjf+5H32uFhl63JucB34PlCpMKII= +github.com/containerd/continuity v0.4.4/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE= github.com/containerd/errdefs v0.3.0 h1:FSZgGOeK4yuT/+DnF07/Olde/q4KBoMsaamhXxIMDp4= github.com/containerd/errdefs v0.3.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= @@ -90,7 +90,7 @@ github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/cyphar/filepath-securejoin v0.3.6 h1:4d9N5ykBnSp5Xn2JkhocYDkOpURL/18CYMpo6xB9uWM= @@ -107,8 +107,8 @@ github.com/docker/cli v25.0.1+incompatible h1:mFpqnrS6Hsm3v1k7Wa/BO23oz0k121MTbT github.com/docker/cli v25.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v27.5.1+incompatible h1:4PYU5dnBYqRQi0294d1FBECqT9ECWeQAIfE8q4YnPY8= -github.com/docker/docker v27.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v28.0.1+incompatible h1:FCHjSRdXhNRFjlHMTv4jUNlIBbTeRjrWfeFuJp7jpo0= +github.com/docker/docker v28.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= @@ -125,8 +125,8 @@ github.com/envoyproxy/protoc-gen-validate v1.2.1 h1:DEo3O99U8j4hBFwbJfrz9VtgcDfU github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2TmLfsJ31lvEjwamM4DxlWXU= github.com/evanphx/json-patch v5.9.11+incompatible h1:ixHHqfcGvxhWkniF1tWxBHA0yb4Z+d1UQi45df52xW8= github.com/evanphx/json-patch v5.9.11+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= -github.com/evanphx/json-patch/v5 v5.9.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= +github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2Wvf/TIe2xdyJxTlb6obmF18d8QdkxNDu4= github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= @@ -158,8 +158,8 @@ github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= github.com/go-openapi/analysis v0.23.0 h1:aGday7OWupfMs+LbmLZG4k0MYXIANxcuBTYUC03zFCU= github.com/go-openapi/analysis v0.23.0/go.mod h1:9mz9ZWaSlV8TvjQHLl2mUW2PbZtemkE8yA5v22ohupo= -github.com/go-openapi/errors v0.22.0 h1:c4xY/OLxUBSTiepAg3j/MHuAv5mJhnf53LLMWFB+u/w= -github.com/go-openapi/errors v0.22.0/go.mod h1:J3DmZScxCDufmIMsdOuDHxJbdOGC0xtUynjIx092vXE= +github.com/go-openapi/errors v0.22.1 h1:kslMRRnK7NCb/CvR1q1VWuEQCEIsBGn5GgKD9e+HYhU= +github.com/go-openapi/errors v0.22.1/go.mod h1:+n/5UdIqdVnLIJ6Q9Se8HNGUXYaY6CN8ImWzfi/Gzp0= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= @@ -172,8 +172,8 @@ github.com/go-openapi/spec v0.21.0 h1:LTVzPc3p/RzRnkQqLRndbAzjY0d0BCL72A6j3CdL9Z github.com/go-openapi/spec v0.21.0/go.mod h1:78u6VdPw81XU44qEWGhtr982gJ5BWg2c0I5XwVMotYk= github.com/go-openapi/strfmt v0.23.0 h1:nlUS6BCqcnAk0pyhi9Y+kdDVZdZMHfEKQiS4HaMgO/c= github.com/go-openapi/strfmt v0.23.0/go.mod h1:NrtIpfKtWIygRkKVsxh7XQMDQW5HKQl6S5ik2elW+K4= -github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= -github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= +github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= github.com/go-openapi/validate v0.24.0 h1:LdfDKwNbpB6Vn40xhTdNZAnfLECL81w+VX3BumrGD58= github.com/go-openapi/validate v0.24.0/go.mod h1:iyeX1sEufmv3nPbBdX3ieNviWnOZaJ1+zquzJEf2BAQ= github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI= @@ -185,6 +185,8 @@ github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1v github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg= github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE= +github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss= +github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -209,11 +211,11 @@ github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYu github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= -github.com/google/go-github/v68 v68.0.0 h1:ZW57zeNZiXTdQ16qrDiZ0k6XucrxZ2CGmoTvcCyQG6s= -github.com/google/go-github/v68 v68.0.0/go.mod h1:K9HAUBovM2sLwM408A18h+wd9vqdLOEqTUCbnRIcx68= +github.com/google/go-github/v70 v70.0.0 h1:/tqCp5KPrcvqCc7vIvYyFYTiCGrYvaWoYMGHSQbo55o= +github.com/google/go-github/v70 v70.0.0/go.mod h1:xBUZgo8MI3lUL/hwxl3hlceJW1U8MVnXP3zUyI+rhQY= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= @@ -251,8 +253,6 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/hcl v1.0.1-vault-5 h1:kI3hhbbyzr4dldA8UdTb7ZlVVlI2DACdCfz31RPDgJM= -github.com/hashicorp/hcl v1.0.1-vault-5/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM= github.com/hmarr/codeowners v1.2.1 h1:+9yndrwG0UVP1GkLBEQMSbSUNeLpbrbL924SRthA/9k= github.com/hmarr/codeowners v1.2.1/go.mod h1:KPlR1p/B4owPjwfNIBueWlOP4CmqlQFX9b6nANG6j40= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= @@ -274,8 +274,8 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -298,10 +298,8 @@ github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhn github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= github.com/mackerelio/go-osstat v0.2.5 h1:+MqTbZUhoIt4m8qzkVoXUJg1EuifwlAJSk4Yl2GXh+o= github.com/mackerelio/go-osstat v0.2.5/go.mod h1:atxwWF+POUZcdtR1wnsUcQxTytoHG4uhl2AKKzrOajY= -github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= -github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= -github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= +github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= @@ -320,8 +318,8 @@ github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/ github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos= github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ= -github.com/miekg/dns v1.1.62 h1:cN8OuEF1/x5Rq6Np+h1epln8OiyPWV+lROx9LxcGgIQ= -github.com/miekg/dns v1.1.62/go.mod h1:mvDlcItzm+br7MToIKqkglaGhlFMHJ9DTNNWONWXbNQ= +github.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY= +github.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= @@ -359,10 +357,10 @@ github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+ github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= -github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= -github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= -github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= +github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= +github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= @@ -370,12 +368,12 @@ github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQ github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A= github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU= -github.com/osrg/gobgp/v3 v3.34.0 h1:DDIWsAIE7j1dwhSV3tGsTKs9OO8MTOS4atErebZxTtA= -github.com/osrg/gobgp/v3 v3.34.0/go.mod h1:l2nPaHaLmIoKbFxMUzKon/h6c9BTzCp5zJI9Dhnrx5c= +github.com/osrg/gobgp/v3 v3.35.0 h1:8HQyCCoByAJyXiSzTFBl77wuOyVdDXozPdXX+tjYyHQ= +github.com/osrg/gobgp/v3 v3.35.0/go.mod h1:l2nPaHaLmIoKbFxMUzKon/h6c9BTzCp5zJI9Dhnrx5c= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= -github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= -github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= +github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7 h1:Dx7Ovyv/SFnMFw3fD4oEoeorXc6saIiQ23LrGLth0Gw= @@ -395,21 +393,21 @@ github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjz github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= -github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y= -github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk= +github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= -github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= +github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k= +github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= -github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= -github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/prometheus/procfs v0.16.0 h1:xh6oHhKwnOJKMYiYBDWmkHqQPyiY40sny36Cmx2bbsM= +github.com/prometheus/procfs v0.16.0/go.mod h1:8veyXUu3nGP7oaCxhX6yeaM5u4stL2FeMXnCqhDthZg= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -419,10 +417,8 @@ github.com/rubenv/sql-migrate v1.7.1 h1:f/o0WgfO/GqNuVg+6801K/KW3WdDSupzSjDYODmi github.com/rubenv/sql-migrate v1.7.1/go.mod h1:Ob2Psprc0/3ggbM6wCzyYVFFuc6FyZrb2AS+ezLDFb4= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ= -github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4= -github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE= -github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ= +github.com/sagikazarmark/locafero v0.7.0 h1:5MqpDsTGNDhY8sGp0Aowyf0qKsPrhewaLSsFaodPcyo= +github.com/sagikazarmark/locafero v0.7.0/go.mod h1:2za3Cg5rMaTMoG/2Ulr9AwtFaIppKXTRYnozin4aB5k= github.com/sasha-s/go-deadlock v0.3.5 h1:tNCOEEDG6tBqrNDOX35j/7hL5FcFViG6awUGROb2NsU= github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= @@ -437,21 +433,18 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= -github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs= -github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4= +github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= +github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y= github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo= -github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= -github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= -github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= +github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI= -github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg= +github.com/spf13/viper v1.20.0 h1:zrxIyR3RQIOsarIrgL8+sAvALXul9jeEPa06Y0Ph6vY= +github.com/spf13/viper v1.20.0/go.mod h1:P9Mdzt1zoHIG8m2eZQinpiBjo6kCmZSKBClNNqjJvu4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= @@ -459,11 +452,7 @@ github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UV github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8= @@ -509,12 +498,12 @@ github.com/zmap/zcrypto v0.0.0-20230310154051-c8b263fd8300/go.mod h1:mOd4yUMgn2f github.com/zmap/zlint/v3 v3.0.0/go.mod h1:paGwFySdHIBEMJ61YjoqT4h7Ge+fdYG4sUQhnTb1lJ8= github.com/zmap/zlint/v3 v3.5.0 h1:Eh2B5t6VKgVH0DFmTwOqE50POvyDhUaU9T2mJOe1vfQ= github.com/zmap/zlint/v3 v3.5.0/go.mod h1:JkNSrsDJ8F4VRtBZcYUQSvnWFL7utcjDIn+FE64mlBI= -go.etcd.io/etcd/api/v3 v3.5.18 h1:Q4oDAKnmwqTo5lafvB+afbgCDF7E35E4EYV2g+FNGhs= -go.etcd.io/etcd/api/v3 v3.5.18/go.mod h1:uY03Ob2H50077J7Qq0DeehjM/A9S8PhVfbQ1mSaMopU= -go.etcd.io/etcd/client/pkg/v3 v3.5.18 h1:mZPOYw4h8rTk7TeJ5+3udUkfVGBqc+GCjOJYd68QgNM= -go.etcd.io/etcd/client/pkg/v3 v3.5.18/go.mod h1:BxVf2o5wXG9ZJV+/Cu7QNUiJYk4A29sAhoI5tIRsCu4= -go.etcd.io/etcd/client/v3 v3.5.18 h1:nvvYmNHGumkDjZhTHgVU36A9pykGa2K4lAJ0yY7hcXA= -go.etcd.io/etcd/client/v3 v3.5.18/go.mod h1:kmemwOsPU9broExyhYsBxX4spCTDX3yLgPMWtpBXG6E= +go.etcd.io/etcd/api/v3 v3.5.19 h1:w3L6sQZGsWPuBxRQ4m6pPP3bVUtV8rjW033EGwlr0jw= +go.etcd.io/etcd/api/v3 v3.5.19/go.mod h1:QqKGViq4KTgOG43dr/uH0vmGWIaoJY3ggFi6ZH0TH/U= +go.etcd.io/etcd/client/pkg/v3 v3.5.19 h1:9VsyGhg0WQGjDWWlDI4VuaS9PZJGNbPkaHEIuLwtixk= +go.etcd.io/etcd/client/pkg/v3 v3.5.19/go.mod h1:qaOi1k4ZA9lVLejXNvyPABrVEe7VymMF2433yyRQ7O0= +go.etcd.io/etcd/client/v3 v3.5.19 h1:+4byIz6ti3QC28W0zB0cEZWwhpVHXdrKovyycJh1KNo= +go.etcd.io/etcd/client/v3 v3.5.19/go.mod h1:FNzyinmMIl0oVsty1zA3hFeUrxXI/JpEnz4sG+POzjU= go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80= go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= @@ -523,16 +512,16 @@ go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJyS go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= -go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY= -go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI= -go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= -go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= -go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= -go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= -go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= -go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= -go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k= -go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE= +go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= +go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= +go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk= +go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w= +go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= +go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= go.uber.org/dig v1.17.1 h1:Tga8Lz8PcYNsWsyHMZ1Vm0OQOUaJNDyvPImgbAu9YSc= @@ -553,16 +542,16 @@ golang.org/x/crypto v0.0.0-20201124201722-c8d3bf9c5392/go.mod h1:jdWPYTVW3xRLrWP golang.org/x/crypto v0.0.0-20201208171446-5f87f3452ae9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus= -golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M= +golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= +golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa h1:ELnwvuAXPNtPk1TJRuGkI9fDTwym6AYBu0qzT8AcHdI= golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/mod v0.23.0 h1:Zb7khfcRGKk+kqfxFaP5tZqCnDZMjC5VtUBs87Hr6QM= -golang.org/x/mod v0.23.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU= +golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -577,10 +566,10 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= -golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8= -golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk= -golang.org/x/oauth2 v0.26.0 h1:afQXWNNaeC4nvZ0Ed9XvCCzXM6UHJG7iCg0W4fPqSBE= -golang.org/x/oauth2 v0.26.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/net v0.37.0 h1:1zLorHbz+LYj7MQlSf1+2tPIIgibq2eL5xkrGk6f+2c= +golang.org/x/net v0.37.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc= +golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -588,8 +577,8 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= -golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -615,43 +604,43 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= -golang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU= -golang.org/x/term v0.29.0/go.mod h1:6bl4lRlvVuDgSf3179VpIxBF0o10JUpXWOnI7nErv7s= +golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= -golang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4= -golang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= +golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.30.0 h1:BgcpHewrV5AUp2G9MebG4XPFI1E2W41zU1SaqVA9vJY= -golang.org/x/tools v0.30.0/go.mod h1:c347cR/OJfw5TI+GfX7RUPNMdDRRbjvYTS0jPyvsVtY= +golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU= +golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -google.golang.org/genproto/googleapis/api v0.0.0-20250207221924-e9438ea467c6 h1:L9JNMl/plZH9wmzQUHleO/ZZDSN+9Gh41wPczNy+5Fk= -google.golang.org/genproto/googleapis/api v0.0.0-20250207221924-e9438ea467c6/go.mod h1:iYONQfRdizDB8JJBybql13nArx91jcUk7zCXEsOofM4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 h1:2duwAxN2+k0xLNpjnHTXoMUgnv6VPSp5fiqTuwSxjmI= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk= -google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= -google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= +google.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4 h1:IFnXJq3UPB3oBREOodn1v1aGQeZYQclEmvWRMN0PSsY= +google.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:c8q6Z6OCqnfVIqUFJkCzKcrj8eCvUrz+K4KRzSTuANg= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 h1:iK2jbkWL86DXjEx0qiHcRE9dE4/Ahua5k6V8OWFb//c= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= +google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg= +google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= @@ -663,8 +652,6 @@ gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSP gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= -gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -675,34 +662,34 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= -helm.sh/helm/v3 v3.17.0 h1:DUD4AGdNVn7PSTYfxe1gmQG7s18QeWv/4jI9TubnhT0= -helm.sh/helm/v3 v3.17.0/go.mod h1:Mo7eGyKPPHlS0Ml67W8z/lbkox/gD9Xt1XpD6bxvZZA= -k8s.io/api v0.32.2 h1:bZrMLEkgizC24G9eViHGOPbW+aRo9duEISRIJKfdJuw= -k8s.io/api v0.32.2/go.mod h1:hKlhk4x1sJyYnHENsrdCWw31FEmCijNGPJO5WzHiJ6Y= -k8s.io/apiextensions-apiserver v0.32.2 h1:2YMk285jWMk2188V2AERy5yDwBYrjgWYggscghPCvV4= -k8s.io/apiextensions-apiserver v0.32.2/go.mod h1:GPwf8sph7YlJT3H6aKUWtd0E+oyShk/YHWQHf/OOgCA= -k8s.io/apimachinery v0.32.2 h1:yoQBR9ZGkA6Rgmhbp/yuT9/g+4lxtsGYwW6dR6BDPLQ= -k8s.io/apimachinery v0.32.2/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= -k8s.io/apiserver v0.32.2 h1:WzyxAu4mvLkQxwD9hGa4ZfExo3yZZaYzoYvvVDlM6vw= -k8s.io/apiserver v0.32.2/go.mod h1:PEwREHiHNU2oFdte7BjzA1ZyjWjuckORLIK/wLV5goM= -k8s.io/cli-runtime v0.32.2 h1:aKQR4foh9qeyckKRkNXUccP9moxzffyndZAvr+IXMks= -k8s.io/cli-runtime v0.32.2/go.mod h1:a/JpeMztz3xDa7GCyyShcwe55p8pbcCVQxvqZnIwXN8= -k8s.io/client-go v0.32.2 h1:4dYCD4Nz+9RApM2b/3BtVvBHw54QjMFUl1OLcJG5yOA= -k8s.io/client-go v0.32.2/go.mod h1:fpZ4oJXclZ3r2nDOv+Ux3XcJutfrwjKTCHz2H3sww94= -k8s.io/component-base v0.32.2 h1:1aUL5Vdmu7qNo4ZsE+569PV5zFatM9hl+lb3dEea2zU= -k8s.io/component-base v0.32.2/go.mod h1:PXJ61Vx9Lg+P5mS8TLd7bCIr+eMJRQTyXe8KvkrvJq0= +helm.sh/helm/v3 v3.17.2 h1:agYQ5ew2jq5vdx2K7q5W44KyKQrnSubUMCQsjkiv3/o= +helm.sh/helm/v3 v3.17.2/go.mod h1:+uJKMH/UiMzZQOALR3XUf3BLIoczI2RKKD6bMhPh4G8= +k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls= +k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k= +k8s.io/apiextensions-apiserver v0.32.3 h1:4D8vy+9GWerlErCwVIbcQjsWunF9SUGNu7O7hiQTyPY= +k8s.io/apiextensions-apiserver v0.32.3/go.mod h1:8YwcvVRMVzw0r1Stc7XfGAzB/SIVLunqApySV5V7Dss= +k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U= +k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= +k8s.io/apiserver v0.32.3 h1:kOw2KBuHOA+wetX1MkmrxgBr648ksz653j26ESuWNY8= +k8s.io/apiserver v0.32.3/go.mod h1:q1x9B8E/WzShF49wh3ADOh6muSfpmFL0I2t+TG0Zdgc= +k8s.io/cli-runtime v0.32.3 h1:khLF2ivU2T6Q77H97atx3REY9tXiA3OLOjWJxUrdvss= +k8s.io/cli-runtime v0.32.3/go.mod h1:vZT6dZq7mZAca53rwUfdFSZjdtLyfF61mkf/8q+Xjak= +k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU= +k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY= +k8s.io/component-base v0.32.3 h1:98WJvvMs3QZ2LYHBzvltFSeJjEx7t5+8s71P7M74u8k= +k8s.io/component-base v0.32.3/go.mod h1:LWi9cR+yPAv7cu2X9rZanTiFKB2kHA+JjmhkKjCZRpI= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= -k8s.io/kubectl v0.32.2 h1:TAkag6+XfSBgkqK9I7ZvwtF0WVtUAvK8ZqTt+5zi1Us= -k8s.io/kubectl v0.32.2/go.mod h1:+h/NQFSPxiDZYX/WZaWw9fwYezGLISP0ud8nQKg+3g8= +k8s.io/kubectl v0.32.3 h1:VMi584rbboso+yjfv0d8uBHwwxbC438LKq+dXd5tOAI= +k8s.io/kubectl v0.32.3/go.mod h1:6Euv2aso5GKzo/UVMacV6C7miuyevpfI91SvBvV9Zdg= k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= oras.land/oras-go v1.2.5 h1:XpYuAwAb0DfQsunIyMfeET92emK8km3W4yEzZvUbsTo= oras.land/oras-go v1.2.5/go.mod h1:PuAwRShRZCsZb7g8Ar3jKKQR/2A/qN+pkYxIOd/FAoo= -sigs.k8s.io/controller-runtime v0.20.1 h1:JbGMAG/X94NeM3xvjenVUaBjy6Ui4Ogd/J5ZtjZnHaE= -sigs.k8s.io/controller-runtime v0.20.1/go.mod h1:BrP3w158MwvB3ZbNpaAcIKkHQ7YGpYnzpoSTZ8E14WU= +sigs.k8s.io/controller-runtime v0.20.3 h1:I6Ln8JfQjHH7JbtCD2HCYHoIzajoRxPNuvhvcDbZgkI= +sigs.k8s.io/controller-runtime v0.20.3/go.mod h1:xg2XB0K5ShQzAgsoujxuKN4LNXR2LfwwHsPj7Iaw+XY= sigs.k8s.io/gateway-api v1.2.1 h1:fZZ/+RyRb+Y5tGkwxFKuYuSRQHu9dZtbjenblleOLHM= sigs.k8s.io/gateway-api v1.2.1/go.mod h1:EpNfEXNjiYfUJypf0eZ0P5iXA9ekSGWaS1WgPaM42X0= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= @@ -711,9 +698,9 @@ sigs.k8s.io/kustomize/api v0.18.0 h1:hTzp67k+3NEVInwz5BHyzc9rGxIauoXferXyjv5lWPo sigs.k8s.io/kustomize/api v0.18.0/go.mod h1:f8isXnX+8b+SGLHQ6yO4JG1rdkZlvhaCf/uZbLVMb0U= sigs.k8s.io/kustomize/kyaml v0.18.1 h1:WvBo56Wzw3fjS+7vBjN6TeivvpbW9GmRaWZ9CIVmt4E= sigs.k8s.io/kustomize/kyaml v0.18.1/go.mod h1:C3L2BFVU1jgcddNBE1TxuVLgS46TjObMwW5FT9FcjYo= -sigs.k8s.io/mcs-api v0.1.1-0.20250129110323-a7986579439f h1:VUD0/ipPthw+Q6eLYtbEPfStDidsKavey7fTKw+U30M= -sigs.k8s.io/mcs-api v0.1.1-0.20250129110323-a7986579439f/go.mod h1:M1Zjh0Jn/Z5e/2JHsZyEeLMw0qGBBmkJqEOc+OceERY= -sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA= -sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= +sigs.k8s.io/mcs-api v0.1.1-0.20250224121229-6c631f4730d0 h1:LChl5QBr39XNzUjscGlfBJYjyclDru70cLujcC8Vn/M= +sigs.k8s.io/mcs-api v0.1.1-0.20250224121229-6c631f4730d0/go.mod h1:M1Zjh0Jn/Z5e/2JHsZyEeLMw0qGBBmkJqEOc+OceERY= +sigs.k8s.io/structured-merge-diff/v4 v4.5.0 h1:nbCitCK2hfnhyiKo6uf2HxUPTCodY6Qaf85SbDIaMBk= +sigs.k8s.io/structured-merge-diff/v4 v4.5.0/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/vendor/github.com/cilium/charts/README.md b/vendor/github.com/cilium/charts/README.md index 8521919ba6..b659739b2e 100644 --- a/vendor/github.com/cilium/charts/README.md +++ b/vendor/github.com/cilium/charts/README.md @@ -1,5 +1,8 @@ This repository holds helm templates for the following Cilium releases: +* [v1.18.0-pre.0](https://github.com/cilium/cilium/releases/tag/v1.18.0-pre.0) (_[source](https://github.com/cilium/cilium/tree/v1.18.0-pre.0/install/kubernetes/cilium)_) +* [v1.17.2](https://github.com/cilium/cilium/releases/tag/v1.17.2) (_[source](https://github.com/cilium/cilium/tree/v1.17.2/install/kubernetes/cilium)_) +* [v1.17.1](https://github.com/cilium/cilium/releases/tag/v1.17.1) (_[source](https://github.com/cilium/cilium/tree/v1.17.1/install/kubernetes/cilium)_) * [v1.17.0](https://github.com/cilium/cilium/releases/tag/v1.17.0) (_[source](https://github.com/cilium/cilium/tree/v1.17.0/install/kubernetes/cilium)_) * [v1.17.0-rc.2](https://github.com/cilium/cilium/releases/tag/v1.17.0-rc.2) (_[source](https://github.com/cilium/cilium/tree/v1.17.0-rc.2/install/kubernetes/cilium)_) * [v1.17.0-rc.1](https://github.com/cilium/cilium/releases/tag/v1.17.0-rc.1) (_[source](https://github.com/cilium/cilium/tree/v1.17.0-rc.1/install/kubernetes/cilium)_) @@ -8,6 +11,8 @@ This repository holds helm templates for the following Cilium releases: * [v1.17.0-pre.2](https://github.com/cilium/cilium/releases/tag/v1.17.0-pre.2) (_[source](https://github.com/cilium/cilium/tree/v1.17.0-pre.2/install/kubernetes/cilium)_) * [v1.17.0-pre.1](https://github.com/cilium/cilium/releases/tag/v1.17.0-pre.1) (_[source](https://github.com/cilium/cilium/tree/v1.17.0-pre.1/install/kubernetes/cilium)_) * [v1.17.0-pre.0](https://github.com/cilium/cilium/releases/tag/v1.17.0-pre.0) (_[source](https://github.com/cilium/cilium/tree/v1.17.0-pre.0/install/kubernetes/cilium)_) +* [v1.16.8](https://github.com/cilium/cilium/releases/tag/v1.16.8) (_[source](https://github.com/cilium/cilium/tree/v1.16.8/install/kubernetes/cilium)_) +* [v1.16.7](https://github.com/cilium/cilium/releases/tag/v1.16.7) (_[source](https://github.com/cilium/cilium/tree/v1.16.7/install/kubernetes/cilium)_) * [v1.16.6](https://github.com/cilium/cilium/releases/tag/v1.16.6) (_[source](https://github.com/cilium/cilium/tree/v1.16.6/install/kubernetes/cilium)_) * [v1.16.5](https://github.com/cilium/cilium/releases/tag/v1.16.5) (_[source](https://github.com/cilium/cilium/tree/v1.16.5/install/kubernetes/cilium)_) * [v1.16.4](https://github.com/cilium/cilium/releases/tag/v1.16.4) (_[source](https://github.com/cilium/cilium/tree/v1.16.4/install/kubernetes/cilium)_) @@ -22,6 +27,8 @@ This repository holds helm templates for the following Cilium releases: * [v1.16.0-pre.2](https://github.com/cilium/cilium/releases/tag/v1.16.0-pre.2) (_[source](https://github.com/cilium/cilium/tree/v1.16.0-pre.2/install/kubernetes/cilium)_) * [v1.16.0-pre.1](https://github.com/cilium/cilium/releases/tag/v1.16.0-pre.1) (_[source](https://github.com/cilium/cilium/tree/v1.16.0-pre.1/install/kubernetes/cilium)_) * [v1.16.0-pre.0](https://github.com/cilium/cilium/releases/tag/v1.16.0-pre.0) (_[source](https://github.com/cilium/cilium/tree/v1.16.0-pre.0/install/kubernetes/cilium)_) +* [v1.15.15](https://github.com/cilium/cilium/releases/tag/v1.15.15) (_[source](https://github.com/cilium/cilium/tree/v1.15.15/install/kubernetes/cilium)_) +* [v1.15.14](https://github.com/cilium/cilium/releases/tag/v1.15.14) (_[source](https://github.com/cilium/cilium/tree/v1.15.14/install/kubernetes/cilium)_) * [v1.15.13](https://github.com/cilium/cilium/releases/tag/v1.15.13) (_[source](https://github.com/cilium/cilium/tree/v1.15.13/install/kubernetes/cilium)_) * [v1.15.12](https://github.com/cilium/cilium/releases/tag/v1.15.12) (_[source](https://github.com/cilium/cilium/tree/v1.15.12/install/kubernetes/cilium)_) * [v1.15.11](https://github.com/cilium/cilium/releases/tag/v1.15.11) (_[source](https://github.com/cilium/cilium/tree/v1.15.11/install/kubernetes/cilium)_) diff --git a/vendor/github.com/cilium/charts/cilium-1.15.14.tgz b/vendor/github.com/cilium/charts/cilium-1.15.14.tgz new file mode 100644 index 0000000000000000000000000000000000000000..aa9f9f728ec2f5684d5168ddbc274a3785de2185 GIT binary patch literal 180304 zcmV)5K*_%!iwG0|00000|0w_~VMtOiV@ORlOnEsqVl!4SWK%V1T2nbTPgYhoO;>Dc zVQyr3R8em|NM&qo0PMYMcN;m9AUdD`6qk{wZ-@#x|``_Ww{%G)b!-M0)!{O+7cr^UG!EkhNbo6&@@H^nL@n2zb zuK#ZE7mrn+-2Wl}@ibLA&!tMuxWyRPxt#MTUly~TRQ*WDX3W}Mb}!R-%+6u6d=mMi z()Ux9$Y}kqDPs>p8>!MUdl=b2Q@#>oh8ruzwwY}!h3Vn1J*oaRZ865!zp_}GEaB@< z&hY0P!ZzAt7%ry?i7mB}+xL`%jE%a?-jnY54*TCNyR67|S(&F6C& zu^BfaW-4VL{`umQ=`f{Pt^nd(Cb`gz=j@8Fg=WXSe@&|dKTisi3;if#u@mh9EK)iu zyFRlNtZ@~BVZ*8KwkkpAjTK_1$TFpK#8ahnxhS+u7mQGJL_>gxuno@L1z0IFE9xzys3CyCi0?B!D~bjp(-mA+3Dk9Q!bpe~QpWd}OC zQCK)kNIC#tCErFLM>A6)Sg`NdTxsU8^QVikRHfZ=4>OOq-J-uJnF0)=;|77QRK$}T z5lbzi+^su-e&*CCwwtDIJ8w9E3nMZ&A`S;~DWfH|ov)b@IS?Y@Gm+r#wVfH) zf^lQyA{8;q72R2BEFWZC@Pvsp&Xlw({=$*Iq=vhkV-Iu(CRYdh3?W_HaGzGUK`^mQ z#YhIFVOQ7*(j8mQlBj5jw6V*B%Ylh>u5=_;BE`!GN9-WH$<&GqS{P%Sa*z&0z7!fp zqPxj{lJ*9RB4NzDNRl;EnZUiz6H9J@+(h!*L`HRXz>{prhg%76d+fT6Jz4!YyQ~2; z2B=$CW1&@%!N2Tc_=RwLuz4YxS52x-eeEp9x$RJ7Ezc`==q7%H-}P^0a0$wGWC2xR+lGyNco|Z(z}{ zqUh_$_#D}q1kpxvy@FI;5Pg|mO|-HTH5rdYNUaSH`~Kr}Dufk@8(&7vOm?=&GVJuq z=jr>8Q%1w>cA|q00G5ulov1FXKuYbbw50{lOZf`A5|#+EB5av$)Sw;TxQVt>gZ5>^ zR(gRLG}>;5G3WqbX>V;10~tBIlN+f**uI7;b`4%f5=;8m)5DIl6EP|{wMY%YS)^7g zm0DTQUb=1z)F%njJ5TMY(qb;ONFxz51V3A|RgvT}OORRG{)Wx9T5Z`Q8%*f^$LU%8 zAapJbT+t!L0OT`N<6*}gc+&fiQvkU!;Iovgw6=sR49LbT7IT^6yy=o7Dpv4f+^|QH zB=&#dW1ed{E38Z&GI-x7h6f^vAj())Cg$=*}Kpk5{V$~WjcRSdjYqfFwau*^MlY@+86Mq$hHGn6?K1RKelRYDxfYaN8g#ut_zB6cv9USb=bT}`AwA! zkKgmeUXXe+ohp^=PLD+Kct(9?mMN9&NDMnDbE8`M9LH^@#DzV6Ah|boZ;^;^K{Q^0 z_{?RKDJW{f`LbISFE)={c;0rU#+DPeU6>EH1Fg>Uo1sJs*?J-kF!ag-<2@zT-3Oi& zf@NH4UM#nsFvw7F#0Oj~a-ml;^-3fseA|-)%-3FD1g+de_B9AR7**w3 zaQB?<*bRlCV5fCJtbdT)DXG$2=s9mXe^V8|mKkL#evt=-r*Ep_mpD%i7ZL>Q>%{i7 zWBDxxARRxp&t3af*b&aI*<0BYJs=baQC;W>Usfs>?|T25wp%=tKa^8!@htOy4tvAH z-teGRK~cP)e(0hV=#$r8bl5Fv3z62JXLb!qqu*L8OM+#dXJ*{*dm{B)GO`f;hdAy1 zVq%fV2i;3W-d`-KG5yc~TydH9FH~gTifHWX%N1V;(@)ewb&X0?&pa$z_hS91bZo}0 zE_*-y(CPx#t!`)p>jvnzx*q>lw}yO|-Dy?K5@D7~<+kyCF%zCfZ~n(WSvno{2HnA% zR=6)(^ro~(xG63qSR^C0s?MzSP^bC-ZhPwgY5x&9;qo!RlLNrB_W%9kgTujb-Tr@k zF#4PQ|9{1Qd+dWu1Y3yI%J3*tfC45-`uG0S_pkZAn9D@`cXsiaefoUIE-xl`z1AMP zTS~+17kfc@BXrv3aRPBNX4U@Cq2kpd+Y-kVfiXcgyl)c9X9Np*#GBr%OuC_&9Ha!rqznYtXNo~ zdyjRy>@wxEL@@Y~C2Fxi)scXo<1w4_#0b~eiKN0_x-(@g?Go`I5(9|VfzZShTukA+ zS%ibGgfaGt01%!k_RW~UJ^}k^v;=sjn03wC=2NAc=Qv_}I05y9lgJ z0GR@jGSm-8zDcO4)I$G<15U~kZU!g_Z+6zYRY#A1LqP_BR97C%-PeeKJ zY|Ubk@U>+Xdltxw6=^Q5;4CyU`66D3oYBmvBg^GV7^aH6XGwMruC_H}-`d<53Rx#vzdQ{={Akb|S;x=%~kr1OGD#hW5kp zKeibAQ)U{lm=)UOyJKLxlr$FFskkijGE1d%1ZY50$T(Zd#nLh=o{P;X1jvSS7;T>e z03Wwnr1|#qu}>v(p73UW_Bl<~)eFmM>92)Z!lqaidBGFLi+pLH)#UO4xZh4EHq zF$~Sle=2w)=TgMbzt^e)g}sLo5IK{D%otQV;R^^J=0&c6F9eN3XHE8R|ItE?kWBG} zH9!rnj@VKn7pqKZuGe&^UwU74qeS+lF@-Sw;r{87g(0nQV;_=8P&G_$!T%zT4XJADv*8Iha}gkNaG&+P9!V0Tzozkq-CZtax1d{Dm~or;9BH} z#w-o z;kz!ojPWbLXIjh?xma#HCKdRZ7F}%piV>_-{5<8C#cY;{E;OtIE_c8t0&Vp~zwmmz7pY^0~;pme{zH z*Nh81V|w`1v5VwPcJ^@w-g5S(t@L`#+J7qe8sa6w%d_2LEa!_cYd?^7(*9zfMX1&v zv-V{EN##@Yx@)6IgsTwCg#haa{$tGAX30l~N8|bF;pyq=>EQU_WHuNa9iE;JhKC1n zBnI0fun)%x-lq8T<&%ht($A>Na+Rq>E!N+Q^_Z2a zB1=g7XtTQjS~9e8-I9rGOg}QV;(4^Z0#DiUM`mT`F3+;s4g%|)Gm|w;(%!e>5LJYf z8M8#D#Zw3^SBck)Sb(@Gji0_1IM8MXLbcsjxv}pwF)!8k)p24K8T;OiM3$R=392g| zB&ap|0Hw>*xLXe2Zw07~TixYA}?ATMJ4^pcX ztdb90ODl;wD3|HOb3p9Lobr6h;PP>uzN25S@wH?7IFlr(Ld8?Q zDcvA<$UusLrqt4FRL-*OOs|xlY6Z3p)Rh~r&-)gF`$p;u8JsZ7aCsF7-cUL0+4h$d zD4UA7{W8To&Mmlno92v{;PXA{qAWX|Rpc{NY8m$TUjOx)o2s%u`{?)I_3PLz3Da-+ zU%fDmBai8Og z-!~OZTV~5{`CSe#_Ww5ydg!#O_72R7ocad=xff5Oe4V_1zYlmQX!J# zZBB@SgM?Ftd;!Z{#8YR0lc0j(ES7U{?#baeFm70)(gg+5t`WYA1lyxF4_qc-0#Qgi zv^VvFL-hvGv=fMUhHeN_&;;=z6#Tr6W}Z~|DuZuuPMm-V(*hAw1aIKi+=5{(SM}>e2)LQLt24 z*f}X1AK9$P*-9>!Ij9uFum9tp*kNxt#EaHG8$B4eE#BB6ZZ2;>f4MooeADyaCns?*B*?%9u zhhc9t>J6CEW$Tze9QFnQh34g9?F9H&6{)^`*I`)x@P7KiLpuZGN-RyJ1&Bg=%{W>* zT@VzSYAg1Ej7Rs}GMvCZ|IQQZ;_UqE=N~R_ZYCF(wSnkd*KDa*_D#gzo}FKrcb5Nf zJ(D@8(e1@;yxAOE;G4;EgoWlS@>T=pY1qff#p_49y}bK4xp?JBUw-9C zUudLfxGf0c-3a_kztwIh=nDXXbGW{^9;p$ji#Ksi&%e9* z{Q0i()Aa7HQ7MgzcEz5b`IP%JrDIKF2 zLEGEDa^4u$7kSjj!Fn-^rT!h~yXAsR1W&tkO@hyU<5Zy;KEiKpqjHwl3jLnpSrQHb zCS^`0bOTn*R5(3q>P3J+F9x^t8xt_BnTU8{Z{(#6f3CXQdoV>xn-|_8q%!cbA^M^K zW1lMx@`}Wu{{+J;Jsfl`5+wfs>fy|Kzu)!!x4ZLicjpUtC#n5IeHiM%naMTJ#bVvU zzi;VJdsI<|zER-t^94S)7}Me3c%oPQ=}YRd9J7%fuM!o(en4NV`n$W;;`e5@R#7VJ zeobfJ-?I0A{Cxgt0$%2e(rsyveNwsi^#dC)lW9agbcw(hitX>c3BctK1Y0;R1lPVy za9GPO&i)K;Pvuzb?Kf_J>ycgd+dH`D1P5~0jJ2&0_wYKjY;LMzh={P~TNpMI`J)gi zn7V~-?YSt9IsHO^n03I~ZWn;za1pJkYwPm5~BB7sMJ4P{d_6*?HU+Pvnfxc$BCj-q;!V?c9E2m(_K9;V8{`8b8W7 zUsC1>kcU*3%a#1IV0Y(JV8vf9ra>Mf@B^`8Z3}N8u#RjVVTY~$DlZSz}|xANTvndJ)p9;(|rH_=G|70;Ik-~50($Se}4mHiZ!1Xir8GF)F=M;6O7F5B zSe#s2NF$KuK0r!~xQI&AjLEf(tmu=ZFE`luW2w{0XVkg!B>BT66mR6No%<6l@z zS3dfiMui5gULv>^N-1F23aBDxvEXrsaqycj8)P!NCvQL)=7Io@+3?UfEMWcYgXV!J zjOP@N27zn{2o?q~bHCCQ;9ocP!-e2+B2zJDRk&7SQ$_BtX)SUs@qPw?JYjP#6BB~V z^%}h(#%yGo79%4z5H1Mm?w?ze&2C%Q#f9RPuVN!oh>!TFd0xm|6^5PNOdDbPK)x|+ zi+nlQ^l5(oaoUbM5q0jz?W7o!{ z-3-Qcw?>yq3o?(T)u~!x%sF#c!iXnRmlv*#hn`GBKdj0KHIN`zS+ZIw4_ap^@l{VJ zMvn*%Y}|<4bFc@g!SB|R1!P3+Gt!YP^dPy9VyUM#Vdpm&re_z2v{TcBr**c1Y?6Y> zf!k-c)fTx3ZDv~JU}48>D-uDJ=~wW~g&TklojooOq6{0*3bgX31~JsbQ#KFkQI6p@ z3w`NTJfmuhb4S=Y#RB#TfaQvC4j)1W9>xp#04JTtq-+U1uIvp@!7Qy-w#thOX(II? z^v2}|aUs`tUqCwvEl_=!#%ywaJ!Nyv7e3&!YoE~I?Vcr&5l*3q=wk<(xcn@eLsHfv zS71GE*!|A;iTM};`%ne7B+h|e_)C$pZywM$yS_9UvhHdx`xyX=n32|FkA0hE^PW~J z|Atd1LkyT`G2JF4@jE!PUYH zZ0@?>SU8LEnDx!t^yj8O%jT{6DbKX%1`JTmAz+uSct$}>okQp0|qV#^-zd! zhoghh$;mg|>&t7NUGpc1f%>&$*#;!{rY`HG=Fz>2fCI(Yvxr3bUG$fs;np=(X}5|C zqCog;#btCf8Xg=VE@$bwhRaJ>xQnk!6qI5VkD0so?1})VO=gbnM~8>|M}Og@Kb_s} zK;)F?zYU3nTr6kykC%tEO~XO|r-_zgg}z0o1p6s1lB9#rR-u31XjOqcJla2jMQTFd z?7T%RhjF;uzc$anD|Ww2AL-oVEI)Xgf!`N?c-Q+?Q%7(PHJEw1aPvg*{OBNDJj>C$ zM*i9Ea`2B#5=Q(f2o^3)`^cDjq>l+aPhu4XSt9x(?Hiz0pLAq>G0WzC3%0+xLWAC5 zFgmnLwBp&tI^`=F-O4|U8_P7eQy3kT)SU6fLJM>ogIPFhcdBxqDAyp;Kk!7x-Yvt5 z8mUS}hb8KkJ~8?W@Hq}rTo;+?gMXqvjigsyaT+R!OH&@7|2s5=g~cTYh{Ud zOgSjY8bkP$7EIVqR)$R0;37{#;9J1+9g}k}YLeZ;=MtAWDn}BYTS6}>mAza+Y%^Y) zR`!xXHZK4+TW8!DmWrIx*VCN~7<(DKff0M!*_Kyg!K3vJ(&JeYLeBRxgUa3X=JUV& z5fe#+*rG)u?R5sp2gvNjbdiWMq}<1?S=v*oed1`(;h95H$_jFcG?hC_JXvx{SQEQ( zMa+J`R3Sj7iv~P@`q?7+ zknr?9fCfY>Pxk2t79}dWce(H~t!9oPSGNz#WpwXebUf+V_s6;II@M|>YPzRHfYmKQ ziPOi)V@@~Fw!%(IW$t}!y$v|D3GIb$(0i>t_T$;jr^%;}WAL*A4Q<4=_ z(YU+?8B8umFTTGB7dBbpORY981`xi8Xakl%a{Or@&%^#~<7JLA* zOmH|=?ME*2?p*0Eyi8#?w!z;ww7|0&TC5XZq|p*W_A7q$d+b_ipF`Ez6X&WZ18*a> zTFKleQ&4j@6Lw!tZ(4gV28#BxB1!tA!Em(SvIu`r`uk!grg(OU3ZdOE3>P~8G^yv1 zPh&QdY3X4DsbX#0@__`=ya>g`u@LMVFq7^I+Wv-d8_Ob()nnQ#b+eR_LeAM*6>qdC z?d-J0{O0np&lmKZ1@l)9s{03*=l=ZD1T|n4jKISf0L_F2?K~@EtnuIsxR5)fLl-hQ zRdQz|LknWA*g~-)MN5JcCcVc){U$8=SG44qun%(cXLjioTv(QRSyC)yYI583kBTW#u^2RjmRl75Km?Z*isc4QAdB+aKjVV^b&&^8|YF;69 zkDWpKJTjR?+=x!S55TpOE-oM5{}s@8TrV&iEw$+dD~2D#HM{2bepPb&Z}pf8ZI&`C zQna&tyu_Rh9$v znM_L(Q^ZdQnFrXTsLNx#C(0`;=Qstxus3!?`5awu%Fg)sz^Pa!>9N&GvdRMX2Zvn)DY-n1o=meM_tXd8jmaY8PLNFu7&oeFG0cJLgJG&x zZUQ?OPtk}8rnO89N=$}m`4~Is)5zwrFau0oVxE;p(oTm?HJu^M%m^K zdoR{b)Uzm*sJpF|m)3E^RQYIZK$vVi9t()zEfIO)$UK7Ir`?gBv?Vryy1imEu#hd+ zx(>ny^HtX0b^5izU*HX;{exDu87aHj>@XKnU)RGKo0-V1FQI_hlU!7i(*(lx8$MJZ z)ZC}UWE5=e!k<_z{D}qOPs4$ae)4IqN}4RyBbx&#&>Jkco>}e?sU#tVImuBfiVE%V z^NGv$nsn<#;i(=EspKrebaR;cjrW4GVSC=q~S44O5bSZc%OJ7_iT{7ip>JpY#|B zK#uXZ?VF7JKV(`p7X$AXPgbIzgUy}Pxp}Ft-q_}cE4$5gY+w3!2sc=YJ1(vKog{3j z)ICiVWE#|=P$-67Rg`pil5BX1lnJ$r*!HZ=Uh30h#SE?EP9ahRt z!7`49)taRA+JwB@uv$D_(?w>8(4{hv*A5M-bStYDm|np41R=Ff1wrY}s;g!HKl~lW z-?|~~efHYLvM--Tx|ND*_x7R8^Q2WNlg*=h0)~g6L6lqykN>E-jLU zjPU5$4QLe7OKmhtaA~>QT0CV!OK@lM_M=iL^f1*N{Qs<^0Kjb>Ld?YNh4aZQu^V(KQR?BO+z+R-E^b5PIxHf|byvrJSva`;*5&lhC%agQgc?F}1+$#qICpq1iaq znzN?Ufc_rNn!U}Ojf7~cB~#ez8UMGX8H_yUDK++7SELsb6V zW726=UA@u5R3ch#5hzHD#!ieUQ3h^XFqF6(GfKgg6#2p}H169Q%O^$3DDxNDSsfineP69^Mp6c75jOCg z5+Y;v(;I5on~9wFDuH2dI?R9F_y93;y@gT>P)M)d{Je4aHrONhuOK$TvF_P@(#sR` z)6YHZ0*R4HOs4~W>pu{ae`c`XW}NYFd1=|h_FG+C!C~EcQ@LE~Zj02H;RZkemk^He?|eTlZDv1^?N1!-ZKYZNRb6+>=MkBwn(bnuVwe3(}S)ayo{OrM`xt?d@z(g%*|JPcZcM{ba0rw05r$zmH)nEKSE zl|u=7Qj16sJc4_f;yuEh=++E1HWc`KHl5TZ>rg4+YS*7lCs6Rb^qcrSB<#PcOD355 zP8Ur`s{eAA%|^)LoF~bQNB1nMR01<9DZE+E8^)jVuatlf{hC{Q_F5{i78xYugO^22 zd3-1WuWrd?SI0~XH2s3XP(gA5RM}-hC+i-&6@tZK{X{CPm2;T@VUHV20Q`z^}InGi@P+!Q*h=@UTAuSdK*YFa+OAWr&-zVj;RAx%M7#KzW>(oqa zvs@Wy_Rs(eZYGxW!93u)Een3MJx*;GTT{;Gc}Jojr4}D;Pc~KYXsTkL9e{)%{PUU; z%T%ZMV&^7uJ2WO2H-4Y)04S2NMpa%Z%V6F-jCNs(QujrM6{c49#Ro(V^Auo5C(HR8 z3HNo8rUDe0uE>Gf2xf3!D6SF*fO*(oT(mS;1`-SbB$LYxAY?C@iAN`(2yCD6Cfjr# zEm(T6bkC^N@dA!mmV|+dZ{-69k+>IYbcU;tZ%W=GrH2YZ3x;xDr!J;7NN8Pw6{6gT zUQVuNf9b5f$YEb0UXGj6z(R1N%ZZ{8t}sNnl5bg#R5bbbLS#I#Y!3Nf4-|?6c%RV9;5gZ4moXcd;I-fA(-H7Z zIB7jP4qM$izzqJ|p25G6!6V+S;3V6F^XiHF)BEhyU(XkQtJ#I=@k9uGmi1drI%ItA>2JSvf7KFu?)U86U&m8_tC`j~vkTPyPVgjOo-YOXux%JK$j$8m ztL56JETXnPI@F~baUiQ!rcLXa4<0r-&kL|q-on2~v>lUgdWS~r1oj8h0S9+`<@c?S z06s+Mh%;g?-pqx^TDnsG)t;X)4mEB{RD$?a=|f5IYIL!X)trS9&n_DuWrzb&h(s3` zZCQqceI@n2+b#{F@%@RRI_OjYTdKQWNZ;toH?CTUls((?G*edxq^W__OmrzLlif{(*+3d(5MVy%K5@u zc4{*r16~EWe``zCtHz38P(j=tx9tY$gbmP)-%a^Sw8!oL7m@*>r-+FENBci;mK(&K zQxMMN?3!ssfig~&>dI-BrM+{=xfSPbzPeHL z{vaBBK#LYgO2sS{Ih$OVP^S~i2%IzVCu~E;az&mXW$t5FiZd0DZd32E$;xskoGt5HE<7Cc&_^GX^C@O$Pd99AlB>+PY0quYyR@Pc#eFf0~XqShjY7sXl?$$Jg8yfe4IKQZwxAN0w z499IO3ksP6A=NhWmb9TL=<6)KJC_AzAu@Q0Z9XL&8GNln#x|=RzSPZCJgoE$YaTiP zl`^vk!V=omlZ8raxIT3@N6Z3JAz6`xo+j@a6dEsYgK8Z@9B`KkgxK<3tD)L54f?*A z&xK}2{@Lr$q9r84a5I29SrB#`2?_ZuF@YCk3Nb$8Lm9HcM}eJVMNSDLdq)T9j9G^DzYmZImJ$6yu*gHxv+t)>XE zeA(6L8+O=!haGp=@UX-7haGmb-(kbiafgi#4?FB|IP9?DV6@+%_M^eUaR)$m*vatJ zem&Yh+3&FZ(ecr72XYi1mNP<{pYx1Iu!-8XXE`;(eJzlr|hyUQH!NWAtb(PL*_G8BsU+`5(G0PR;mFh0yE&jv)?bXJ=>OXRWf} z&pZnMvfC;6Q%0-st0jJA{6DG!G>#@njZmMs*EuakvSQ~~llm08U3)f2m~bSfdHqxS z-Lv$|9!Num5T`RY0pq?oK_{`&t zzs(Xg>#w*>`vQ+iQ>$*I<390R`wohf7RuOhz7kpJqv@^rDsffQHk7sv4vjp9MXAva z*NA%5oEe)}cn3I5u8aii7c~Qv9Hc-i*9gkBC%u-3Q&FCpNb{`1`n-`E6`#X_sr0@P zFjdHJ2IrwnobVhn+II`;#CIuraYuZfhzRULrrTP^CAyhxla1uwTEskpd&F4r0nCXq#ajbXMB%HRc^C%a}92E94vmzygFS0j7-@$&9nP;$IvtrfY!U5$*Yu$KtnFPnMM%P&=Y@V`PQ z|7^#vHusI1!~z z*I^kq=22<#zM63(BTSx7%9;OF3t)tvq;vIGz5s+}Z2_8a#|Wx`)l+;e%9P+dPfh57 zs2<1u7yJD3D)*px5RY;ZV;t3(eS=O|dHY)|()Bms5MVl|3;^hLtMX-~ct9>!qC-g* zQsJCP>4f(Jglj9_wARn9cGG&7E8ajL_a9QD;RDF|nnwaXOP^~2-T+$~oZEM0mwchh zHbLuOwclR3(_YwXM4uiI!}j#k?vxYkRHjlwru_s~jvcn2-96)S$GI>6^>!W*V#}^M zEApk%RM&7z8wf@0ZfW_~xxeg`|EXO;xzjL>5-M&M+FJK~e$QXH-M_bO-WYJ4xEJf+ z;YO)pf&cMLLaMmG(hCJ}?je%Z9uD!UQROVPZy~1-rCcC2g_)NSot`NVGjz7c2cv_7 zwoBE>H5IzEYeX=tgum`EZo;ImRE&5pkT`K5O5+Q{yaf&UKH-b6c}ZisN&HCJ=?M}g z56QHcQ#ooXf5j%#ha;wRCGiOsN<*fiYtL^4G07f|Y92e3-5t`3(x|BUM2&cn-lys@ zrSjfP#%9wK#jedaD_h^o>v17WQ87U~_>s_GRWv?P5?8Y6^o#HWp97 z4z&mo&oJvDi#~ZF&@8l6mX=nEwkUE{xfRZeTy?#rGwfSl2R~wA4>T|6LUd|jd!?b2 zou6SbzYvF@@;X_QR~Ap_n(9~%YAtQ=kmq?f477+K79Jj~9hIFFIh1Kq`b z?u$qF1K0JwrDFFNH8cG^=N!_-5eT^ZblNp|Ld zIJXFGFS|{Frg9aQDGq4q+`9(KLN%#*OL#XbNmyWMMu?e;P3&@eH($F43L^ zX3xpzBA=?*Yz~63yD*m^fiE~iQS4#q7q#gb^}KJ?G63$Do?N6LZfAJlpTc`AY}`e! zDf(Dj<&%gC7qyQ6by==5 zm8iuUp59f4Ze`_~Ar6c>x0Ls=YR`j5Qj?*NvHKI#6n{H{;?l1B|Lue4`kg8bp_rTAMRs;sHKh&3~I*Kx52Kf%^ znEyJjy1#f%+x+SpJ?q$plU(J@1$8uh?qL!v;c~c4AL@`^=fFne?Ci#d?eYF-uztRb zL^FS)Quoc!p7rhA1Qhso5{#*eFQm~0s0QB`@dA?lV#tB(Rc}yv_|j9f0gi9A^Oh+^ zNsDwp_5e-4{(Rgp1zyWEsNvergpNd-Q}H#ppCJ&$m53B*fOdY%wMRP09iXjR@ux3o zP*jytvN)$N`%?Q>Z8HW>v2X6hB-69%mkQFA!n0eErcBT)9t&2KBe@}J!w+jY4Kgj2 zw$~13tPFy1HyEyK3W=Q>QX~X^Q$nv??&pQ(kzkq7QpEvWl*QOZ?QRTuezYB@(2v}h ztJqieF^W&jUIDpSAint9E&b&7CFV-8lA25x7_{l+qVTu!@_LSf+t^Rw-`niryHo$t z(f1iw-zI8Uh7D=JU}3Qmp*yd)^!qvf?B+Q)AMt^7ba=Qw+6K53Hygp5X4qIb$UdoW zl*&K&x`GuUP8aE!`J~btD8*j?@Wlx2a0`B};EcaVJVUopDFO*3)+!6-owKW}ZF6*C zBO9)(wd8r&7f%tyng^x8F-*5qli@=E^_U%(%crcYt2A@Dzj~Ez>YHqgh;E5^T78-( z6XwAyy_M^*Q=f`%e>mDd2;UaU6=9YQ~`zx&?dy8w!>%PF30sAl6EbSGFi6 z%xEa5L?*PVk2+j=_7rW*?YG`yFK#L)M7~1nR4RnZwc4U^szBda<+N>py0)nt!y)do z07CH_9^DJ%p0svu*8piimcNzqRBmQExC^I zOy-IOu>YYK9>_8fBW$xJ^Art?Dwj`Ana?8ZR3*|ymb$?al4eNjg z)uoG?bTQFoC{ZTJnb3KOz7@SikG1y)rcJCOmOSy*ss1w<`#q2D)qGx>J^*g6*9~l_ z84;O6fmthb%(+ag)V(=AfNVtInm^II0w6&6QCRC}22nmF#25;)pvu=+JK0e6UaW%y z8hE@r-)2I)%6abyso`KiC|Ot-LUbk4YyMP4>qjTaWI}fd24QDA5?U@sYYss-94)?< zk!i|<`5?5j4?}@1%)sHwkJ;-LgYDm{utc4!_6MnrAYVRYxp#AD4?SJWNnF?=8!0>u z1T=U3-Jg^p>%akkA8b6x|93k^l#?iv+`9;nRT)xK(}bi!*(b<9hWU4$x~m@sN0k7d zbvO6XG)I9Xuo?JLMb;76hk?|Y>1%>p*jv-g|Na_cTAqv|-5nJE}6?gb%F;wi(5l#0z|8VBI&J15x8;U)^A9O|OVYolgr zw9=a)CMC!Il`$-}AsS;@;OjzVroopHes5L?J>#%Z>Vlv9D$CdkmJhb2YP`rAw=*B z`7+s_$<~SlF;7%l(M-@~3a=wc$wTq+5Y)-4i~v3f9bb&X=eLVRCem1>kqrG`COJh( zPyuZ2UD|k}1&`P8p?w84&Eo7 zaEf0hfnXV{R6w(D;NDtf3(aGJ&fJfiz{*5*R=Oc)k8kt~iDa}^-EJYjHSa>F(n;z8 z5mcPb&MI(2{Q{ZX%SG*wp(QCV_27N0(P>rYq23@E25YiB$XGB=dPRzvE>d5Ha#QVL zs;@MIs>cck2Z2JXUm(rcxbm~FRe}Y81_y&d?R%xpa`|(YGKJNDXWj*a&Qu}0h%}^@ zFb&ORE>;`x8H@qXO#42Kbl%W_LD1QlsIsL|0HxGaJOib<5kZOg3#e2~?bN#D95ug# zZXtg|r-R)!YCg?V`I2hjfV*kVYJQ1-k>U&=fp(=($?{jIMS~wmDNvzyxuQ;G5IB0& zTs@kcJ$7>UC}fwQmzb4_arS$oApW7|*}(_<2eV^wdLoVlKR!JY@#rX?pUe-$@o;{! zKaYk-2SLR^bj%0)vy;&%8qDT=cvwPi_U(AtyJIV%>L!Ywe?(NR6yD&u z)hH7Hea5idWLn+@WW`vt&0MrQpwMdctlfyQL_UaA7&FysCYoPUt_c)Q6~V<54qW<7 zmEQ)v;piW?zEoOnZ}M2afPruG`=CH_JJt8~IsmAMM)_MwynaJty$Q@<kAk8q24z4B6ey;5i*vfI- z=ii6ryqeXViv8ngI66I=oeV}N`-7AHgD5&V-ai=e)8WY|ie|Ip;e0Oa{|rx$hvMYm zbTk^y;?wx>bpLepJFps0q2H9(c&NW2v+*$hVs2A|^a^(KilgQR`M=_ff*4c2%}blnPS#;>7YSfqqzhf@3;Q9jq;wD4IT7n9VVW7?3=66*-O=PRiEUf)ji=~)ZrcI#J_<@jprSTr?V$nHyLs6?F!`KGb{OMNySzOO7C)onL zl%nm!zsULo1pt@|toP8sjM-rT>o$jQah!qBOu=`2#uB|+;`_S-})d=HVt*WBFK1d?2?0uhpw_Q4OaFgdwv2fscD9m~i`w_tO+ zlJ>D(f&J#++SIT;{;7R&^LaYGyl8jYmp3<`Z`wcq#~W%Ji*()G9ntxksn|jY+=Ti; zcWN78`fLZ<22{<_ElcD1-)m?_a;O#o19SE?jv8e(r-h{&f3%axwd2`^MWEFuWKj07 z)a`?~Q}vb&#blo3;M{}WOx}oDPQ&(>DdYAUhD0ZTacC2QPS-rER1C9+H&F|!C|pPL zP8Y!R!Sd$pY}&mys5S%@BVM`6m2^rTCFI`li6k(Iiz};PL^AhnG%u)JizU zxx;-UYZ9P&*}UfV+WOQ*h|Db7AtqU{uMNREL?UMW6Cy^zG_)?GG96Q{Nis>MZGe_PoDDyTjaX zkm8fC;HThdR@;Uj!VZpYgkF|)I) z3)<~BgwNZh~w7+>Q&Y@8d^@DF7@)H7X!6sQw z`L&7!6T6#xnXy#lb1Y<2IzpHATvQ6eYNq1Wo-Zi4OEJN+s*huXGNo#~tvz3-gQqcg zBC_wNH&`i{T`MirgU}uHJfu;|{BX`(7>v@|tBu2b1+2iWDO3pCrMAtYC3-!cf3k0s zyvTYjVy>Umxk^)@gCpFHrJJ{a$c(SXn-!TSQ%26r3=qC7_++EK``fucGe3^eA~y@dC*NkA9NuoszL;$&P$Oe9zuMP5Ack5M0Nc%U$4i z16j79W-=r_*LEACiWKVL!M|l1%Lf@3JRya`9MW^0T~|^qO_fS4w5f_MJd#@4!$A-J zuYW=epf>D^n|qXE|DsVX+r?4?ql3yYN5ARpj%u@;$AiIm+#e1OYO_1~EoXOB&Tcpw zgyS%~fsD@yL~?{o*{Vo#3EK;x235RmLrj#8yK1W9$`{noqjrYH_i%>BPV7&k~~;W{;yNgk0T=l+6mr+8j}R2-m#A_eTSKd^^Br(+L#7 zd~Xp#od2^KdR(B2A^*P*S)X^(r#w zsUHyZ+4&#zwh-W-r?=pa>F3Qe*|q3&VQ#@i5>{2SH_fA#u<8AY@|EGpYUak@456#i zF5(Furl7v^89Z0crjr)Wq-CdoY>8>LV1jr*{m{jf<}&N9xcO5TUJosqU32p%0M<}- zb}w)rw@9Zdwap8mc`Pg=F|EDe0kqf9z!$c&gI>w>{z^qW2~#3gK3J|26w;D(MP8}s zo;><3)j%Bp(Tz^_Xm`6-2x`0KAiu!}b?<7kx`LpMK*!9=J@)cNRucL;z@5-kuMCZR zjxE%>9KE8%Sy*ElGonP*J<+;9snmBOomz0M`TBX53U<#x<=ql);Bg3<6tNdd4siQ8 z&&tqW+urN$LMI`kK5jiHXxZZEe-ma$b<6;-?}fUaF_f+s=^!d>&`uIhogb9QUYJIe`Nau_QdwhyB2c@+_~|` zb|{CPixBXUmEgMUd0z!j$X8lPDllqo_(5%-bJZVe3t~IswE?S!*#4lk=^(WowHhrO zaogV!wM|%?xHU0<(5YowJ*~rv999~mO&C^nZ0#|~@8@$5b1g`v-C=Fmj-Uo0OLCjN zEw}o+4r^yz=aMHIzuPBklvjVY%=mThmQv>q|Aa|(=URwVOyna1|Hh|l$8^hwCY@hSH-&DmPWC?a7P%*%!u7~~E{py^`JpaB^PGM!=@uUgD|cy@oMZNj zQE6qihNvl{64acb5}hTA$E`#yZsE@>#kWc!E51m?19(k;(#o!>u5{@G?bn_tEaU zn%lb(fsxoDZ{au~;~L#oPF5wFfv88?D3%ER;9@aA;DB{^iZs(};fbJEKFPcdo3U}2 zqhB%1(QAgG3!bzw?gmjsPLZ##8@(L)#)7y7s7>{esetqQ$ww%7?8-LN!!3=p;08^3 z@OFg8A8$YRKi+%}{S8s~0GTILYywKZpM1PKpKcxb=rtoBy=LU2+Q7W&xw(cH zFXXczF2B9Ris65YeHjs}f5yu08xz zVY)ULvU#@R89fu9kYitBk1>?QJZQBZa*>UzJK=|axjOsA?h2?ndg)Z(Z-2PEoW6tj z)`iPJJQF#8=j+|H_DCe^Iz2CXvniJIXG1 z45x9&Z4Ot%f}|PuA_kcdpkeS;V5{b7+_HCA8XM0r(9GqpJ02*JEpAsbEuOrg(;X>GLfnSxXk+zKg?7P)Y1++lJan1%x4p%Ko4LiQV4i0YNn ziTWf2#!domn0SX&W03D1ylbP%lM5z9MUHv21UBNG%6!n8(rmVF?K!<53eiQbx`->d zW_AhGJmHHT=%+pbIyZ<7YC3-1?mhb3?)}^D{a?I$kDlATbtJ%QAyz8)4?Yz7U|^H-yu)InJ8Ze0X^GgO z&Dnk{ryWJY`D&L#yKGGUYq@-uuYkAdgXHXAE~WtJEQ8pYpcFe=XU1g!#M@ZGA{9p1 zZtptooV@*)iz$0Q{Q!V6trnU)Zv#4ncIy|uQbWKHVl5wpeq)&6I$67%R0xRY%Xb|( zGQ#9tF`wJF$y+-*jSFP8V^>I^QYT>I`xs(D=w&0Z^ncjCoS>&@Z1jaR8I(zi$~x+g zl-FW1b@T;0?__Ep(>yap;?@8FIkQ&A4P-qd>QAQhQqq(fEL?x+7z{tQBYC_C#GQ-T za0l!A6T|$EXOCP9_8mlUkol_g_}(j@sSG_$+;hJMyMDH=j@eak%v+2l$D0KRu8z0x z2-s>OeNbx#T0-ymy!7#Nuvx>*QqFX_8soWKPY3noIQV4iGP!mueUP$j1SuiG3n7U-~RolvB$1+C72mbXeqn(+$&Er0XX! ztfmh%90&A%fzIe2ggH|xNfeC8Y5#PgJ^4B^ZrGzpl3p;TJ0D|qf&Xe4ceZAaSb4>f zs+@L@Jk&8MKzZE!?r{YzEx|}t=w1FXl%ayUQwvZG-%LID^W&}hX_@~PN@k(|l)DX# zpt0Qk*fxNJ_d%wjyP&jeQ~}1BuX(hT;sFE1(2*?MF?BM)xZX$3MJIsn_v(Flu>|KM zZum@e@xd50nsH}_P@|aUSfrG%k-%;uN4ep)9H*`y_}$h`${>~FSFL6;?J#>R@~?EX zJMNYQpOxV5EPHIo8lSqil{9m9zm+0~5A|peZ%8O~(IFlJWSv}zv|NqNK>9tO@y-j^ z&K_L3BqO5t!b57>+X-H4@KLAB`{Pwr4YFa)Kezp>ZI3a)cMuWZGk&gdl3a6^0yt>{q5eye} z`))l`58ysP!k1*!FMoW8U?tG|bTb%RkMH>7E0q$l*hlM5}O;+`AW%)UXc zt$cD#;x6~9CNS;z2O|0DqfCt~65d?SjQ}i!5cXw^^vnb*lz~#Y-D@?BIBvCkR<-7g zYV^d}u)|d}xI1K0tHEi^qP9~8wHhFwJ#Ewa+>oPkYf8#88D%vQ<;Hw{0?JXVIr(Hr z#^%J6|92&wYzRxKh1fTzhODjQMzmQ|Ear}onnLUV+%IYdXgOGRKfMIZ_3(gIuOs6sRWw}x)b6~w=xH%wB2_#)mvB3R@ z50Qp5(|;J^S*Fkk?|lU$Bf|JPSE|fCiCUBLLx*+guNlV|8@*}DU*EkZ zIzY|rmc~jQYsZcuIspGkX-P30j~HxOLK3SvR+15yt5UvtDYOC(P%%R<1=FjLsJwwU zpu>T})&P-2K2r0jIcxJIX~R}&rz#a~@L%!|Sn4TizyU@ufJY5zjUHuNv3mZ?OdT9FFdPwbC3Vj-T! ztW6<|0OBiv03o_0D4m2$P}g{hqK2m?*Uvdh7aT~_ z7(_AK9OkqXJQlhX#$4WvtK+5@JMCFYb_O$kn$JHKt2;#DJC&KMWAwq9P?`<)J7X+< zD}*mSz9l!Zy&G!v2DudLc@&?^p-8tbusmcAzDDo-BXKy0PDck(e6qj4AB&@7F&G{lpA1IP>FnU-aBws~jgOB<^TW{)q1Ll8dPQBC zebAFGD9#h)=0^HfS^871`MK}raxv8bi`vR?y(m^L)d9db(~F&ZZ_U?O&(+ur$jW8F z;8#AUB6R$Yeh)G#(uLr1Vq1Z3mF8W0*?0>CK%qP!wncxH=KV?wH{h6U|8@tF?-!uh zSH7$Y^CkT%@`f8!tBfzmOlYycQ+_G7Pd~^^*_4&?xlY$rf;A)N50h>h4SQSh)4_y083KUL7 z+}?qHCHAdt**0s6z~_2bvup-;q>t$*H|f9k!p-Y8f5lGRM-SVsU*B#?sD7&{XY5`j zU+kVs^LpTO#qI?;UJpJ>9~>NJh~3-B4jcA59F8yzb3NYfxe%|7h&%Rs#@ua>x?{g8 z?ye#7t}zGsE28hV#^3FRVFSF)IJf3B<*$ju+ZKtp8Xn>aqUyZ!F7XKwuwv#s9=saf|li*qUM4LbC^E6 zxVU_C{pRi2#m?Eq+w05Y^Q(*F%hU7gjl#TtetP}(_{Hn9bBp*-3^C23Tm`h;kK|8cFHf)D{CIhC`qYL1J4=wjfh?D?r^2Ke zdVcW|w!n+i%hzwtOWkR)F9}!Kf6nu%{OqOtPLhzM%uU^m++AW><+i2cXY<5D#RKD3 z7I|DUciZ#(eZNb1R+JCbi=p2wwPsw*lkI{xef_Pbt@VYc`LrKBzj#^bJ~ocAJx9PJ zK_eL}3txDg3p9l%s#)00D-B5HM+yM%6gvS&>H*C={I$}|_bNcg~#vk#MqQI@XfPnuNj}gD@ zh;+`mhWh?^0X~-2gL95kh;8=#J-|KNvdqA(Al22Oa==uZTRX^mtSo8r+mq^<56zsH zD|f!SkMasH=$$YTfGe)v?L2`$JbWx`-XJ7Qgtln?kHZd7x=gUH;my5*{NkMFOx@1% zndJ;=OtUIp!9JD|XMZ{FdfIcW)}&tjn#Y|Kb-8ECAI&$VJW`lZTIn}dq~J*I`K2TI zTbhJ#>+S;R2c1vV+)({)5}h=rSy<5(rWjEN^zz#7FgCSjX!2F^t-7^JDUw`>u@S)Sk z0eb-_>Xc?y(y!r-(u}?({leTW$mU6ryj7hYvA2^b*CICsGi;~Vy{V&XmTTz^4d&-v z6ta%qdf%vT&=3qM5{@gxhcFAym2XVQ1GuBnK2RG^YQK#q0+n5(Vwc>FUtmC1HaBiH zYTg5uplwqKVn!2%>iQ$5x`mrqxpCzzmNR3XsM%D+t(tthBIl`ojPV&c%}i9Kf~u5H z#x%&q#uF1r3HAvQGnO8YbC#VYQSJhwP+n-&t~N0{DORU;%|uJ+aC_Z zIrwSYvLZ~M8}KL$Dl*he9`A$~#TnGG=^CLWOOuMS3{mHSNz!%3Oi*Dt=Z`eY62S%cVA4z3T&SpH3pXZ!R zAhB284ZLq)-CtR*;pZ0DVV>oNPVpvJX&a3W-vWXjdBrl)a31Y$s76XE)r~; z(4>d}k@ZHxe6Ds#ljGIRs68aV3w}!}XwlbC7bs7HTN+29+=)VKiH39`>}WYVx{A)N z;S9;ianV74SG2TW$`R>x4~MRMTAbM6=s`yFB#f;J6f9a4stDF`ZO#C}nBB4rAXXZu zu#X@FYSV;K&<+_wt0{m}g>?Ygrm^1 zZ(@>u# zdA7J8bvp}o>Dq!&as_e`K!q5^{22vxmGDY1ELCao*_g}|Dc>Ny^tRe@MTJ2?aF60x z?b|s5jvGLVavq5%_UbCriG;$e&@Q~%Lah>9%k|=4G6;S3&-Ll19L1`$Nsy1ms^iJu z^V9Fn&dJH?<@MRivy(`&3MiO9&4+S+{DdG zh&zYQmck+QraVw@0e-NyS>omhFg@}gx9?*B%#i<P z!a#UW163f-KvrRO6`qig`U5|&F@=vi?>1C*DnhJN(}565N_Nn9&^$$uAhd6pEClc! z4EFobnegQ{KHTdb^xz>9(R~3&s(RSe9okAzZt+AgD*ypPFcC!sM$9Og`UIrW7bJ6? zjQyJhc?b1OsZ&7!7G9zVb@|hkYAA{M9m~v{J0{m`JW>cJoiO8Rr>XBfg4K{s)fZd% zRDQIf*gNM+g}PKtI#M4w5licUE9CX{k7ZnKgc?y1p7OQ7@%sA5qC0hbH;QJJoD%g* zg9vtV`NH2&KBW5L$>j?pu&+#CR|avO7hFn9%7hBro79QndL!Pr6Zr= zoY$MEQnDLEd-cIR4?<(3do(Xbn`p_KvF~^NeYfXfcQ88G?eCBG$A^dgy~Ew0AB+YE zyQ9Oy!|tHR!h`W)pN)e9)*FobbU5Cnqr>5Fe?a$!jP}F8?RiL+=73(qNNzXFZ7a9j zuWesPS6|=PL(xfWO-H*H8ms!c)SdYv_Yla@8ZS zXhg~7ueIOnPUw%}A$~Q_CRRqh=DBb-MtwCBWW=TbqF(~IL3sQk%bLcG5!}XFM1U}^ z(eyM*l5&`Zn6|P9y1C=|c^+j5>-ggQ3j2OTbzlPcmEhl_ZrZAPvr5=ZLJrHdX-flx z4;Fm^C6CO_PT2^o5ucz(3P`d|?P{lOtB9whfQ6sQ{@C&@;9e6Wtk=uQ_s8eooqDfN zkNUg&hgB#Ub>_H!b+vPK^$J^|V$p8p#LPJ`vL4&pAMPLQhus0~_4~u!aNO^Q zqw!vUceKBE&>z;SroYTHW>*XkYSr!yclQqu20_3M!|{H%&*jon_ z8c^Enhxa;~%GY8(9!Kv>$I2p7GGl<9CA1f~KKWPYFydAy#ae%KMEdI;epf#Cx}C&I zi}mySiCO$ryXTW`hW+868@9t?_s^}4zE*#K!LGg$SFRd6;4Nz!b&s>|7kSM6f=;+? z2A!|AoO{}G>NdB>vi})6_l{7m68evgRjzi#{fw#iw4<&Y;JS`!sq)Dq7?&R1uJl+Y zrLWec|3GWf7h*{2ISom@dl`~?{e$%lNe{Fkm0ZkCVxbAmMshd<9;-9~wmsszlOlmr zXE=kbd@gdf6RMk{eAPnj>LyCTIT1*RoaS?(d_3nVp?PugP_fIBrnrgHlkXCW?;n=+c^BSvw%(2u{@Pm3hOyIoDpfWDTEEH&6y zOKEPFEUTlxmdH6FVB1MnL10Vt3=(XwYOQMbJuh~|CU96FX8>aBK|A#T9D@>zG2LI| zakO*zgM(yBL;e_XG8KX?p)#wZ$5v?8AY@(AVeQzPwPRi?TxIom4f4P^l1dHExDdTI z+uOOS|(J9*423sMVq%lOH?&B|2FTgsW3b$xs->RfMv@b(2^yQp~>{?k_N_Kg8sBHld zE>suA^-qWkBGx{0~q;ZKlg*v2rwWT5dtqBA+7m|!tu&}lkDLrVC%Fv@CIqWQV?=NO>Z&?Xs!x$>s8tKe0(5J*0f{Z5ByuLUFfmIJjiX<1 z-4wa-8-3m|0xXI02jA-`UD6Xl7Nxm;yAuot!9EKa9qo@pHVEn70UI9DAl&U8(ougv z$NfP+*d4o_;10t5(V!O$0y-ET>~;IY!Du(=9kPD6-|G(c`fPk~IM_cNjrMkTd*R;x zUN;!9z3ySSH|&l2ho903PWLbzx^(SqH!It`q^=(1mt!KS+l6YmG z3Cea32MC{&#KVF34B1Bn<1<6_8{4bY_GVk1sx~Lf;?(V~VR5qMl<=Dn%-rx|UqQlT zKh}`GqJ%_gj+>iY0P-Y**)nf2mm9WQYFfISH_ljbZHq`*uiw1*@zrUSILKzH1{k<8 zH!zqBZ3~~Be1G}o&2{DB#r5U!$*Gw&BhGWV`KK@Cl2e&EV;+=({9rkvf-nCx-ykM= zPG_mkCwZ4ed2TYfSwh6t0ZVlrkW_Kgnqv|6Y7hV znAk`&Hjbmow5E&e!%D_HEKmrU!leu5n1W^4YF5tcv>qoKrOow9jlJatRKvqQ93CDX z9(MN!2cvFxZ+Ljv?F|QEz`En0%SJ5N8&NhM4##1?*B=Ilqy1i&b$7>myydbfrPG0@+AxgQS<#f9FA{=h2z;sRLiPFUR5)Twb1Aadz z3OG0b&_}t7gy$ZF4Wofkoe7E)K>1@Y<8hV3j0(L&-{*4$7w)AoRnASzsQBOrX-sKl za`VL4UUa4!7rEk8r11h(1t(bqj=INT$P@7lenW91a-wOR4dEVQy~j3`~K_^s7){ zn94l_cw8(bC+8O?BO@Lvbl!e-qhKM|%;*gx!oUIumf>@KfJZ;|hzaE*Z&28Po{HD8 zE_95#F{84XnnTG_8T;*hD(U92#+bAcK2{WZf8z=9ZB%gohxv&89a92A+)5%vAbc4m zz;4re!i9aPDSFyKFWmBl>M=}djn*H9@?(;GLb*y>cw}(niR`rF<1Co&4V0M& z51oR92rOq%*NB)#DWRrQi#%{on)op%T??JaoTPCb*}z2}O(QE|xbG~)j@V~^BmLnJ*J;Rtj8VbNlGM$Ub2Fg0Dpchjcp+7w zGoFwuox4**tI>=>1(Nq+ujiL?zp+p;FOFoPdpPM@5NfOFe9lpqthviz7q)4JCl&*G z-Ih`KRGQIoG;sq_twy0)4blng!bI3Xl2y{j7iTIhzP@;YIT%&YFy-iLL986k)F8?U zsxj6S1=(?D7x-FIK}JIre+i&q1~{}}SVv&mD`g~iJ(d-Wz{hB|H8aFG!kb6xZ#=bH zxLm4$InF5LEqvOuzdBY zT-cFImgKrEd5JaIC9AbVpUb>#Nrv)Aw!OJ-aqb=TdyLZE;o*L;%Yy!3JPL-RVVCWO zD>kfilwmazeLpsB?O;=VexBLNvIP7Rd@)Y7`|j zTgZ78B|$t7Yk0-ZKHtWWeYMSFo?!6rq9c9__dppdE~6SA5{cw#US#3k zCLfhb!5G<&$A^8MYw^RM5&3(0tlQv zTyDJXNY??@t}BklELa5A>A~b_Ez#5WzPh+gNIQhBGfL&F`=+6C50-nls@*-SJTG6J z+waPQwjP~wv#l!-6F(?N>|OJafj2{ufT$K}=%((x(z!=X*5wb;2-Er`L@Ijd4}ZUs zukmbR{XxK}q0VOsCf%0nIb(Siv0GN8BLFdDVanS_lw#Xb^F!o#>Qvl1kXGy6Vsljr z;B4&LHVTdRX727I#=}!#gR2P&+qE*Z>BUv*zRDVFn)2=m@1CZ5lI{&kbKN!5YvQR) zq1WvXUa!(y)tf%kNv_*IL(Ha7m3s{$%&-Z@kPGcdaw-BEm;A7GIhRj|{DqmiYd1fS z9KU+CqI_%{LWh`C@f)RuC3kXgQ?Y>M3smRg=m{Eg*=OaPhwMVG<;e?EJeU+g!&BYeC5$NAn_=F{8oMD4u{!gY<6ZBj#a7yiz^~ zQD8w+Vfm03rX31bLugf}1h6pWP#NT5$Sy8PG)rTs z8&A!UAx?L`p<6bEnD#Na%)-<*C6NWjftp+cENVkjL%do5_9ze|a2_h1Qh**LEYbG7 zfMtL-IM{Sx$QL-GLdUTGt8M0i+M6J znNE?QS_0+f>PeI+rjE$2IbJ$UHkFfEdBZXvz2q7B?uXM-G6g()7RD9=*P{M}^GVDg z2qoq;^hT<q5xvEIjaYVX4{NGMj)#k7b zmE-_>k6&KnG{NJ*T7tnz;iy)TX7Tt z)ks>dWp3xP^$6JtXp8#!fP;4BjNN#`en1uK_PNbNWcek?a?%cukyf9PHkGf%Sc}E}&4oPw&KTK@eCBT}f?GcHm$|XEZ)nxF-axO&-3GOv zoIg`6Mq}emL1r|W(>PxEwj_uqIkSOq)KC`KzgzZ5spi3?Jhg44I47Bzm8IJcH65wxl?GPv zeij1{9S#8Xx$6B@duYjbf3sLE|CUYY#vZCv&_LcfzYUr@OyWq88KX&*OvdwACB2dw z^pYj&^0z2?#ig|HnJN5;2+SPJ9EPK~(O_WF{uG!|)z06jy2SWofw9H52%Tg+`M-Qr zB;%YiE7xRFbX>1pwYB#xl7%X53}%&Hr9Uo<35E!CiPWK@LgN-|am0J2%WamlAF$_RU(M(Vv( z#kTCedXpIUtg-H+NRW=20v))pN}?E6gH&5d#)5eUKHUH2BXbd?ToFs_3H`lPhH5#k zJYk-g@*KX5EQkBH^uP$+r-|BAqB`(au@`fuZ;<#r_xRWwNl6e!BG01HTq^^Xcb;-` zuSg1ET~_5!;Tbj{S0IX9y!)0r3?l3j9CM#4bR8eg1A&XhL zL+$3Fd#%7&$AtOhL?=TfVv*$ZJ()zeOpsX=CK!9B!|OAV%P9!iMs8VVuhTJ~XMp^i z@a4T*pai!`!^qnohRB-?2?Vko(GeR&*h=oTK?LxRTsI5lJH0?1W2azUis`& zFRH%tN>Yd5dpj+*%V^=}vGAqT_i^bxvq+R>M6G2NSi_Y)QY-tTwvDwX7{_XURAS<( zS}+}#cY2}QuXR26`xMS{^|QX!%RG&i(4jOi?KUn$t-7}F1A`p zs^sj#_94H2J!k2$CR4VT+0>Cq+u4(+sL(B$&sgiIrm^GQZ$ zRYY3?hvg%=;-X+?e%4!d0H&nob;t%d%vke3@S3~RTj4PePF-V_cL zX7`oveMr0Un2GJXtvu!|kXj9?tKgt4DahWZ5L73R>QNGw6zMVow-s+_PYMgJx6`S3 zJ-6UFlg*m^EC$96@@y{TRt8~qZUJo-J0YO;eeC)m&}-M<1Q3lsr&wR5s~s|a%OcR! z-H+AbOR8_^^vCY@C0w+3eR=;`rO)l%;&XO(Q@x+5mz%10cW-5vbJz@2l+uQN^^<;6 zg-+A4*sxRA=otS4^7q$*Y=XciBZHQ(dVJ?6(FZ2*tr%GxgzLY#k9Tq&6>e0Y( z1cHo8U@(DC`T!_lLmMhLlXr&DyOOJQ{WGw2$bu}?2A(J69hUQ_T;!LW=kG{L^QpdL zfVk;+A%|HOP>7s`FEjdb(}_HXD0~@Z)|~^!qlPW8nB2x=9_}Yhp<`odj3q@5wRT=_ z1x4jXc`>rk(FAl8+t!w!#0s~>SH^~>`=g~%rb0Sn&^eVb>?&e+loONnik#qNIG*Pm z2ecKIrO6GX*^|TR$N{Np^z(IHNc7hYvzqfgdfi(il2 z5!uP+i3$9KqPP9(0uu`n<1&-JYfNQ98( zEI_lO_8Uzmka>rdG6ac@C$cAa79tHT@c>k2JdbU+Q;qoX!-2Rih@L7+C2$odfAa(` zcdXo_*<2pBG=`0=PE6-{$nTPkCq-t^AK|+<$(4H7Buv3>%NDw;F}FgLgmCK%aT6vl zwYCQU;G3R*0I9a|q;wNTm0cL{Knk6x+LPJG>nhIF`ey zg2XJIA^260kOh&DI{7w+HksHDMMn;oBtzz@zX2lXoadLaMUWu@JbyZf6Q@xEcA%JN zu5n#xGy+@NM#@DIfP$H*d17w($Q-eVvz*Xj3<09;)RaT8PVWI3X24D zH}K$w<*%Y-!ZMS?sn@mh8GpAVw zUykR3h5vf$W9r*WpmeEfIDs>}Dq~l{B2H9QaP_5jFo!p(Tz3l7L7uMVR3<6Q z$jkqFagHtrAb4mnHEi0+$X?U;XXyn1t3Xu0%f44$dPii~wWlNr!Rb#y14UuXAYDnC zvD*j-m&M>^7yCu)+SZr~w-V)ZJQa5S&^28*XF zYo_GQm4~i~Dm5C`$lot8hOX;El9EGT5fM`!hk{TA5oSPWlqgf!Y(jYnMLD4{PbNOO zG}46P`&^!H1eg7QgDE>q_mj%kUpUDn?2epW*kRv>eNFkboZT_Si20c)Wh4jA(FCVm z{K%%1I6KPagJ+1frl+>XiYNdn)Zq)xiP{n6I-`k5X~vQqU3n}`xuRDCMOukjmJgDUPJl8l8vVSB?Wo8X2Wk(Q}Cq2}wfYDT= zl%Ag9q({oCv?{0;NnP7kefE4l9)sommjW(oN|S`ey3glo2hLc)q)e}HtTJdB!~?m& zX-sAmB+AcoHfLASFAP2F?_aBZHDR3J2FKt!LK@XUa+{a1|}mu;#(L-eGS zts*SRGX?NA32KQ!L9ExNHZ}?(VZbcp<=itC<9R-(akX2)4HR7NWQ)X<-e^p@Vxr~> zvP=&3crK4-vbq7JR9YrSJU|Wo$C1Stu9y z*~JwLwryCwD6v1pr2$Youu(xPEi2E3+EC#34)zf|P`kUM=mevb!$-o{FjAJuhA5i} z-sTMY!){Tdo&8iRCWzW?g?K|KxKzkde}=DBVWUM4wa>iT>2bAQU7THFf(s#4BNwm; z6aky&M7vF)KvQPZ#*nO)5dfj;Rj~h{psu(nYo%!)#?Iy$kI}fP+F^M_f!k#5ZkXso z?%T6Eo!SK&3@`d|(Ho}VvyIP-M-vTrTh?lOU&;{FG2D1kXSXobU$ zb&I2Lv!Tj%Vl>7xIbth-ny5WeWmCXYQYlaX`3Zh)+bU@tTJco&%3=r%bsOy_5^~Va z3mzR8c2!iOnTnJ{vbY`r?5%vACOl#T-e3VXf75b{@%9e zBZ^ti-`(?j_VX%4*?w=df4Fxz8147=b`RO!?(SgN9}oA(;bF)I-QnRd9QFqx+wC6g z4i5XhgYb~<(cWM%8gxgj+qY@EVaiB5R9VrJc6YgEKf!mkyAb<3=zIIhquResJzVMI z$uxi-7HKpdGhE=Pp?69j>25yohvgU5em*=LbccJp`+I}^!@Yz4U^pHhj#GX}b`ZcjZ>xmk61?=8zRDrTdCo-6GYAQbSuT$Xs2P>y zCnjzE0n7Z|DpG5DjW;`p%ss0?mo3n#Z3403^qLiOwgAVZ&b~--iCJns-D08gf6UUF@uM|by8e>9vu$%dItyn zz475_JRI~7KAn1GdDwp<{it#*a1NX!WvLp%B+89%K-pW~Md$&lmUj1CQP*e?ptS2t_{yI-Zq@lRLf@QJyAWqNsa?32?I0yhnOlF@X! z(6=B4rN2b$hAplU88s8s8*~r%IxXy6&B1zJUW@pw3YP@x6R4LzUUe$(cs8L)^ougN zbbx%d3{C+B8>8$VnvYU^S$P*#Nm?MpbF|O~lQ-(ztG_efpN%0SmsU$&oL}LcI9_gp zMOk@zXq<*k=1+X`SAAIpz!v^rjj{P}zPJrG{cpF9{wlxr@aw;+sG+TGQ*(tgH0Eba z@c5R2BbaW=XaPq*Lo}% zFpGNa77j?7#t~lO0*bU{**iA33zaDg^p-I#%i#Yuux@$G<#=Pap;w!JVQ%tvyJF){ zU+Pae2p4yH=Wni0ul)Rd{&;nCyWQ^IU;zK^cDvR8_6NJWf9ehPhr?cfzqi->Q@1zh z?RWn~x{t>~^KUM4n*FK!;JT`f`$B#nK6oS=BX-rMkG?&=Ns+1VKex!H+!;*)SUOwP zr`l+0zS`O%zv3?c51rpi%t7rP&*OMOGMcEEMxBxqQXQfIIOlg2IDJTi2!2$?r||Oh z_{BdEnlrNQ4K$2@0fMX(|EUc9MvuLovZRt^GcZ~F6;lPKCitMmf|D7|ZdjhYPxer<6e*G0S zgYSVf#8{!zvwHC3nQvHuwtb2UttygG)?oLfyARtZ>{>jmFmQH{JKl+$CZSZbSbYp1 z#9%?#9*^o++_MTr2zz|vAAFvXjNux%Et0~1_@F=2nPTOa8!vfAXtE&Vc?N+IQx>P& zJ#M~M7>qo zB455sqWGN@AseLNyCog^Gr4f!0}G+LFvF23Zx%F_F)`B|qHB+ykthk-d*YLB@v7Xp zR+(N=#@S7F*}gsdDmJ|E<};NS*r?iUr-KPM@G;7<}G$odEo@q7c8Hr9pYon*HdOAisziX`{wOCQefSL zCPC^QgG&rng6Is%$r)y9pI~IsJG6qH9KYL;-D>{40%$^59d-tm=6%a>YC1L=gH_nq zBvqg5UZ=9bN*J1_!c+@ym8meGm102#b5xIi9t+7pz_Pqt?uIQK<>XVZe4>LXC8@$E z=18SEeFk}A!;`tl`RqhiQxvu-l@Da{37i@*M2~cCdmYljp6w9-5_Nmu_zIVrZyoFG zCaYxx_f{R*W~nVy%c^Eq%igd>z3hsub00W?f`L1gTcc}Csz9gYBZ-oH%c5=bGGTJA zXoLL8amT8m!c7v|2XnnV%S%cy2>#EBPda)gc4Sps#oxtb@07-%5E9K|-~@~-(bxie z6(r#ah8%+2-_|;bF0xJ7EX@_dv@GWk(^D#Re;IY{n}pxVHp%}KLNsCcViW%NBY?fW z=AqmUH*O~UZZlNZU`Ls5fHoo0~ ziA~c;o&JtUHll$~=lPUp>e|!4Ie>fW+ulYA-u9BmY-7f9D)$AcfGBWVeM~dEmT8vd z`UC>e_GL2CHs}AKvzYkgBS|8RnH`Xl{DePZEAtIa(~8q_O0(SKW5W#qc^qwcgr?DV z8K0-{rAHE$-|_4wN+zn68K2k3+tCt5?XYh|Zng`Q*Jz*pQc?4TD zR%4J6;wZRpEfDTqWnU`=0{&E&!T5*Dgl1KapYU)+4z!3Z4>0@TKtLLsUw>VZM_J*H z$Z0+=)%%28b3I9zhP}oh0?gi^<1zIt#8w&BEJsJ6CQn2o4*}?0`XTI#k#@GU*s6@5iO(w? zDAjI?f#5jnrC=#UkQLLHFy&*D(*=;5CwUahGlK%X;q#nK`5gp@-0~d1h`I%~9};%1h$RI~qf zv_1+mfa)q$T9OzJ+9?l(3i!AGkW#A+wbTYONF`tcD?qeqjw!^ME607+i^2 zixDOG)^EMtP>Y@tntEZbPuDdST>DQ=cHg6_@_Z~RS-#xrWlevyyjnVK>))0ojI$qV zd+KkkHUDvbE1&;H5rE;`=NP!;{MYLadzJHFZ*RZ<_5Alae)m5A=~v}2AagH|;-5U| zc=sHq+!u4o<1tfNTeO^RY4JuST??FNEA3Mr!*F!O!dIo1kmza{d&*X!=?4=es3`}=#tuk!zM{2pTe zQwBg{F#1%w8W6}M8r~Dq zPi)q=?Az8h)}Nshh3lrpBj3nP6)VlRCG%lC@qZCmIn`@rYiXp8jWMP$SB&DUMZZmNaHQZdQhlm<~nu&Nul3fP76 zjL>nrHoSymB~5M$ztTLD#7P3yCoJQcTsgbSn zDkmn#Lat=xmF1LY(QL`8(wn;K2TKGH;#jW@lekF#Q_SV^-eBIBlN~vo$*t0I6$8Rv z$5LOd_YhR}>#v=LZ!!Bv4dJCSJsbm@@i=}n&vA-g@KD%gpAeQnBml$|$f63|8y4S; zrfO^oB!I#ZS-W}Yq8>yni1`d8t9FV(e>(Dwf31ece@+Fvp!t+^Y9v7K>TJP?BV>e_ z&&qoIvS!<3RSClM(=57;Vm4u}4=WqyUwi&;*Y9=;hrmCF{+~m5V%(}}N@25%nDBp? zkC@t+s@~Y^d=Mo(+bR!{6hkyk$#I$@IC6=cpyXP!X+B`oYY;(nEqYO6xU*7W>bO{+rYVJY zdPMvY%V`x_$=bJ$O3k`A#hB5=(~*lgV01X?R+daP)kHN~;vTF(1_)0jae0)+(KL-0 z9wv3%e93$H=$ug6r*r zD|ctTntSx5)&`uFfLHXzJdV+4%BrjS=4cHbYRyyUmLCp;@urXYoz6Fa0+ob3d0N0= z=Tk;HREdrY6gD00xdg69CDQqHF3252K?G38QZFM>?pF){jwO2GABFJc)>NnhBh@!8p2Q2@?Vi-ZroTdtWU6h87?v6#36JxbTQ>3FHg+$ zc3>2VBHpCa>+d7#LWgvYj(Q!}gqOb*!~3U<#`*M@@+&x3Sao5c3p`Pj=U6aha%Fyh zeO<=Ol8yMD(a>7OdK_>qcSc#n##K0uCfrGRmY-+rdYUmY<#AXx+FKpzS4);TP9f9E z6(%uhCQ>R^rl6uZElzsYTV+RsBowBW;qn!yL?Aohl8uP=Wkw|zCAyFeY#QUvX>d3mHQcvA?l9X{@GU$x4p_s z_z1Ch;xx17$LFV)=cm`FS8uOQFaLgaa{BiB zH&?YrlqPnC>RKkuI*qE>XyNSQ&E=|`0oBzxtE<V-_re z*tK&dtn_s-rBMA#F(Cj9SMPC8(6Rk&S}MbLFUmP9_(wN*Nr_q*jbx#fJuu5z9}pqV>C z5_$7Pjfrm^gjbm_MOGcFRLP06)FF+tsMlR>8d+M5<3^`~J)^RTyUe_hoUOi*q^vOe zZP=ZsEFl-Q?H_BB->KE-=QNwJysi;QHRU1Sry0+Az~duweR9#Xyd0h4kS|54l^R*U z&05y1)-PHcQn|Bxzi1n0aC%>QwTGo#*Lb==poFKU%c@;cW-UdbtNkz9 zHzu7RREn6H&o5p=FgH4cX&=hU*xS0sC>=W-0}vUM{ahSCbjMxJlRTO+-_Bs!39^WH zwwx-)C>?tEvRNcJx|&nx~z2zD~& z842fUjQN1o5>TnX;jaP-yLN$#^cy#h1~wc@lVifGDzbU9l1Q)b3V9$8_EL(4%Q;LU zVDE${;}=o(S{G{DmhvEM-Iekn)EAX0&8ID`h@;VtRouBBm6W~uVHUU%A}&9`gs08y z6>-)}S*sUq;cdBQY(}{-46evc-J-a1e--xq*Hrur8e-cBMyWTW;^$fhOPec}2RrMG z+H&I-QhQBPXLlu$*OVEed1lKmUPdujwb0EVpd6y(IkBcYxabx0B2Vo=doo}no{utTU3gW4yJriX)@eXNtr|Pctz=$ z$6^P+xmPoFwfU@JbKkMwNCFv6Caio5AoU$t8qcpT@^a=R6?nYb)N)R2yD_qm{;O{B zmJ_L}i!D=ki)x#`d5HCky-#_T`{5#?vnW`>2sV!SUCbw~i*Tp0(0Y@*ewp)k%Lwsc zuii?J%*r7WMY)OyRpkub-_BL=u{{7iYow$m9?8?hho0{bfBg#yIr;CBwdtS4yQuSag4FUXL>=#qf-`q<1_iZVcEc8;&ZL{x zV{$u^%Kj?chAdgMGwUX`8ndhD+8xikIS`&KVkxuF+H~vIs=vB9xp``FbNcH4^LhM# zR^1QVxAN7GCSSm5_mk@u_fw~;)~Ti8k59;@2Cg?ZLiknRzD?mOzgH5nEM7#(g#1t> zJdgsT$bI+|<|<7DOL9mY3tKOC*p!R>sZbTbnH*~@&{lN{WWSWBT1}K@Ry?KQM5voA;^CLox3eTCoo_zqS^aCN znhyEKS~+&5+04z@iW`pRp<&mly|zvEErvMYr!Ou?_))Yollbv{em6bSVzm`HDoNdG-=SSvc#EuSh|_Sy7x)n6$LaC<^{5--Rd&u)|po6~vDeTE&S zV{iO*d#>KAJ;kp7NG2IeNyl=JByjmOO2-}Y@uM0cFNYZ+|KBS4v~90m=hE*J$jSzx z_Ykkv&*;~2VS$mDFU`FNNYBhGV?XEc2o#jLJW-_a=d#ys=n z2cG=mKYqb3Reys9jSW-V&HeBUpravHX$*NEElNw2s~3q85sy)eq$%c-f`oS>bhdTJPY zzk@mL64Y{xyaGednq_XYD_%<-zk2od>iYP4g)1rJu=xX9IBurO4%5|}1^K&Si`M?c zA5T%1HMl`4zMo&b{0;e*qWd1a|8#kFefsv~{0#5}-v0Fc>G|8s)8iNa_*{(ujo8!9 zwcg#7B_3>^tF`0n&EfCs=J4y`^b0zif&jtznX8=E!E}C$b7@=X0Z}v zZYk6c!llq|hnW;o9yTc}4-X?+C*JDIbO!v4+PPz%$`i~>p8cRxF_2C9b~9mC)@El) zhb__(c@_nEXKTgc08h2GMOuzg;G_Wv2fqWI^S3=mt#2c6Lb-UPWXq_W!0HG!1zKrY z*7Ay=jm9KORe(-1&OJeI8S!#5qsatLUSOZ`PRY~%ceE*W5AW>a`uO>)(<|@$v-9hZ z@aW?5&Hwig@8tOW`0^iHPyeEmla_yYKzqsONl5C12+v53mSK%#b4=U(w1wHHRn#6r z*!w6a-OA%}1u?m|vrmicoA?j8Rr=89h1B&pz(+&?cp`%zG94U2Gzk|KXDfv4)np?bF z^=;I$y8BlWeLjz`uLrWGo>}{pw)`0Mxc11_o8|i5@(xe)ogj&Jic99w`gbgERV-tG zf~R>sj_>F~ueCIbIE+V3g*ewSht*YAD%ByIaU^o`jRmDuAwoKM;6ZbKkVW8@VFzxO zvrnGiJ2|EN$>SmPRypbAHNRoWDpbw)1xah9zsqRAE*imJTT2!MXuWHpJv}pi&0v z3lpIykxEATGRj2$QxlRO@-%z9bcTu3Cc2O z0h&ev-ugGUBIg-{r<%BJqmfJIIF6_&1ho}rsc71fmJX)Xqe>{AA`zwXYfZ_IQemH{ zqZDSPEM;vVk}2?>KO+Bxd}QWx6{hXWXDu9sd`6>${Q7H${Htw1+8^MKVvYV2@{(sn zOet6nN&pyS#PU1FP!;Rot4=wR5XQbqQ?sK**euN#FQV*-eE8L>`WK5)Qerarar z2(m*|noLn4?JzJ+RZM*8oL;qasgSd5UUM2G;Jt7}UT9yrH`xiO=~hpE0r&rFbSWH6 zM(LPXR?J4@W^?bly#|(drc07os5^}jF)Mi7LMJQ>k%7|&&(JNGRDA&(eg0ZHqT(A^ z>4K)FuRCJ(7P;3(#qzt-@~D`B*H|9Vie{(MD5(O~HL9@9jIQANyNu0dM$3|~nO`UN z)oPuq;DYYS^Gc9FZGb>6GuMR<1eGFqU}32UcoNLBj3s%zKz)NlR#rLnnGj5nP3BL0 z%igh7;mnCC)WTXLz>w~?mr=|d;^n?Do-2TOwvP6rAbQ?UdKS<{PE^k)PDW0?~Hg%;%-4>|Y zIzKHgTf+v%|muXx`VE5aE@{|tJs3T&&CaMs0S2!weq&yqc;aapWHEDrR-0tyshVg zWn>mjra8Hz30y$W1tZt5t{U~X6;TsgF%$7`yRrD>+#a6>2*uxal$_p7%TH(3L9vd3 z#Dz_`3e;no@qe=*XC>40Z`(T#YqsYYY*Jf_PGHK${;oC_mJS3jNv7HRG>(EOhnO#s zvye<##X^0weniF=*vIU`G7|Ay7Kxfm~C$AX2mE$IB1sD!IWRn@aVS+FkOclq_kgC7k z6Ad#t&-u(mznRHGq6A~fl*NEhK`5L#$la6+hL*nw&%QTo;jccH)?76<9Z$9@XI{Xv zY6rItZA}`ILL%m|@NZbP-C80Bd^Y0=N{=9pSdu#mTayW?$ye>uV1{9HQHuqE*Rz}wllQhn0B-w=rv6{fM08z-=i0(i@yQYf)=k#nEzOIt#M0)- zwBWY5xofJVF<7OtT6%;PEqsoJ^9hk{TX1dv2&$lJmQov}zED+~9;7fdZ?x+2sa{|u zbwgiWl?r@X#o((lu!1tMjsnmocUJ@;UJZVDwH?A;rM|z^B+hHSy!zR64dm%I-05~Z zlYU!w!I9mo-(FX6ZME>VDb&lXHjGUk=M6ZlOA%MCfBxy~Ps66OSsK%v2^|rMsWw9@ zrXxZRscreFHa|;FqEDvc`O*6SFoM4&+|4a5UDp&5+0EgErK^RGO84SModzjpMeDPbLUzVR;1yh>k9v_#;safD+8#vK|w4rIb476QqA&mv$G3oR=+MxpMV(rCCG3pz& zy`nI1yU5Ta;kk0QGVkq&K*M@)v{Bl8meE|c4-K3=yDE4%RqGEur|Yx%*I!||sHs|{ zS;oclk>vdU3QYX)b>IAh|JwKejK^&K{l5BJvi}cz`+Md6zqhwP?0wz;KgZ8u{lKWz zl817dv2h$trgDF5OT}1k$OG>qnQWS zUht1Qng`PjTmEj`d(@V=%&X9+BA|1UF(IUqmhm`dSDwJeCxUFLhQGow0mM{RT5?LUVRU7snT8wW(yi`h0UpRl0NzEoJUMb@F`1xdYB; zC*`OG*SpU*d(gQ~?OMyG&~g^P&Y@2}henfB?E@DvP3l)X)%TxoE~%?LS+$qSx|dF< z)p#vUs^8nN|JK^?*WdE<-zZALD49Iq7`Wv8w>Q|Y+JC#d-TklUzt8de4bFehaYFr( zkAXa93X@NZ&X(TUAjH}r+WJDwNB?F)uB0n|R1S{baNb3(5q7;wXMg@}`_T@fIBUr# znYMrYbLitu>+0G4_cgP>kQ4DI6v6)i=i+XJtFmz@81yMyZe z@BU!W|9bxa9KQz((=_8VmQUHd<}qfum05+TS1Z9M_SzCD z3uHwqUhnMUh@2zxBzoSS=;s@Rh!TWVow1Z>`sAfDEU`R`0`%OMFH5r28q3te(oZv< z^MJ=k1nPFFx9EI@UWt5y}V#%UF89s4?b>DU!9^s{vIms*X_#70q5p zHLjeBv8%vlW7lkbu7y%t1{RwU-w{DZ$j5r zM^SISuNt^S|LfKCzkaXV`|AJyS$?1RFui2A(nmI@3;riLyt%+on($*S)$kr(DnVMS$61x+crznN+?LU6$?)>j&?1 z`ixgTzs+bKOkY_e>^jn!*>Y9?7A6b+?X~G6ED2K{CHiQHM?m{f7$rH&ZfU&Y4A#Y~ z_uo^V@JuzRh%Hwr8g_kaj6=p?nnuaQjDz)P75mvkW&tvNS?8hl;vSrQaG`dtB+-gd ztRL{5W4Vm>LdQb_|J84a{@2|b)cik(-LLxJXZbnwKPSkI^JRe1`+%k27-+v1a?O=S zt~s%hYufDt4X`z+>AHTh>2b zxIaAncd7kvx4T=7{~ZnnU)TTV_?ff922rU^ZIs~mF!|%3cy<$W8rF|^O*vw=oF$4R zF>(RWdCr}{giAh=yC@GyXUgJPXKTyFI4$8mDkvtY!7Q3U-R4pKXuCQLoa0qv>#AiM zno|}PAWJHb0+!`l8=iKF`sOV;dR9yE6;NNWe4ciQ-!7(6-Ze;CMCCj=zN#QGt|DVK zHAL|$)ALUlTxX5;1}q7XCN{QG?gzFYrE*`5laKRjl>ZOb`iftx{O=9+`+I|`{NLOA z>i_duewO~XS(2Vxt3&{Oz_d%`g|d|0LSa1Tjc9OlN3&3nfX`BzN06Rzp>r=G#uI+` zJRQHL=?lcjyNZ5cmo$&K6lYz(>n<&Rp0kXG01VX`(^#;MtKdn@gPV(tkCqj@#_ZlL zh0ZRHU&DK~J$WhTalP1^lw~yM*>M`dR(KqSkj<BBOlqnr1gpH+X3jrN$YZ%vh3NXEeBpl8LK~ zs_zF!u5xED(O>2n`%$oqESk}5p`cIOC?ALuz<2zwiz{ewbBX8kOhB!@`fPY{eg*uU zLd&l+nuwHUECE2%DyMqFR_E)KC8XyckUJ_MgIWY|>mf^HzF?s}Wuq|>YFuD3pSg4uJR_Z6f8X!=UB7270t@uiTZ7a;kB)l(s*|xTJg%knjC33NNG~gQ z3Fkt1MmnkN)nazb`j^kN!+X!P!}3f!Tz{rLYD0S6d$*+5EjLB}y=q%_z9r|3g+gUX zu#!cDk-$kgVwlPy%7H^YS9t>Y9Bxtn736QvZso$KBYw;LA}?U;LaT_r6?>|G|DNhs zdaA#APuYpKq=c@6-nL2aa${RoSkt7^!{HJ)?UK8;Z>-qOgZp>$pwi8QwI<{b7nkP7 zaLhC0C1EUNp`51tmStHKGQwyUM=aA*P=6zcU^&i1Y>TT(5(#jd6)6j%aa6Qwwb5vs zxZgx6oyq-jGvG94BCw)wQ#$i!a!~A@VyoDSM`hKePGOXe{Xmy+NaZT6LcwXGrAj#D zw?zvrcJQ4X9Z4?dSMbn~yK(f@mOWGC*}Y1s^VcMb|E6)>U3EPpol-|@mR!}9+gYX5 zHmD^=L5ELRLmkCZ+-fV8(o8O#_Ck`wAa5@Ni?_7|&PLORWK5%&bev^%qH9ag%hi%| zLgo68LX-kzQ`Wjofbw>+7i@+s%~(Kl7H(}5)x^qEo)K(T>G_tdBj8CMC3EI?95*w~ zqO(4AQ3B6vWgBSNGZ|9uy?X9m66US3F>#@@rYkB)CP<=_DUFgSnHcpr;fW;^i^Y?^YPZ?3ky{q|H1(^%n=I|THKgY*}nh(aXzA(|?6G#e3C(I|;(@2njCWJ+h zsdU6hn(^C6Zkk|4kgt>#+R)+|zRRabZOY!&I8woo{AyJvFMUl?YxT(mVUhsLQyU-_R@Ia0^5_@$;QkZCv z?2L$c#-PBM$1%SHlKDL6nH0iNLZ*41ilZH6dGzBbx!Eb_OSJwvA|E~Usqyf?CHEQ9 zkg?K{*sxq{DL+`#Qt`!YaMa)3KeUqylyY@^sHbJXg{1q=VnEhX{<19>Lfxlpr4){N zCdVlB0tyL6kI2*V1W`e?JN0~9Pce#xEmr=}zhMjfDqvZ@`e&vxx6eaWGzC0K@{9&I z-<_Ok#~8dg&@a46;FTP0%%uqIC^Tm;l;rBPQgypywD9wqzHeb^M&Cb#Et_~dXXTtk zM)QLt!fHP`nX=$ULD#F-D)3(&OT0jqC}-zyAk)N+lm*%nDG%Ko6D<=sNuaZxAX9!v zA}ka!O#djRG=l>?W?DDSc26Mbu^=N*{&K0rPf?sK@H@yf>%%^USyYf-C&Zl3hsrAlpM-pWWQ(nVHFGGZ~m^L6%UW9%5QvD`m< z{vHwYH04=!0sI6x`I^0?vF%I^rfuPtGr{a+=S@RZg>fo}R>vCQ8)F_g;{so`4dbz#?8C z%aoI!8+ICLl3#!AxWun@*Kb57_Swnh3-Hs_8ubf&t&%v?3FyqYX!R#j^ps?@8p0q8 zP4sQc=A>uF$y913-(@sl7c7goQR>b73!sEIkwv@|%cumh@QkQuAafFJd`+o9@uNhG z6j7RUC^@BqAnA}qxshlKau>xh!5q7VoKVEgmay>2i+QHg=-Pa2!xOuxtO|(GIAb(i zkdfRylptUsm`6AqGH@G-5<7`AnbKQEDDWDf49vMaJmf5nL~d%ErvaaV^wHHq{$XaC zT@pi%PF;05G>_bRn?J@8bj%@+o631c*n9jZIIOd<0Ch!SuLQhlD!ZleT&K%a(GRv{ zRWS=~`T!mkbei&Po|sA!60*^Jg6aWw9Huu!(@xH{i!&4}YbNrEIj$}j7+6u~mSrO@ zm{x=2Lr*;`%L1gYUNtTIgptk2_BvuT&ZieV%eTnk!TzBhfh&fy>H6dX+{EE%EFYlL z5`IQdK#}8%Gj&K-mS0F>&0*0nR2$^yO2Ff{as~n|^|y!4=yrn&-kMrdoknVzd~ZJN ztk<5RAy7tJ44lmC`^QH1Ys{?Lij>DuusBV|JVVQ(-1$4UfdiRV3S2tRi%i>idPP~3 zF*yp77GVW3n7Sx~l%Z#v(D;rn1PGsm-Lec7MSb`Td0=8UBfi6u9dN?QcXW={9js+1 zO7e{FJi)&_t4>eXiL>9s64+fazhhPmkgM(Mb==9>i_6ziR9M}nSPG&r^FWLg3fI4R zl#mYTR9GlcaR>{^MvLct#VTb>6czJeF8d<9?9&Ddr4C~*_ieNw>*X|7>Lr?sxDe>nq^6upBh;)uxfnPdx z`J zh-R{gq7FogH*j)OoE|ysH%;v!mi?wVvZT3l0)qkzFln#LeRWL0=Hv;M(hRg0P(t{a z6yK2vK(d1fRkOCz55hNM4K566tS?LXm~?0ycR((w8vr;$>r}w*1OVOw`OWTu38UOu}dkD-8G8 zh{d6~d}W~IeW*uH+#m4=MXCJUvzB6g=X)p~OlN6d*Umrp7QITGX+oQ7q`Pr>}0=;~3k}))(Sx4C{^aAy` zjihZhZb52Y4zuk*D@N!0+OdPv@Hvg;VJhovl^o#hSAM+jD@zmU0BfDCEgctYStYgc z-{4JEt^AHwDJt@FWp?%+4fl2V`f2V-9zCBKdGk_f-r~_D(8zEeDYZO~8?+7ddn9KUw*x}a<0nccaGkCu%r+cfi`!$VqT2>d_!e!R_`Pq5;`DY< zp60g8wrwLT^Ro8P^F_@H|L zbWM367%`GDynRGlHn~aoUChD>!=o$(0%;a;V}!`&$u_T@U z6J(x(x%COuL9<+$(P%8c&Z5bLWr9FQc^W%A9ec)7RPw zhzKc_>`vPzQ+~&886GicvLHN}a5*voPZAd3<*=FoVI;D7DrfO1i`W?5fS!<7MhM97 zLKZ}L%c&2F851mrF|_Ezmv$Yu5yc4$-a@KIOTL8Y#27~7G0XH+)AEmwbC#KLuMVae z=C%Q?SiXZKA|1TPmI|*ief}uqEV`R=au*4(B%q7&LY=+%T|(6L_sRKL#r;bT9=d1> zh7G`?UXVnfxx+mzu(*v^Fn5tKr!d(k{FY^RS(M{dScy6`yG*WP3{E1yXwj+$&5<1x z(<$xDvR?!F&V!JMGK799mZmD_l^-MfxgqvOd9*L9%>8luuDir{a+Hq!98~_w@k-W@ zFRJZS0kL#hHjzGqFgZ z(Rg(?8vQ^*Rsozsxko4BC@;27q20v{#OM~q=0a}}le@tSFqoqVXY+wH`yI^TCp^#Q z17$ig!_yg3HyE<&3;ic!26%B*X=njMC;>Rh(IXj8Gr42+o4{a;b=rM5v>`G-gW}kh!zlw1;)u?1g0%AK z`s6}?k-uL^mYb{PCzvu0$XL*D3FQS)o<*hsdsK}_k~BPqT9NwjV<@q&TUS5HuE|@K z3ZIw7Jbs7fZcqD}sP!dh0z4lLN5Rf;^xyL4n{XHd#ks*8%?Vuy%*F7Cg%v5dN`AtB zNOPY_ep`5Q{Q@K;3C!Q;$Jg@osc+Vdlps1SsK^|qfd&{b{09moKGj}(x>G~L64d5@ zIU0_>wXtaF(Y@Koj4=KVO<}mWBpJuaw8T-w_aK%90;)<>=BNUrX@<&n{rbKDn?Pj0 z!Z3*h2%*L}2FN>DMY5H#hx~vdkjB{*%iTp0OG(18IPi#x$&v`scwtM2x0o-K=*fs- zJxI)fKpf~^$xEzAo&fF$y?G($D@Gh(4kkj(6=EFFx5sKg@~Q&MmLY2w(#1$7Btq|@ z^sCYXBHqaLF^8XWM=`Z%HKPMbHXkD@mLEzu6<^Jry{Ie}nptNyu*xi2ZFe)36_sGd zXD-5`9r3m|(%OPY)D<{ulxLRwT3&&YG}VS+a!V2z1-=uW6p0vz;~2Aa6NSfBH)VbVUJV)jGCv{dV^ zJ07Y{!wJ|?p+|;h5j!hbE?RF@#Wn1*ja#F-6{U!K$_bqV);(c5hz!~=MtC|a<1F5-0BZ?nEJ(G3dI`D*`2r^&`XwE~ zIGq@SA4Mt$c3-r;&l0rSkTtb8m0UB2DR`bDnt$BVopPJ0cFD%CN4Mg!r%Um8neGU9 z@z|(#Ubu=Isl1Wi0SiZNE1Ify$>7$jJEz(?E>A==DE4GL4vgf1x3sl_)Ar@&$^fdu(U;}s zxs9yF(ObE>867>m)-dW23VL{89;QJtdof)Fmj|3kXgk9_@m8!Ds)rYI)vi<|kOWCD z6H5?a9w|WC=^=CJ66kXv^AN|P)O#Th3WB1Q@Y5`YRBSnU#UrmlcsNs&q5@6C2%Ir- zQWQ%%gcOKrCCXmFh(cVPsVKm6v5Vv_;uB!X2Uht&mhhP{>B48?`z4y9PX&_l!c`xQ zb01TwJ)tN@w=m&Kc{0v$EafZ+=7`Otc&&;v+lshy-)mr+o9{YRxpoW~hy$=ulPCE1 z41u$^75v`o2nCrcVM+6HtqBR=ASafrzLCzy zQRUM7rWB#ZGOjme^a)v859a|}5-3RXU@QL%Sgx}&w#4(4;)GAY;L?I%LQCikuIj20 zw~%?Q6x&->%SDZiw&E~t+xAP^V@DK3CBrYR<;deh+_3S2gb7}D40x6#C>CR&|5|Dc zR9!U&6_h%UgvQ_r(C{oLcT1r3WX3(m#DkEd2@%RVO!z zQqkngaP08VZuxFn6J04!=w})z=L)pSbpxzL*K_hliLIAPF{Vc@wV+_+Wo3s|Cgd0t6ER7SwOZ$!aUxziBjf0nVC zhHmTwF~wy!;F|JvtyP=2h9(b)2Jc-&x?IB=sv#(z5P1(qQu1I>^q4U`O;BWlC7iw0 zds37LM2P_EB0=t2irv%-jnL|@*z*R0R0W<2v;~b)-ql35>Nu1!9>XyVW0FM=IihXW zhlkS2AeZ134P@Zmp>ns7(ysVX_2v`8_$_8S^j?8@d>B*>CUiS3gVTPUr48}&>nt4v zN$KcM&d>BcR(?{w$}bS4BcK+<1CXdJ#qw`K+rtE#pOY90LBagAV`Q0k0;E*Unc@0{ zFqtmcTv*6qQ#t`5(o4%6jdCHVRl3?RPu0N1t{F+b?&px+ls0@NT;A4Cm~#$0REm~ z8RX=jGi)uB7rLBQ%c$#36){pDRIN+SVg*?qV_Gb>j5cMwYQ6CUs6Mp-q@ zyY;#mXIY5W7-*Fm^VhB8BF3{RFBx#6mP}yib!Getu;Ltwu2evss-RaP6Vg<_sC{Pk ziivQb3fRcx+o>cc)I#zLUXEKtD(W3ONk*)9eovoNBBc^jcDdb0Nk|t`8R*ppSmyIq zNxn!C7~qtlutYz_tI|YN;Jap2UXKu~mIOJRlPuvE&`Io8DsqDsKM883gj4Y($O`RgA0yLrrVi1I`sU4MRpaV!m*ye$WLxnQCT)*;gbd)YZI zU=~grzao^v7~g7W1*%1n;Kr0`N7e_pbxv!OiRKLm`RX+P>2B7ftnsX`w}gM11xy7# z7NTp-NzIfDRLQC?wi%eqSNz~)1d$FfMX_!GVIEf$uhpRUl^f1_i84#iuaZZ)Ou^(T<8$o{!{0cr7((x zupbl=D{(5VOjtm(SRT)9>AzsEU8*|Hma_agIWeWrbm2=;wk8}*jWlT5xr9kX=09a| zY!3Pb2J%xd5#LHKXqCD>9UBdVcO<;gvHi-eOUDj7fMB_Gt8V?WlKPMcQxtORJm!w8 zDDzep5sOs{)vP@d78s0S65Zj5&)&Dgwe_3ZJg9H;pi7(n`W*(l zob0MBkKXPDrbu9nRh-5v*|M6{OO?W2d5e zZ|VRvFCO~{I3tYzgekg%aqNBR-&ut5V4_R7o61+U7axd_%yf{e+urG5N&0;roE~xx z!xxHK1 z{biO2p7B;zeT%rVRB}d>sv>~`A4H~*SFxaC4r_}XTBOxbx*l)B4G-t&kXb~b=x_o7cvcW$#o%fTr|M(( z9O3EgEoLFPMRYM}SfZt(WqIA`N_MpeTx1Hm3$z5KMalsH(o~2H6ip~%vz}4=E3IiT zokI5;AZbd;q!b!fymI>G7IK)F{Nx<5+)A)&$Ym;3L=sga?QT?Dgh8$LqKKzj=}t{G z2SvBKw}T}x-kz4gRJvS((WCD%nO@284?=EHjm2adsE`m+1S-)^yLhKP9Fyq>?I4tc zmzm&Pu%^rxz)6GoqkrGsykcOWEsf4DZuhjok~!VKVuAjs^jIm0stUWCS8P}XaUV1e z8qqlDg2q87GzwjevQJcrU&?~Dwp~0KjV7?z|Tf-MRHNp!k&^CJ^ zW>jjLwt>l*IF!!i(tUg`(;w=|%@_g@==hEQr9E~v(El0?>N=v)X&eLB;zL!}cMAU3 z-7GwvOE7Qs&RE^NV&{n85qhJQ-*q3nn2FVss8Sx?dM$`f>qf>SGa>s`s}#Y=ie6R@ zUPhA1PdG;JYb;P4fMSQ#v{}SJGkQ=JQsfBS?hJQcY9~>drbnlxcB>t!Ac)f9)M@hx z05UjR-i6?9hLS*L*{SqqMi>{+-eT%>Xtb|wc_rK!GhR`Xl&o0iM_HSzHtY7CX_t<2-Y%61IWYs-8o zWduubAb3Z`B1+VW$yC}obzbgS;tZ7lp1-O4F+7RpNCAaj>#BX=3X zEkT`7s5JwxsN9{=s#lDa*MwWIHAs=Ec8B8l*M!_9g_%k<`9vi@tg+%kaU9%;+Kz=x zSuxn9N_BjSE32v@j!XGy+16&!#ejh=l5j>zg8x9jzG9-bxd*hqD26W`B%I@HwQkSj ztdbs%vk3EQP9!K^S5;`GIvrd_^bMKGjB%XeD@1RRyarRo1cGB6{bTp#-k@~GQQ^Le zloghBw-;QugU&1)k7HEM2cmsfevu!+^z!1wIFVXz1*)pM=3QtGqk0N{J3ss1AR=>! z6QC}~r1ObxMfOd`167$^04Jg`1=5&dRj8qEQGNCi#OiTGRRIGTHuK*zl-Y`@r{GOC znIH-n{-cOpFq3{ZHnuXrw2Jd=LO-f7oWnfiL^;*Wi#$%s*lMDPNa3VO(lCD)=*fcC zSjdI~`KhWjGgIJnuGewR86PzoCoqK}=7sZ>d0|FK_lOgf?^cdY!p)GBQCFpdPy?yO z-)+2`6r<~#yh){nZ9LzK3`2T8ODTqq4o8Qh!PYR(b){El@7zMV-zF270#nL2Q$pEF zHIwI$0@eIc2IJiGX9%tk0{=PsZ@Et4EyOV_r;u`T)=e>=W#eH;<~n!!w!&n49Fy_( z95RmR_FEEWbCf6>=xqaQ9##5wn|de>-RVrfgaEiA)q=>DjW+B-X{b0>A%6q8kMOMTKoo5c2$ zg?_tyClggKKnf`bI02KG+*O+wg@cU7vaD}dt(J!wjsIQ3EYb32R5FGrz{!NPVZjg; z#agjoh@J^>^$z5oS~CPAL|L!G%2?R+@>0#FIO52D8Hh4ubVTGvmtl8wfNFJ)^K?{K zRF=#QJEaAqBI{bCVL}I>5lR-FdfBUc0&36R6^L}{T+c4wz{w44U9!K*QpXo(;0CpA z9tly%VmGL1@5o|9m}0?LJK2*oc1Q3k6EwtdQu#_A`-3_Bq+$op$6j74iw;H6_wbXD z2i}j}{agcc48xl&^^#r}<{MS8eS7L^Ot6#CZCmviYQu^{Howk9L*uS=>iVL z(R;{lYLm*ipyl=yTcaJcEGkVJ4d@q*pvB(k23eGB0Ninb4Rs)D@5G4x5R<9NW~wJ= zTff$C%}+L5z^0E>KSCioFt2dHO;~GpUnjm_X-X1t$)oY!3s5U@Oi*nB# z6K`<(TOzd`Co-q$*;{6`R_362gVP{WWr9$pOU03QjrgD~Z9J$)&-l>%giwU2^rR{F zK-k}UH1Gk7Nc>g*^1=Q=s}If>A3GP=`co)1ZUcEXeB@K01`*$@x)RR1vr&!*>>D|F)+>b5E1fKZ0>T8FV19a z$Tt~fTy8dHt0iMWO0fZ^I2ens4I8%}T1Ud(1nl20uDnS7UQRIG3Z-K-xD*SqM%L1k zl42<;sSug2FSA{^qvM>6=F{X9DR-id4wU;vwnxr%;A(+bTS6%*Q2W2TkiEfvn~t$g zJ77RjND{GBrM!5pvV59wiEgB$yhW!QvszYpJX zf`?l4e9$`F%-(X8y4UXFjnj^JQD@s#ogz`XuneG1tGS9S9Dww_*|*8`U4ML*r5+}*x!Rcq|rYi<|bZ(5{hUP+(FEpC<`4< zUL{z4+;e|@#Y|A!s~Cr_VZ_ZVR@jfQxa@US1EG)6fijHM7wgjR?;OsHi|7+=dWQ0( zvntM|jCNl{Z5(QwA>-Vk{B$_dx6m$F%oG&G>1V8Xf z9G?b+WFTlam{qyWKn06-0$Ozg=L;y*8L3N@pu4vSMlm)HY8A|=C=*1wP&;NW%zL88 z=|X#C)FH>|!gl@W46g>U&y^Sk1;ms@$Mq|%SWQA^ZCX*aa2%X`fNH|Rc|aE7;kpxn z@wDKw&npJF;;TVsXnTpCU2HX!Ox1z8koUC`;LXn$GP90u@C6uW93;fuq~H!wWQq*I zg`7nb36&%$H??;sd(gA99lEHnA5YVKLV|c)_K$67If)6oPx^z;)~&j{n&;%AqP@G_4W zC4^hPj^d{~p6O&LUiK4I)IEp_KXF;Kpsz$-fayw40EiN1j;ooh8IU9}-v!qBT~}SpU9}`h zLOA5c7+EVy^vm3axh}@ftNd=Zti`!`#ZKgxT&*(vUD{n3!%<@r{HrNwX?Ak=0u8*X zxAK6Og_;_8qYG5*yKR=0E1npVVePJ=Oe^O|1$h~z(=Uo`=_`U>)xy1qW?KtZ!33SG)@Rqa-!8>UpkwMQSh5{Q$Ns2T#!h|E*?@O6DO*6@&Zz>(qWMOin>Oirp6T#h*nkB zxj56-;9%6)GeOm)3Zdb8FfXIY`!$6LOCd#x?HBPhUzmBO%qijwq!fiHLP>}ewN=Up z%^G+Cs_IK&+8uhE^_<460Qt>{I1J^IOhR^z6JX(@J&F>Uwcl|wVqH67h~nGxE7^(D zj(teno7xs5mVAhGWHFpf5!HGxb9~Ct)eaM}EaZ(`+61m0*9w#3vP*FGm_eS z_~|UYK=c>FxXEW}Bc^4@Is7EAyAV|Y=gMlncDVgf#8He+@;s|Q3%SbHxfmQpv7pGu z8b|@mr+VoCxIdC}_~|X0AWBhGfP9N$lwa@|JjD=3x~PqBqzd~{Z3Mprwm7-waS1P+ z1f0h;?W+p?TS_T9f#IyQo>Gc}YEay2rDfV9pB7e%!@NO3lqwNAJk^$L(h!OFVxnTW z@HL+k9km4FQb5%ffOUv2=#%c z=ZMV$nDJTRY~qHPo}q;6q_BSNVgaGX(X0gIcb~7B4MrwqDDs|R#5zLO6$^h@uUnhl zR5@8)hg+mloBot`l~bNuX`b;6#zU6ka_YGPHC3kK0^}*8ERdy@P-)Znn=MDTu|Z0d zRKgl97<2%r`XMV_dEF~LNNaN1_YTjGfCg48l60I2W&UhTxxFK!ig@k8yF>PisGZsy z*sq5jTh|5)!(4It=buA%8y>wHz1kU+X=e=v7W?ywh)vBGRh?_x%A$(|U+cT*M5Xpc z$5K3*2$4KvJW!6sotu!A-YRG0S~9BG#;3_7NV2(V2Sve*q%6S6w6a!Ea!VG&Pm{@c zHosPde<3M*cc^qpXR1mmT33A&sCNsewGH?-rVG^FZJwj30VS9xMZaf=aV7=PVkLOM z&f%v^UHAggLUyWyYzt(XP6SLLA6BG961;G3V24KtxNKL+bP5`+@l;Q{h^a(Qwr)YIpnIM6+0-4`ba`J!R zJoXm(0ur1Ekp=AfrbhoA1c6Uo>PtcA`xfTtz!d)nqUyAIyYu7*C(%(c08i$K!w7PC z^aKEPW^}UH>9^MPo6cK3%rDwRBL;$M_6U3t$eX})4fuTldMNfvL-DuGv$DtdY@vA? zLyp*Xe*JVy7Tanj<}j6xdCRsj8jVH=dwcTV(P&iucV{%({oBso;r{;4?%~eC&fi8m zhlhvze*>e{cGdr6j6?dj(Y@O$ckUZ`JY>XWKF(!JO3@_7(-|+aO9|2>=uvM{VlOo9 z2ilRKC?QqGsdI7GS7k+bOzXu_ZeVW6uSIP4D#vq!>r?P6LaAg7H%MF2K?cTzaKOsy%GV^N+HZ(>)0AtiDAA>$V$%AEkk^Aw(g%p9o|GhJOIU0@zmh)UR{CgzdsG?waV#S;q{+f-E z-o-=x@7C}={1>6UD&U0BEqBtc5EuIZ9H;3qofG;46Q^b&;RvZ|kR@^y6BG@G;ChC@ ztr!D!B*)BJH5SMaCSZ)foJ4qnQ6$8&m?TpfMv8%(gxrC<84hQrAtt1_g|h$2N!H^CEWbkj&>WiHeQ~U^`=UdyEs8&q~WT zM2p=|r_8SPLlCMELNqfqw-sac9+`h|ss>}oP=F&a`k~AVrDbA%Nsq?a)Jln6Hun*q z!Qd9rF_BsGN)`17RS^s%dIRtrur$V825oS2ep+?Fma~o}(lMC^F}g)D;1q_)MctUF zJs|#*nB1)deOc6letiv`Ovn#Sj4v-gA#@J8{|ZBY`}5DrtT$bZlp2s9>TgKOz`HSM zsmwp7(=$w_BT6WSz73dX8CRUHs?Ja(%W`GM%7 zl8&&9jkaGj-Ws(?r4=%1a*{t3{FD+-LJ}W=>ywK{`L6CLStHbT(+CO#%zKsDs|2?! zs0&2i;UxQ{lLxxAr)FiAadAPE`BKfsu0MbCpQ0 zcDpH~N_)G_O^&gnnuB8id52CEGQZq1W*p0X^tES?K+$YnoqMU%<>>r}wQ26H zQTI}8W8tT@d~WHoHK4af(fmP!B2l}U8d`B2-@%1hf+@vB(!Lln83@CxL3BzDz;hWj z;aP?4l|X@kPH`)Jazlza4q+Z76iC)0Wv=5e6vKR8_CIq!n|7*>XW|k2PcDfor_wdK zK}iRodG!LKMQtr_vifsUWBCHC5HPfxu|rk7OC%2+P}I*~c~I~a@2b^?PPTD5q9(<7 zkEP$WHL)VD^xbt)*nayEt#V>A^IP*_y{v7sUS6*>t$1xkW$$OQGfjbQSwNlm2evDj zw3HaqEHN2B9sWD8&%}!q9f7x)$}wDgpelT>7nv$Yy&pO`yk!w8aq2wLtc^~Sk)U~= zf`o8%Brn8bsZp4MKM*Bgj$p#n)p3%|$A|*V08Ri2Ig9~CDMid=r#dnzdGdQPs|79h zznAXDqrK6{`i420mjuxONDn;k#YIU9RKwj|@+r6`;0B>oC4vTH#P1MF44r3S8)WH} z!YH?P@EqV;WtJYT$WVOabOJ2drdH&sUu1syr+Ec=w|;Mlr%B$a$5)g#U;T28`Fg!b zOEQ$|l?|a<#@n|pbk34rYNU!*$zfv&`dvTG_Maww=Y6a{TkZdMM|-2P{r}#^{(n8s zz3o3&q9^n^4`}&*l~$uFywUTjm7Rr_Ui0r&F4byByBV9-+Vi!xWT>=N5R+{dQkdF7 z5X%KBmqhndWj4oC=wf}_%iMM)wzoAlGu_P1Fwu#4wqD_*xhA|`2fpH&ES6JA9j$WXD;2nl#YQIhZk|s3A1W2fvSL81|KGtu$^T<# zbg;AG|Lb^G#{d1=HkEayjm7Iq9!|f6j8Wk>w-TDE9j!+z`|&WfD~>zo;C@s1r=n%>;O<)UqkeJlL5Pe1O>S?}%SnJ>Mo zDg?Rm)bF54A~K;r_31SAoD1bS*_%+TWwfv-ES^%iCxq%Q7FVSDlE7}Y_xw;Kmo-ib zOML597kA+MuMyRO%PJ<74@=;qPVdF?2+ZQ>C#Ti%e>Xw<{UX4$@c+?%<@`5#xx2gJ z|Lb@>{J%N5e{(5)i>Z!%dGr?_%kC5-rkXvyrac4WJipWH6PZdcb|%-jiOC{fz9V99 z_P~N8nG{3s;%^{=Ro(tKX&PaQgD{4SJ>dS|dpRoa|J|L9{J)l`WQ?Sz{rKW6-|%); z3&(T!_~K0OW;Xlg4tV~tKUtfD(*7elwe;ugw-h2OVUG{&O|L+{`9qeuP|2m%1{@2rf zl5Y-&4A@(?VXNwhwJlq@N%@tQaTEI8QB`gjb^)Q-Xv_Z? zg=$zcaZEzU(L1XxGi1yJ18FSOu``V@>O0nNluU+Q_uUf1Z2*0Cqi}mkqREcw6Mi0q zC@w|nY)ZNkqW5i)yll*5=&k_hd>TkoKAawb!OlN+ht_`smpCfA^pAi1;~$Sxsng@B zlm7&1#R=xC2m#wH|LyGTj1J54-`?Ry|Ff2-v`@{nmoHn(kz9SzUTd#arB|SUx_pG! z9Ipop@@ikJ*$A)bezpMF`LeGLqx!*lbU|JHXs$SjrPtVc6E^i*3Y(fQ?|Z~dEypsg z-_YGRb+3)xY2k0;mTuyfme<5eLzgxIw%qX_GJ0uol&@}%9^p&#p43J#ZH{5;)~^X; z3P6k}C|raws)ms92}Ts%BFZn3y7SE_5(^PeJumHjv<@*D}~y-@ zsH#5RR@*jgI54g$+)3WxJtCg;h(1!^{yJix$WR7nZ=J9VJS5_qVPYWl|>Dsl3% zr;JbKVfJ0Gphd+BSZ&;*VkuWc7xh>d{#X&EC^nnW5#QOgEo-gRk?(Ewa`nds5`2+8;J7KzM(5@ZNm+|@u!ad zlT^rygl>PFp*Tg94SBk(0a%;;*Qjd$wR3Q|q5tc6#IB9d1Sf?kZl-=1#w3ddxdHey z`5PR)27@U|5XIrZF%HkYqOfB$s&$~M@@p`FcdV#qlHHif@Q;k5=IWwE1ArKh;TX!| z&2X%O7PurT_qY7%Nz;%?RpVPcMNFO-tiiLsh#|}q^te@r88d|nCsG+=dCN*Fkt? z{_)8r;u%f!Js2=eDV6*ac|+5>Q@m@kgfzKDl;Q~GMe`e-`i@rW$eF1Uva%MUO!|hP zX8YDwtPp@4M1gkjclLL7B>q?10g6(>Feh|ju&pV`vdIL0s{6{}w0S1&zrQvs+B{$3 zX_fzU=rc;ws;6TZ4q?V;gyKKs zDYN1!q~qsZlDHwvqiM!x`jn&akal4$h9WeJkuoGr@z0cGskRaw44#xOlLDEv`UwEH zh>rD}DdO_q7&G<99pvH6@I*JyBN#-Oi8(PA#wV9=2cY1fwgFVNq*d^_cfPY^4ih*< z(FKYADX>6vsDnU*=Lzx%D83pd)`N+M{{`TLAM9;`5!eD+E&mj7q7>p#s_Wwgh)|3; z0{e|L-VaD#Wi`hJfa{ZNrFO-Z!urw-abMR4tSb z#Z#b?0!Zc&NEHXO_@itS6j$(;u ztq@7f#g8)`O3V_hw%mIusQZMkC41e(mF}_n;+*E154U8Yj5_Vt<7t{AM7nkLJ}9PS z9>30vZcJb_)^3?NO$mwF(4P0w)Zt`m=wMwjO*!>^H34fW;;x9gQr6mNFJe@g{-PGm zw-<0**%E*77DrN&(BeXKNwcS#zsem~Q=|oTvAk#kV}Z1oet)TgT6;eW**B&rk_mz5wn#HiI)#4iu@G(L}*!E1!$HeIGF;RfM1TmoJ45&AnstJPa)6@WGfb81 z708(M=(^N+z9Zp{Jifg{FhUfZ8Zq^0p1EtD#T=(GvOqH~`oe}!o=QKll%ffu6h+_; zE(G+5NTtdQGikj}WUKEGWQaKpLk3VJ4l$hQ&r&TDBgURQm3|B)k+S0i-60Cd_+KdG zxw*MsGitWM0LHNfG8lR$i4YoTyX0D~A}x8^S(xT3tzPN2+iMx0O2nfoHX5K7EbIV9 z%bx4|G^$sqb6U|K&51=SPsQQ}W_5$Ji=meT*DG86N5~55wL#sgUC>2~k;-4GA3SE1 zWL$rmah_T~r!)<%pK@iRBt%mRjaZhy^PMTXPsY+cvJY#fWA?kEM^4GSWA@v8EV`*{ zS}e5s-2aSm5{W+dy#{Kt{~R5Z?SFRnUT)&QuH|_&_djpc;`)ZKfk=!l(L@wCWTRmW zo&Zq20;}sW%M=?udJ+Jyp=<~9*y3=%lzHsNy7RC3w9@|wve}qG8nLBgfw$2A-Q9}) z|L(!gXhZ+k@zmX;71M6Zc+wO~1%)_z4UB(gOvc7Rvwrn#|0r`C=hxLLb?>tj^4SXo z^F)QumY{VbrpX+P^!Pe@4JewTPnRf-VTg|77!02Of5RWQx1JAw-1@Kok_|l@DBxi5 z?DOuQgT{beEg{HD&3^%$W=RMRUTUYgD`kT<2nIfS`mAYeeK|v(b%}jg$^_9R1dYW& zyT-rJtD~_C605>Fpj%-%GRAhXy@vb_20H^C*AC57-nrh^R@oV^;=-UV(Hh~|l0~&( z51)_Dt=!xHqXU}variL%q`E`?{8Mg0z5EL=ZnYu508aiFBT2nM#@8l_-_g^~|4|x~ z#T+f4{;`Gs?;h^#SMq-x?r!`)*7DTx|Au5+Cf=Kl374;rPDi&T0ON&@a6(BOi+^<`xpe06lV#%ddS^8HyBMSpG9()$6F?GVIxjUKiBx+_QzaGK zCjys9D>s;qpf^;N`3%uJ%n*QyX?2dLGY+PR1IDxP21J<*Tg7K67wt+o?Q4|Ce601{ z3UxkHD9S*293Pty7A7tmx|MQrL7Js0O0M-UZWhec5v6`-f;lJhA|jW2M8BV--#x~i zd2PY4uQ+TN4wMfjPklsa?dZqB;C@mC-5kFUe9< z4uqkpS_nAqs2>5b6yzrr{Mo_LkcgPIk|JNUMRw0Pi=S5zbkS>ZN#giJ#xF?Z1Y*gz zowTzRg}~hmB_KcP>$T1D8b!=?y@~Upfi(DI+1!cmq5P7-v*ESw%QjZwSRT4 zILKx>x?(e#Z(^C<*=FA}ZR>#irGhWk_VO!5{R>*=ygu$<*(Y%5+aXPn{z_hN{z!k7 z7<)k()u0JC+aoYvoOJ30Jq$D{3nENkZ}WMoDcrVxJ%_1NIzr=Y`uf@Ple2eczrFwX z_VmqfKW}wd$}*JCF`YIo<@#t&UP9tT(KA!|>#M7e=O5mlemp;Ze`-hX68QW?$=nS^ zCL?+3D-ioB>D=6pveJdj_$hXlv^^B3feJEukR(pW?@zBTj!)Krli|9BrmhY#IeYC0 z<*5pPMGFsJH)wGLjdJ!sdq*?@DsK-TCB?^|K-&fXlqIsSO^?!#|yA8xH#*m%dG z6Bb=2u?~=09FL((6-@3m&o{4_zMTGrFz%b-Uw?aZdU<|&eJXay<$s)=oPPY}!&PM{ z91dTC+UrPAg;AvcX~pc~!(~^@E^1`^4tv81Y|&PZWn&ff6_e!bt#=RozrS6ce!Tka zjS9MY_Tk*Kdz@ahLGIcIjuM#gCakVc&yUZqyW!)5))fe0D8z54Lm<;m&C zpD#cBcF_T+TDTs>+@?<6ot=7nexFS*aPscJ6QYBegt&YG6cPqoKE?2dhG_7V7Vtk7d#MlKTVf4U}jpI2Y8P7ky%)e0tBm9>g zpoa0;?=(CBOdyUkimqoAu^EXYyQV+fbKpDF1fruBSQ+qEoyA&C)M}1CB=N$30?H3L zJZzLe#rF=#+(Nn?+lqX%rCc$nE?fYk`zwW z^_Hi-;;Y?kU7e*C>3Etp&GdMh_So;50F9>|DNjFauJ!BhyYu@As-qZ)pibXdx ze=igok{&dqJh&%1%OyLg3vTaIuG#e4)vk#z#d*=RHuH7XOM;|v)v$FcVP0jtU&V2R6h83eRpfj5aj&s+cv*7o2&xc~|~ z7=JE+8l#y?4h*^Aw721lisz!d4VH^eEZ;GbqGXf_@@ZKT4Q9^%@i>nhwXK{4&X&_xhWnP6r_uS;*sI+A$A^7uUke;`W09KnPsXXPZDj}cY*uK)=-i~&U{MGPfe3zZR( zdn`{MXtkj2vfh&j9f8r_$UX*Rj^@sO3X}@Vf8H|(bUCKJL0{&`=T61VD`vR5vMtco zY{p5PY74YGm~i94ef{prH$mkyq55riR=qj5=cnd0`E_T>dr#4gKy^69Hl1N-`jlml zTH5D8tE|s{!ZG?lPmIfYEw;-X_?nJ;&FGu2{$ri_dc8=IuTM6FYMI`%<2}B$9o`z~ zO;M#I0=+Z{9rOo{W4{t*f|`f7GSms7i!KYS^-$XGGO|e%_02u4{vRocBFtze>FhX* zrl_|gNSpuv-e{-d|GCNkw4SHQY_{zGZyOoD&DX>mwI<45q9K{5BtiPL)Byr0g%6OS zIu?ba$wjrw#^dC^PM*e?weT6t=WXbk%`5!Nru1S&J>B$z!^v?8nDwR>)Pp#jY*>)LC@ai|%U^;jj2~;{VGAe$!72|9?5!t>ph1ZQ{SJ=h^UoMT0wspZgxg zfaN0FHHEaRjA9YuPF}S5gWTOO%w6w5ci*Ed(tRI%Pa??!Y=H9cZDKrbp0Dt<%71Eo z-b(}2BL9sJclXNipGLcT8~JY?Po48iNuA&1tmX}=sG=Oyvk~JV^oX?c>dy2-3V+W~ z9^9_vN2P9Pbu^#Bpfo69jFI$L`vS%XdngHA8tid?>H<#)Jbbp)LN-?75(4-{wDwTTAo#^I0{*@F4&AO&Wxs`;MqrT^jaq9ZFhdKsP+KeupykoKS%=a z*f1n>sj?0rwK5x)+S62}DI|F5@a>HY{y@iYpUH5ip}0>}>IVoF?SbB@8_Tt}F*sc6 z=}Q0ST6lW^`u}q0a8#!M2cr%BU(54#<$s&>>J^5Q=`bV|5kg zE=d_Yon=%UTi2~|cMDE%cMZYa2@WAR1a}MWuEE_c5P}4^#@*fB-Gb9~`@G*6_kZ{3 zs_I=^o;BxO+RR=2b4T9iX7r;Lj#Q^yc^DUd`YaWe5tkFOLr*f+N z@Xpel@(Q(bfqCmG5x^uDx$R1X>)+_36L%6;rH|3m#W51SN8i{uxwH%Hb_r5~b?2!| zh0zF=`bNudDvjq_IEB*=gWvhx2ErDp_K0qP^x##r6x*TB7V=c#Z%x@&FY-$WvQ>|! zXYX-F&eFCm7FYNX~?n1N*KUIC6VXR(<=pkmoN>W?_=vi{{St-{- zZfSYH(=s}dNTc-RLX{#oK0 z4f|%w#59JRMrVnZ@%BK`vuOh z7Ljr{CRJforScfH^kGYkuG_+3+&_4ZxzvpJ zrnj!x;1gB0{?Fz(jD%qq4Yu%UV@S3SRK2Uqj<$M(96u>Kzjo+vooHo5ekj&#%RwNazPY-w zE(frsT$5^jmIL|cfZV?H*Mm2pIOJU!ywxaVa36p^5^%8EV#9UMcATz&)!H$07=raQ zx#U_J`r73`Vv6Czfo;Ky-^3-zcl2O)>|=lw19pouCk zA?6@U+sD0Tjug8^u0-+UcxHM%0gYcW48b$ITTn6B3O$qK(#td(X2pJ7T{4wc5WuW}oah3opKDXqkntv?ua_hYQMs zjmqgC+?u%6e1ARyePT_Oh9&s}_v(0-hQ&na>qFHS;!#K=>3f+~NpLTx-Ttj@6=8Il z(ZZfOytJfoth9O~lH8cuTY#5B?EZRenDw_W^`)<$^ANOlI$3q?Z5AaHcRDBB5vM7O zAr#@zKF0V>ibOn&eSaNR7#!&*<4=UG^tbn#Xg4etl1{>sP+Vyf7IIEAX!+uQl9Wfr z8PL|{_1OryllqdTiBu#k5Xu%Jxe~INgemVa)KgubVhKnBV3Oz`Fc08Ni(2nRg>&B%N;`LeN zd6sHB-jJ+DO8+trsSkh5cRmnjpSr1MZBo-N3NJJou@fz#OE4xz>v71uVHEcNHbQ6; zPg7ykY*H`KYp9qiQD=vw+{%pHjg};Lk2)?a1l(0&*pU#(#xZCa9;$xTamt^F==xPFr8QbSuP!p$*5h0A zu*=Pg&0*8;UGKH;=nln&6;_C6l&~&g{F9FR)w{}0^{phwWviFAKco=U(c7ppb(*;+S#E4mLN;`aX^Rc@&S$%e=q}c)@3T?OZM!ie+y

Re7wVy#mD^f()FnXp%Z3wVdrl1SpzK6;RJ=)5$0JcE3JcGs30z-UgRH17K$ zV4&oSU3T4Sj6H?BUtAAM`2wd9#kr(~fqP&-HNB zd6OR(_~98DPs$aF3*3E+f(0@;($VV@OS+)e%k$Nv37K0Y6C?hKz*r7OD=%uJbfW6> zT50Cv6c(?$3|m^SV0p}@&(!dp$q$K7C1?DxzP}R@D!O4mvjNQrEV@SWs%w@Ur2+c9QfJAg$6)YL zW`4C4#MA9ugr+^we!~Olm}Ht!(4pqXf?a*suL!Ov`MFq*3CMccaB#%XXe_AkYi^A| zN7@^J*T|{V-ohNpR;6$+CSslwWT!^U^QXLUs$Rzf+yUk`K3O}0ZoHT~_@XrmO9mBe z3(33(dKmlHfPiLGpt+fQ^&FzXG{qmOoj^kC(4bIK3oeOKiDa;pB%E-@2{jj?kJ`t8 z#ospQYSL{%NINR7vCA;5R_CGLm2F*_@}fCZZ--guK%}SU?u(t;Os=p}51KAm`;tIZ zAH6giTqB(CmmmKM7|MTyl;0hAfxXMI4du6~Mj?V?KZjRN-`z+$4lsQoKWC5{?@W5< z6jyjW`{bW}^_5C3Wj)^bySUcq?!vB+Xh(c@lVSat=1=<;?n}b%QB>YTDG%`Ge^3)Q zXw{oV#hMJsJiohZhI~%H@t*#;fgV)J+*6{1kJc#!_YPa?sa$iDm`NyjK*vyeQZaFU zql=T`D>-yk1HL~3-a`VLPXatWJrv%6gPuXaQh3TJ2;hgq02ccL@xw76J^b(~`#bMi z);G?>)dMcdsoz2AxK`ZnTWcE|>~j!NgfbZPPbdpq6aDakDKpf`9#%AR1}{7Yny|u{ zJ%a{T5%CUw{Nq_+*UG3n8*Zc-$8q6Pre$mhCk%~vpi*G(8T{~!H#F%)V)c^VVozf@ zG^@4ElbBuk;-v(MUx7ro>zf)a)}XQT~OabQ2Is8jjHMsQJR)9df)xc30#zEx!> zMh1AnXFt2s!Pda)bAjGbT6+rjiC>4)Yc;G!RUg+mKVbk-`Rb(sIJ##!7mL0~Zd1w< zFbrt6@$<_T@*@#j`0=hyDi2$m%gOW}kgZd4hCw=v?SxQZUWi@j%=Zr-Ko-LqPWYFt zK0qyrc0j~QyP+`?>_2tQ8ar?=E;wQSM@CIzu)QaWg*$>NFfps6aIOK8jW$8rZ{Fo_ z>1in2K7Q!_sa1UB%yMdKa_Y}i{Fa}P*W=p4zmtQZGfYrko}<5H4Az$9?8+mBt%%{#B_^W5d-sN*MT!AYCV1Y%O)nSP>?}rn424 z0c|y%$_e;?`S}k@nzJno84*|FpWqSNKr+)8_+g|_7&1D;NN=-VOD^@Kx!N+s=h*H< zOAZ8qR$59PWahJG0*#PRqLz;{s2r05-8l9llJ^)f=t?bf#+x5h?ju*%G_ZB16zUCz z-=S~rwv9kVVf0M?u7n&n!^8Ryy_MSs8z&bT3-G7SyVoSp))_mm>Y+)t5AR446nMnJ z{(PZt=lf4rJ>wK|-6m7v+h41ncmJ_Thh-O$vd~6svO8GUK*)L7ix=^aQ@pxvPCCZ$MR27JI~QS2-5h@=i|x9NqU2N57YfeR>8`o6 z>P{Q2qOjYXgbg3{qm;=`*zX|0T{cJy zi=M|`&CC|9wO*bd!utK01gq}*tWHt{tbfhs2{&V2=x+uK)JNxbx}$Eak0NbD%X`T$ zRZts#s4wc;Yc#bOQ(SjZwS+j42)dr3AZ}tei6po%Qt8#zKy)Tkm^Yg3D)q7tjp`wo z<`ENnhbyP17xsTBY|E(4BJ0Ie?Ie+zW8eSO_SHZtSz5* zyP5w1^X8~?{U@9)`(gGm9<`=jF+rqC6YsGlP#C$+W|;0b$RmBtt}jR<4cXn%ZegBb5y!^Ps~@4U%l&{$1W>= zCObON+Uw*|MYO7RNnEUC>0b^rhVslu3?HoNJ{-CQ8 zyT@B*2U-$@z z*9e?}#sYIsqYf!2mm1y3gw(0TD5C{sH$}QkQ1m%+(+UM}iO@)p$!yy}hH%t?+uYFJ zJd>1tC{}5>nTg*fXPHl1Ys%5h>C*)twQg6r6MflHkmeX{wwF5mm+cgzMP4ThuO%1fdxGi}?y;`W-Uq@}CK($p1+2LZ6KnXY;HLO7O9SL38-$n=8oIdwEd}gY6$qjZ zTUOJYCvd*uh77|iF8rSp3ZemgxwVQb8#999vX%AwnpLY`yuawo4O%aq+8heh;I?}U z3d$~>vSjfhkkRjw>s@QNk)|nyE#XI~d*hLy`o+!TQ4|-?^HIlA3=wkikCf^au3FEG zrW}Y<|Nge3`_YL>66cIdGKb42cTW?ZXz*93I-EL}`T+KmDhJkdkWwYpL_8BMS4rTXpD3bZ|zBJI!A_aCH|%9zXh=Zy1lk_Y5rFMv^@!X_P(M zB&qV+3wj$UK4wL&-Yyw996!<a_Q;iSe!Bc=w@w@WdW1~<$1smAbUbFH*9o7>;=dek!4EfIvC&{2bN$b^} z5#`!dU(%ZFqgx4}nEN_DCjl5;cHZv;EB^uJ{MY_~3j!z)eEQtU&OF)6v)4`0itLNE{Z`3)XuS>(gzXisSO_ z8Toq^^s`l)mxtQL-pW7{8Bg~Mg%3K9$6khoIT_bt(N#(cmi-q7I|_;awb4*b~xKSt%- zYTKUJKNvc4E3zA^ko@taQYq_8{2~PbyLT5{OY?zgOKmJ6?iet>oCI6B^ZXlb++_H; zye$JN7>S$fl`%=>We1LG7`kmC%o@;*Oqb~+taBrB-K!`%S~UAy+Mp1+Le42eZzJ5` zhC^*5ia1A;j<;}LtWl4N|4?j1J4d5V+Q}Lp%A>vj-iSd|GL?Gpzsj+&fi z#|X5DEXcDOvO>yGanx!T-Ci-}$D7Ba8}v>P$1x1yl1glD953EP2Yfpel~EduwTL{t zNj`tskkP)s>?E$?hOJtyU%fgq7*g-^Fd7=>DAmkVG`~k-3{oYK3u)k*O%dX>* zUK03h8?CAk@F`|`-}Cb#F0`}eZebJx@$Qinv=J^AWW5C-7BE;ZpG_TbWl-e+W z;`}Q89b`oAFd1;>)enw=XjBct9s_>fd{F{J?QgCwJ}gq6{b`BS()tp&@CD6@0|v3U zdR^%{6JAE1LTBhG$ti*cnR00cuId|%5P!wx@UHXi$IT4ccGzt-W(~t;rmEG;lgyl}?FE(#vR|%WS(PK-} z66Q{>{F)#0ijVoYw$SxH8a#-r6x~x8wcmCkw0zg+A3xdvXV zeEyu{TFbD?OWBMmugy-T@oi~>WUt3UhEP=YEq8L@v<;BlT24}Y1*7x8PMB-p~jc!%Xi9Wa$)7!;X}Q3WWSJtGxq; zxke;SSk1?C;iI}T`XSu-V^x73yKt%+DP4_+wP^@FZ}7C{VjnC5H{Mi)U6U~N{&y!h_1+(WxPvC7 zphVGnHwBYWn{QdMIp4bR_njF*$A}(Emws5yBX6F{tDl7xrvnsr{e#5T&I4rHUoD;k zECb8{h=YZs91y-|5}ejNffF0zNNHt49te=*;_dJnAk8jR#PQ@OcqxmAyZ9aPy^d2k zh!LEb#L|c(kBP9l*ObyRQ}U{Tc1bW(_fLMJ?_DfwN}CV?dG#C2^J?y)H8E^r=}gGZ zbPr4AFLPQ%M}r<4|Nge5KSwea#`nf{ga?mLEF-{>K$H{L1sy`eDe!Lbr7JL~)YwsMKH2rRv7)?)ICHPH51=|Qp<%xih=}je` z_WJ=dXUs^H->XifZ>bAxYdHb=U-~kAO49HY3h1aRqMSm~H+Ey++K8kt`rZRdsXPxQ z?qLqoy-fGaU!lsU`A3ajW&y*X{QEg30K~EdoL^kp--4O&kh_>R1yi%lXIng=ygBr zSd-=w#XK5+GGAg(R)kSU2YK(e{T?D6u{yv*Z0X7U0NFkd_-nMo=!X^*Oed5KZe9wcz*>O#@<$Cgl!KW;|AR z{$fF7u-2)jR2tCEZ^a2k!q$*yN%XSyh*6OWP8fX@ef^yMrh4Y(y#e?tkAihtVD-qX zb&BiI?3-=aq}ScwX)AoxTx$z26Atvk!H=D=U&FzUm%!59eJk*swGY%d71s&g*2tI< z-8wFOi+($MQA`Ai4rST`gr>OeL04p7nBQyq16Cj_+CHE=cK2Zpc*Adat1`nqG_4ZN zuC-wspu5jktc@x<3|IcWb0P=g9 z{awcd3gx<3!-)7tuYMqw=s=$Kvvn$ge&F;Av{D5{YkT8cJzTsBEK6e zmIWlh6(LY3dWlCd!diqUI}Gl~4%h@^f185j*GnVkQ-%}$7e$rQ%-Dh*h<6ZtfZFYZ zLlyW5$pmH)iAIl95S<#Ci1yc-?u0dj8i+I*OjDaw zPSFMhYfomkVgQMUiV-47IUQN(eTETgp4m*|a^GksX;@Z2iOVaX zQ9w@n`Ho6%-pHA#oqGOo3DIMqTJN!Ka_`mcNiT4r^DYD|%nbsv zuNGjyr2#VM<(2QS6v_W}f70zG(2+G~=@x0D&J)*FfLi>e7zxen6jLcYq(wtX?Yf^7 zzEi85wkg}rjtT7WJ`zv`&*gV4^_<~9(_7X zZ<@!Tb6oj>BC7ckW)DMR>n>OF$i=%YaawR6MD8)=oj3ces&wxJv5{z%v|3i|<2G`g za?$-DNKT+qr0MdX4N-Rpgp%u!N*@fa2@R#{g;VxXAH^?FAkG5!qqI2IW5Iot;G88w z`W=cSh!;?5@jqEX=!lZASIU8Um4cSFp+dDt+TKBtF|U2?41?xsY9^LKwG>0#h-o~I zA?DI)l6BD^+b%kFoK$1fv_lfnDY??~s%_FFF9n<#ml-0 z1a=#{W+r;smSI!E&jIB^SoTMt8UGb9U~FmG2Q1w_y@BkA3fRaSSQ^|E;1yM@hJRmI zN1~3=%GO$x4zkry(FcZkd>-@=vggs`p z8`4wVZLI_sm#oD~|GN|`?k$1f6?JDPv!=!`zF=MTuofE(^I01W2p|bPM>-dR4lMjH z=K|gi!%M)&GR`C7#^JmZq;o3d{J#=_ma!6W`w*=mJzZy^ZL+azpr-xcA*-i<>PQ6;{9m5KC!vC>~Lm;JGA7nwzZi@jz~p`%qv|B zWhI-v@Bi>>43)T@Ej1FI)UYd({guWLA*5&Am?PuYalJ2Ak~E8P@riOj@`I{g1yXGI zERm+g&+~m-$=4;rTQV}yCF?-@`aV3j=9Cy3$zBW*+%?zX68&m)8aK1FQhn;Xon`9^ z^943BKi(hTQ5?gUtL&y)Gs|&>sC1mMj*~xF!rU5t>o|%koE{Cn$KnLSe{hz&c#lfR zZ;s+&{Cuz7UwzbN;6%qw@U11i`B||mfrWV$n(&?VFX`_yk<} zMU|9D?~jI^BCs4wXeNH!{#^q4)OMWzEQ23T-yWm@_uIRRv-Y{Anm;Nkj_ig;o1NZ% z+A^{^LSGgDyXapI4=-;IZ|d(_vmODUwTm)z*u62bm8{|1Q$=dU>1(!j3ePOHx4UTg z`7y&*CEKRrl2HBDaNopGRV$>?IcBQR({ZeW|EB)Hndw&qEE%s3Y=6}1i^AuW z_HQOtuYe7LPPJTi?~&zv_Q6ZKw!ob!0C0i68zms}0+G zw}yRm;Ote@_RR>f^t9(&n@8I`oWX)YSw0Mzqn&rQtmxP>K%flu`SGnhJppLj+S+&m zY7U-}jzpQ|f4zjl#t6!5q7t;zDtYlL1#%O*Uc^?oif@(3YzQfex;_DOq`)|L0&qof zxF>pX`M`GrF;V=U1Fr5wM~oWx^8E`v=ABc(+9d3QcjTs9zONIq9&gMSCgFg=m;)^O z@q{@ec3i_T`5!NGLuYu;+>&2iQ@Bos=zEQc?5Ra<`4HS_x#LS zgEU~t<&A5K6uc#T{^1RT`S3p;=gR{&x>)-6922zg$&K}7)`(TNEAhu_uaNKXmQQtg+Bv(|6*Ewwjp=fl@? zKdS0xli(;fj5XZ1ha)UeW8w0)jfAN+^b2|{}`_U zXCXqkl07p{a*px$(qeGge>Zn^b@UwY=!6Sc3#+OCbAPw;utt8-uQKA}J-sC&z$Gm< zfph{I1CW-Xc(?6;h6`>r0az?b!Lr1n6G&su8vO8VRE41g%4_lr@@r^6_((-%b7F_r zY;oY1sp-W7iPwPgcY90A_V`mRUhwSh~Z`))c$?sTwYTLG#k4@U^vQA1v1pxN*{C86A-+J^C#J-C#bD zLQKJZLAnwu+HSe3?Oy?&%{{T;Od#@m%a@DX+>U$&ahDT6xj1bDWlAI8AoP*-O4XHQ z7J*p$E2Y_G`sahY9oXNhfBDdkKF3_=uR%c zEMLk_SkDbCaJHEjW{KyDAI*1dNyzb9U@S33DyC~op|NL1gs=z02h5g710>!V zgkjtAIL3MJR~!^TqvwwnNPvQ^SKe=c{r8u)-M+*R=(6!T2K4NjO4O9{z1-`uy2wUH2cSy;e-Z-pFC4%gr~Jam{rP zIyX0s->Bv#0nOhTKdY2cT=?p>EpP;1LL>ft;QGX~!t}hcm=PA6U03RfN8J*&=KT82 z>8k+r*D@1tjZH5tSxFxzljEf&X#o@pg7EJTYVsKIqRZPBHiXR}f{eBjXvW*4r$RO=Q=^oP>d;jh2ow`fUv zmHu=wVgArQTlM6^CgCQ?Y4GR9evNgtX$M``4E1t=o~wqpykevcK|qD;?0N69gqhgN z2WN_Won4?nBz2BEyQ3Op$<`#BBVsJ?Pemt)&p*t*?oz&czfXwn{1Iv2cA!{O7gZ3& zT7V05IfD0dx_BnTDblmd$d-kYbnNCs$Hcc-fIaKkNYwRR^x^i#$P*KzjRXveKT_NU zN-1)4ZYcCr2zR%JqfkP9(LU`6pgtEm87MGEZCCF9?LXOCAw>&7CK+tp;+$Q6Y8*LTmrj6nZLZM!80gle`#v>76bw{M*&@F zN8HK42rG;n8AeYH@fP2-2OBsw4z0Hlm%*liq+-VAydWH#mYUovo<{s?h9Wlni^IWc zj|_W-L|7HJ7}pgC^H_bfm|j%tEnp@O)*5?@YshE402F*q*-w5k17=yAsH`Q><9`8i zH`GS}1CtM&Z|fz({hZHCHn)-us5%f0v&Mj0pV&={*Xgk4yb*#VTG@>b{Ty%{=zt@Z zw-9te&wyHS#h)7bG88*thzQ$X1T6Vj0Ez-?(`08p!o^aZY>t%SEhd36`K+o&980pf zEm=3P62iJ#etu)@JjwdzB3u;0CU|^Vkk;2-i4$&>AL~n;kdq+ez>_K0y}~mcI);t> zVX9&$jnRB@IzRdZLH&d3oa3#35W_CP{;1gQU&$!TN*yU&_7TB?Ajf?T%#1~1mDZYR z)ZHCjESe$Bk4#uziXFN*6ed9&FCpkz$3F{@P=od}j%!aP{CGOQ6Qkn&G!?;#441$; zr{EyFC)c}JlqpH;j}t~+ z0X?j^*_xdGU_x#qMyo$%6>~Occ1x|E@?GbqAKIM7CR`PTTEJM*W#M{~OpMj;I2%B_ zC&k#XCT1F9AB?_sqZfvDq6@<32;E9wy2@<^(ubvPv%1M-*)LpE3)xAf3*Z&ludx}? z?%D~V3eP;;BjQ?FAqvQgX9VpN-Rh|m1vp)8J2z)Y3otw@1q$4-a%ZjeUs8|gbUDVv zbLrAmJQMAOk{=6T_Qq&!zY-p`@?h8WU2&qDilk#{=Qnm63&>0IAqJWv{Dqfgw=hn- z7{N;N4b_Z|GOCo)N--#{I+XP~JqOazUUP&Xug;^(Bk4X5!4#yL|}dD|<^ZphEI z#jKQj9x*=D|FvW-A0OB2xO>h>S}Rw$F0nZo^U>43b+u+Rq#UUg2N2 z9GXElz_+ox=6ztH?iYxLv$l<4;h^{rS@jFDepv#+lK0)bkIhZM`&KD?(3CK6b#N8n z{@;ROw0P?q{w8Xq{=;l{NId8Wv-W-vBVY-gwR=9F)U)#3$y!Ay@hfk%=V;7h2QITL zlhZ0fdj9jorCwsqC0jFPR%IZq-stEsO+uLA_$TB}>!c6qH_-dE;1F~N?xCL?oDUfe zo6t5UH2H=N3p;@7&O1;X4-RljOaPo~qaYinBko;*c2e&X0g!HS1s}dnm~1>tus4}e z_d3WKosq~&j1m=6fGoOe)nBK2`gd7W{h_T03r#g9@c4y)+%hL0{_h*Ai2DZT^POqxYma@xzhv=S`si9p%I8}0gIP?zM;)DdIgS7bV__^;&JUyaZD zBC4y`>TEQo)dbW!q`2-6&)WGTQTj*nIS=|}VfQ>=$HgSm&k?L0L ziRZ#*tcT9q+_}IwBpU{0l(BcXypM9n=3_biMYZGS@aKt#kx9zI)X!M^Ykh1X@2h<% zD{%CiP{>x?OxqTOdxYAgAYU{+APNh&%Cj}>Z?v&irrdkSe8lCk zVff~}!jQsyJeuA~KWDmKpf)W9ofg7hkHVDYzCou8}Y4$?@NKDxiw| zW|`*&5##s0J*$EO{u?oAN6A+J-p>W3w@04bo+w5&G6#l}K`G+E80gA0pDTGk!k^a& zG%Y4elql8(Rrc&|TPMOrh3nAH8(FDEN{VIdyzzyTt3P>zM#X6KiW)u&%RrSytOYJS zS~=CqN=*IOWyIr~(^j&16Ps6-CjLwWqj?~<6t3-3uP-SDp5^eqV@@;PJp34N%Cm#6G0PgQkt&qVSps>w;H@*k>AU;3k zcXld+BmN2H{{jQpFads{x#Dyp3+c8ZFR>vR_PP(U{@-ta$iIlExWF$c5B;TWq*8*<=dz0{)c6G8PC*@>0s~K15S$KNea*vP1QVVhs=gHwWHYJ?#utiaMccDmFcNPNLPf%yVK)*&I%H z7sh`xy2}16SPzDirQik+ZC>seihzf9HX-s;)~Vh%&^os$Rp^$`4;>bt^6~kgnwQ(! zPAN3ni=N8PChZz+z5FS~1QphltDiJN{l3u;4<0i=N7#33(?^TvG<2bAoOm8-U{FN# zBCXE(C>14#!lnw-Cc5fhMqI#>@!Rc?8nKg9SUq(TH7O1 zi;wIi_A~n7eN`j(=W(lrXnB|{SFD*`tQisGRh_4k+$3~`%-kyHtJ%z%U8tVj{FNEf zs+b7VtHp&#aW+xpmb12-7tkwu*$d7Uak_dP$B1t}mF@WI&X!GThLLI>;O=p$YhdN{ z@gO>B1ff}r#j71b->HZp>M2Vg6q)hLu`z#brJrkyTTiy`!nURCoes}fkn56x(E#UF ztHOIgr35+E8>h^6K)xawa%Ceh8TGUI5$lrg!rg-z##1OFj1pGenY6D-L zP6$Lx5&yYjDa`w{dUsULHx-X+6_iJU;iTQ;izi0Xnv%>=K%Q4vxq?IdMI2^Veo)z4 zily^rOi87m>DZE5gPyD9C#fPA*Njl;vsSQD_6C6`i8AY}fy@lfD%$0cp-;Jmq%78)tBSPyqgKR#8n#Mp=v6k^D!hrseyFLUs>d**PcNK4?Egc3v>FD_R^cBnv#Rxp=A>jsMem|g z>_lMK(o~aLy%K?3K~|9a2utMvAAxe|*Pxa9{-T2Q!*C-}gNhLXK}Thhk_w-uijdOV$e}J#`)n=k<60_ zG%?Cy1AD%AP#cDzttnrQ&)&&jF9|OMjX)C6ejnv2j(;LRx9I@GoWmk=f0zgPP6?&x z?sO|6krcBiOKPSwoPu>D!v)*(#e&1hgC6WK)+3))TnXV5+0&5*EpQM1{wp)cHZzx! zAV?VGpowHgjFe5XmP8>=q};QP&}`H5;HEYp;`7-UKdPC=ybscC!OKXZq@p2*(~QMG zmDshusk}cx?$XgEOp+yprlnfSBEl|F)s!4XlW5>BwU(nD=*c{7C+;%TaaJaHIo2OQ zNpAIyf3cG2)5Qr|p6+ryvN1xNSQeUYm=645Co!F|Ef!yngh{(&Evd|VRLD)yx=827 zvp7c~gmo`4m?VT1UxCFbuKDrMi}Q$w^q{+f7>6cu&}Ih-!FFR>T1r4GbO=x0OF$|Z zYk;HTjV{57*8!fHzF44JD1&Mc8#ROx2YYY1oE#(Ant*)67k?C;&T%Lwk`wjKY~EEK zrHn5=PbRh0oSzCu25EcWOxt4|m}@FXvYJ*+_EN#BA;V)HN5#2?#qLGb1DA}os(rIw3I z`Y67;r#z~~x5bNUfw_840(@?k&vLIpW(nUc^u$ZoUVqzg^SSG-X;JDqi%i|vcqHi! z>-EF%Wo=4D|AxFYG($X+k*U)4#?GYiNH433T_GRDR+SV@rn)cFTx3mJRD)XLJOeeV z(HEmg%${6q%|%+qEBe{W@>jy+6eN|P$50D|%|#etuDYso?nndtN9Wly%lx2`Tw1%B z_k%1ASk}EV4@^ubm_}&mow`XToZW{NJdE!KYEUDSxHxf~9}sV7M$XSb>NHg-@yIB~ z%mNYsWlkY-PvJCnao-Ovs4$`#EKb#3C}$jJD!Gd4A49{IVlO#L%djJr+ag4}0*N&l zs_|-`HD{;tv`L%aTPMy+?${UFRG|-u`biS{dXM1#t=w%z6PyR_#S+v@D+on4aPP+m z+~W$Z$b5gJtB$AqGAx)B@C>c?W@S8yd*gwbUQ&-snfs_ijO?%*{gcm^K&ca1&tI54dp04+Mx^V+xNrWV0)q7av_zi*r|GLZG7r#3$Fwz)xi3Ewf<(w{wIEh z7IlO`;$?C8m!FXndd2Dq&5^KnyYh{heHvs zT=?kx&UvM-#h#b;)*TCxnc(LOWdB#!ND2=0Z8;36@vb>5W+GW@S014e-ouVxb~f@9 z&2c|6U&jvt)w$OT2)CvnB|W?XF6?iC>Laf<;0l-L2K~{!rAB*;+3_+XwL-ySs+U0f z_HOl=rTyFG=D{18c%j6mpkTXq6d6Z=*I)C=`q!8jHYC--!T*roD8xJqj7=?zn*O_UWd9(_+I**T$n+y!>Wt4>j2ffEy6`{_iA?G{ zd}fsj>fOcfhN_jLbVhI*;d=5x;YL5J9}zcYdA2F4nDUFKZJM^EgsABve~pF5*Gm%& zblM$E0p0LT`wa;ztP7ztkH6=!6<4r0(@rVlk$07oD*hJzg%Pd>a23}kCbc5$3%?Y= zLk+GU;;o*VSc~a1z}uPBfM2a???J%;(z?mkQCYr{QK>U7 zqHbcoSD=V*H@17AxSq~X()w4G0~uLMH8*D0+u>g{$3TZqL=$a!a`q2_5=X6-(=<)y zwcLm1&ql>HK33hxUBDkG^`3<(QwtUgx5t77{(0NSE@nnt6UxhNx`bK=yvVQt!(*%o z64y_7m53!`pDx|J-}^k8H|!td6WXTQXeAj1TqYe$xj#@-G&SW3g;9~Oqg#JO2ALJg zaxo}?3LU|}h{eoQ!ly`bWsZ!t%#9`rKWXi>R})G8QtU8dIq(njCBTFU-gHwZxJ6L= zImjN?UZ1Iuqn)FuftK&DZf1v#AhkCEP zm+@&JHiF2q2NyH&Xa#tDeSN;$3h9h{hj_Mq;Iw(LG&3AcR|#+;L@;#a2$1h{o0W#< zKT&gF>+miZ{Qc;YF^;%wyQdpZR1QGLFL#Ls!!GCzjdSKYdy~#3c)AGz>*+&5s|(Fo z@yD1s13&-u(A@sK@ga^MhiZ07_e`eR2AA|<2w{nEP{>O42BaFI2MC#-#KpYx5D$do z%92sm#lrJ!C|saKB`^?@jjJAJgQyPAM8{d!kw3&UcrpJ!0K-5$ze%X3T~wmLi-lwp z`x16A6lQI?PLlvo#hx19Hfpu`WlF|+{C_}0UlHWT@&E7b9u)KcZ}LB{<@ucX{|ilZ ze{nIu0&-1;s<`DzGe)_EP*F@42P@*iAHe!>6ypubuF>v-0-{tF?Y%i>1QR^UFTwLjZY9KfCHC0P1ni6?jb{V`Wj-N_T$fDIwPs?=8k#E1>$5g}Jsj->ttw z()yyIq+Oqf%7D@}!bum}sR$~ai`AS2>y|QdPWEy!kdL}Yy*kS)fuJn3WiGJgY}TKR zBUyH;TovC}jpT=X+#lm}W)4KNpNb=_4#hmH@p-M*!CPr4&eA}fbI{kFgTC?*oUQU* z6i6|r?|d*$CP?RSi8Ydcp*!~MJ`E7HuzgfFE207FK^SH7U-xXVqj(aKb`-3$>!a7Y-K)R%hAVC zjyCJ-?{?LciQU-@Vjjb*+&kGWGjh0Wt*l&7KkGgT;H+>jgLpadzdXnlDp@XdMnNUZ zH9Chhmeed(rdm9m*``WKMZD1&p}Fk>c1l;o<$l7#`}}nc3+^XSHD(#v)*Hz>rkgO< zvyC#9t6;gPqcQ!&0jy!hKSA{B(m>UjFwA?IY!y{USTnx5TeB7-lSod_C_hH;KIH0U z_g2et<66A9QUn)|0IJyJtf~-oS9PcP?TpFEX=Q(*xZBl25dGbQZkO&cs;+AMEn;rZ z!pK~PUYEg^nO&BRE)?&;Jh&H~F8}^Q_nZ zGyP(TD<2osUIQx$3+h}FYvlq%6ny5>pb!_jqm{T8tntWyCd0JITO=k#$;J56m=I3$ zGr~XAcBZK7i7|fwweR*lUdy0;KJ@rsin zeVh4cRExYSX3nRJ8d{aRLed%I1y$aur;lB~Bzh0n4-4`Cb`Ll4U)S<{ zD*6A^7~$njpj8G4-}oH<#e#yz^z-M(>waeN|J4NluEGBgyZQUy2Zwvz4gX)qvu6MA zMZ<(I$b+kPG?tWIX8eV1X-bFi)ikD=kh=FySu%O{ckPLdiDRA_rF&j zBYf!_%w@u)%%%McvyaUg!jpx`M!Ej-o&x_D5#^+VM*wL8zC1#)MCmK8;SUE=-iwOnfBt z8)`Jr@0q|z>RrTq(P%ax{*(Q02SR1)(mG<2Dmfxjz1oO}&+w_s|DO^8Y99aJJ1FFT z*x%dae^|@&h4TMatMyUhMg2uYK^jx4I$G3fhgv6g)iP13RsdRgnvXgShkyOBRQjoI$Icgkr zk9x@`=G_x{7hyR@&+8%?V84Ny5bUHFIKrRM@NT)bRn=K-OCAe(HvkuE+qQ$Vj{ai) z)AqP*WGeb>f@xSE9;kJuE0qPv0I^5>JD&t(q&O;ij($r#hSYQnhvc}^S)#iSA3B;{ z>-ErRnr458|C(U(Q4o)4C=yD#WIj2Ok_gE-YTO@*yGUT!aI;)MjXOnhnb`7n`$ghmw2Mqg}fz%m;xTn$GeLNrmk#`S- z)1kp~{Rz)$8TT~$Nw@cl+gvFMoH6M8q<1=OPy(XPeM0!1<&n?b>x*j7IUFZ+cP>`$FVqJB0En14MD2u*CD6_eYPd4;MJ2KMN}x%nW+fP$In#B6?2Va_ zR~KiQU=8ZsrM}0LdGCk9MoKVdtnVoo@>eRyKk6#Zu4YD*yA@cHo`}eZ`8P|25L_@+ z0u8A=EyTD>oubrtXD(}phpIDD)%HbI(5TsAqZVquBA66H9-B;X{>DcObp|x-h;i<1 z%geoZpS#-u*pC9V-Ts~iM6`b(5;Y*vmr;}+F_o<$<1G#60E@8+=$S;)*h>K{MjlLO zrEJwEGK&0(w&gS;uZK&}9Uy%NHH73e95qU2WXXl1qk|O~L-AtvN=eM^`=bz<(?KPf zm4wtl2=kFRM!z*ooY+P)V~Ef%JE`fX@q4bpCHcyX_%Aafz*I7qY`DgX!IlRGDpIr_ zc5Yp{!F}==$`!4M>{IMWTX<@9R8q8L8Md^+s#c4-Xg+j93i}WTVs3{sdV0WI_om~Y zb|kweq1#<99%tJAtAF2JzCJt82a`iKZrS&oO^R3AhLi-pIYTP_rs2|gqTs^c#g$XE zGE7)S)U71$ma3Tl0*OfW*H)u(4OM?@Yz_wuQJ`+47Bg|BPmrPzOlDfq-ls}OvdT(RYAaKfQr{T;sR|}bJ zY0F<0JTe>-PadO7cH@nSA14el7YS)DgD(w~RKAxlYp=kZYqXcEQ;V6yCPb~t?1HMp z+4T*EGs=Pbrmy)axF*!p=}IFPl|~P8vgu`FZ5NobhPqmmAVl6qR8ytH?z)9yIQA(C zy##jS5VkQ6|3#mf_TLrV0Ga3i)$JDUe;n*@&i~i)e4+kdPrfVi`S=xmI%k9pQ{Oxl z{9o%+t<^Yy&Efyu{lne-`QPCt|JQn+FT?*UQSUcAcg0V|`cJU+^ym*Y@_+6X;y>(f z{6E+8ta1HsyTM{>ReVc@q~R#Jo4ZrZLy|I2SNr}}oT6g=>uvvvE`ZM4{~hk_<@bMk z{f+!z%k!D;|BRPO^#<^h-T*C!?lZXeW3PZZXvtsv)Ubo~9AWEDo7@8h^CG$?50pHj zldFruP0-v(G*Zj1hr$6BhlWJ{La3fc^|t11RReU@b=9xXka3;g6aElbny~?t^{=y* zUtC=!kZ?_Qc_>$Y+qO3Uf5HXOdHjF3c>nWoWB*&v^QH3t&*1XsXDbEj-2Tj*?tI$o zoAdCvxxe`lpE>-09rnMy-Gkk{{@>l`|Lb|a4EtZgr@nampB&4~wuk8m9-`$hx9Ylg$__Cfw zFK*wH>1{5UWZHodX{!Ci69U8!pQ7jJx1~EZ?Y3=OY3IK;la3f4;n2v~mGqWru(V5$ zO(rb71KIinoTkH5&0S`%g4*5^JnPH9)c+_qVOc~tmW?P6U*?(mP=Ym;`Ng~D~$C*Ro>8H&~nYTgyzLKTE! zHEy+!ztbD}I~>uw2OLn}5^cdHH@Sv!XS_!4V2gYgP@$IDZ;fTy_JxL&6- zko|k+U1pz(`#LjnPo|>mS~?Jg`q=(Muy7_ZNIIlM6M{YntJ7~P?-pL@Y1Nq*ywUn@ zHy?G=Y|5M4&8vK7>;F%U{HUL~_Mh%S{`|k&+vxvmdA@A>&u3=$|KfKiX$(mg5VV~_ zB|H`pe+Nm11_VK(4#`jyi!t&DmpTpxL0UWsu#BU`cu>o-os=$E;Ug@)aigg%+fCF^ zVr3Igaz)R4{{NK7kNTOz|N94f#rW_0oBU5}dDd+IN!j-2XRrVAxp!*#uVc@*#{<>p z0$;erKMfN484_G8V*cNl=vEMsbUu2&aQa<`f-_tGuOXv~fA?n&|Cd;jVH`Xy1ng}7-|O`cOYVR4HvE4b&u0_=^~R9e ztO&55*+$@Rz5!TI2-sxKp91O=gTQhU1=Pdh81)tm37bG~bco9`YMvc0c1DQTq=gjs ztHIFBxvU%Hbp`QQ=P6*&%@}iX#hFKhxJ>zi<7x5~ysRr`t6@YViM9h0E(2*c>gx{b z&JPId!uiw=YmT!R0o87XjYD51IPLmE(|+v0v?XC_%YxG8Lek~~(#{P>tH3A?LhD5I zY?aH@3>_{RZ`M*qb->x>!p*wv7PwC?+U!bcCe!gpARD*+3d$)71qg9ww_KXZhTv-z zgQ|8Mhx62TFin{65=LTj$}QPKvizzbuyf)A)(rky5%|?m=n`1=jrXs@wN*+p0Tx}T zRpGfX=L%JYdo7LTSX_j~5QT29Ret<6`mc<}S{kXeDiUjAq;#VO&W^%5KL+cp2&~od zSF5A1&KglM@@naj=0;tuh`IVy+jV6S=NTcKs{%N8XL67yY5kv3cvu6{M~)Bs6wpdz z!Bz&GtrZ-$0ADUV>rCcb8cMZN9jL>k%A%-7xtP^1*D8intq!L85m>!O)@gw0wX)3&p7gl#552n3J4YiaWd2qR+O42 zjyoNndBO>BPQngU?&xcQY5<)$8gcBC&R1g+Oj>4GjT|dU?ZDeU9ifJ~BR3&p+&~Q+ zQIkLwHDn+fsNrGt58<+b8tIPmM7+Y$&LU0RlWAECSu{E=wUc(*G7xPKw-pe8$(g0@ z$rL>jPQH+Nu;|dD`==vu{y;+cBMyD`KzI&46@|VWs>_b{G>mQ_tH#ff5BX!uMq4QK z1&nqp%WKB*U=R?ak!E;`dl;olwPRWyMlk&>Bo2KXurN{hb}~miCAGNP;pHs>38H`0 z@cn!N3Kiu<%&h-h^4Kb7nMV^5V8ltqG z-Wr*Y*9sCF$l&(n4YH6VD2zCJpuQ3%3js4^TpL4FO)sEoPvl%Y5(*|cCTM`Ydxh?Z z`E}-EK0#DtZw}pPR8)~pZKBC78gmkYCM=|q!Cmvy>obvAK1ztWL>6&2A#zM&Q6M1a zlWF;tZa7^-nWpS*`&n(4m8sP@L^GS!G6^5pVg**yZ-zRSC|3nVp1`)-r#Qj`8c<1< zE^u{W&dNpmM=Veyo%FWHIANmgu}SBl*VrkXBf0ci4GDx)Q1o;DmI|3a{4?}yoT`wi zl#+B6I6#qYbX6fvMpTaD0hCQ4OX4_`z8eH=(3xNwcF0>ii2_nqs^xLNQwr0a8MgT- zU~8k6i6Dz($z8Gqb4=wLuHyU^B&68ij88b0ER~1k&&o1tM9kNk>O@B_Xv`IhNo`fj z#5=eUA}0Kr46E3kV)m;1h=fS@`rPR>B^o)#Qtcu*m{RDF7J1{${^v2vDnxVQV^6jf z5w^z!`-F?O4&GR!pijbSP7Qk<22-TOA1jd}66_zfnETZO3nErhm1Qu5s>MCOE1;t^_qokuoyD(eh63uQ&6%)D_jB5$ur6kv~>1OaON zulBcJH|jyrFh=exPKNX?YCNbHf=nI=KV_2$bAlTG?6#k^yNx=k`K-S5rS)(A_51OzCAM)QM)5yI?x z$xzgna|H!@d5B&z>>~>(Dk|i*AB|9@5lDuv8fZH? zq|m=DB*rKJEg(#Wr);PIK4h|iaR)>Lkov3ige>tBo>G5xuC$W?#UPJ{$YYZM4K)R3PQS}LfBrv38{INQgv5h@iZR0WjyF1h;r66b1NIjGE)c4A!Vb^4kfSTE2Vr}@w75h zpWwHrI@~1jO(S9l_4oI8mZ`Vj>p7^^SeoFsEzcHhB>^QTv5=02)ZgD%GIYP!%bWQ> z^@--@S>!S2|LrjeCUg`sPFCN?ocq7s{k?qrzy87DCjQe}p0CgqmWnNc_|+7E9>*j^ zgO~A5&u;D@aPQVv=oF$3qhC&MlujYG+9N7E z@b5St=s)y0w%UXL@DBWIUyMf`^^g529>PxAqT0g75dweut#8}nG1~gJJ;3){-?rsM z{mnQXZGHRyZGDA)!JM*Kpo_C}vDG#Sy|3qgK(X9WM7pLc! zH|HOxkJ;M?#U0^_+RkVHN7PyG`_8IE*o$ zqJFo#S6OK+Wpv!>JU%|QF?7>reAEeaSE2*4g>TQVU)`XS%QJNP`tt1J_Tu&B4f_7| zHTwDHyos*QudZL8{d}rkH=)$o#m(*Y#fzWS8)%@{MrQ;(l;I@JhE;Bu5jBt);~+p2 zg7p!y(wKzys9~Y6Yr*+*EQsQ65odl3(Y|fSaaGo*LUKBYfwzG##*l`@M}sN4(Xqh= z>Lr_)XNV1vRJ_z@UOXY8%)`Za0Wgn6Q%*-?iP&SPtz24JMmEwG^kj@$Os4M9GWfw5r=Ak06jt2gH|jQp^tD7m}Zz969XW%=5=G#X@XN(V&PFh zH`Qp=+c@;qP6`;Zb7hRPK+6eeM|Y^bS6~kr^N0x|QmiHuZs(|BT4}&g1lp!Mx+;4l z+-^=45H!?(Hmj|52hM=3~jrZd8CS( z7)RTb?&#?eV;ZTJhIA;WO7?lG>FxdQ|J;G@lw{V3w++P-Tqe{Uz%58&TckT^KteL4 z9>qbnbqCIL%Ky#c2Am|Rzj$NEopl@nP9CTqV>mT+>BBTY-V*Ln0onrL6Dr`;TGN?c z1TZZHQo8})<%U{blRTwGaB|FvufKu^2)><;!$=l@Mx7bf z1p5Tx2TTKqDy-MP!3nP!c}G9ter1BO?=}7JyRN zA#9k=I-J%R8DBc%`8AL%S*O1Qfo8#3S(?D18q3TH(6Dbbk>m=I#tqbByb}1PvnZtE z`YgmjVA-~a2NNm{^RRjcP!~WB;F+ZW9RVLNs1ZXXnCKoWi~``DU7p_dQAv3jsu{hN3QCD*>4hu2Kz{@0vG8dMU=vm z6zO6C{EOyHQIzm5w+|LNFrf*ifvOx(Aw|;_S&1qnrb3d5a9KDNA|^_NcpxK92|fKv z@@Z`)(Y0KpHyt*Tp#lfn3U59YUMxV$Lyr^Smqv~J6*yihn#fy=gzQ)?J$ft@5%ppg zi(rZ-nBObb$5ZXds$4|SQD~i-)f@q0ODRz?tHvdh2qSl`wj0GIoYSw9VcQirSLJaL zsQBX~*Qx7u?GfPQgK4IpbP)>jXG}sFsBS&RBc{c*Qh1%^rx{VdjebySmFo618NSu5 z(2dDxWoWf@Gvh2Xmn#$Ojgf;Oq*%ycs}xW6VctgBwF2H4#lqdUxoUpGi$Pf~^jIR!fw$-h+I1%A3M;1j7nq%G z^424fR0|AZf*BQw3MBM~Xa_}ltj_FB@I7gwF@7MRnQJ}V)EDWX^%Zhgy(7XWCi7;iI!40Om5f{4&-wcUI zgutA_>Kt+!j_i(xP%Hb~g}*I!koJscW&@a>B57GqdA@=jklx`MX)Bpg^0L60E*{lX zSXtLb7eh7w$zDiEDiuv9lOn0HyW$a6pMlAljr(>guoB&xGa*`lFf|N_yFa1Uarai@||R(0~iG)lS>-(#e_HVcGeZfTk+PM5|(1O0+7I z-He)*VmBJ;8Z#o#YE@b~s3odq&(aDOR${0>yL|?Oly*9cgu4SkVT~FX* zfZ=R;F66O@THPkYk7j6jFeOTDBtDHNO-)BiqoH!l5{aDMHfs@H!O5EGRtON1lGcO} zJI z8wdhnD157BXZlbox6_Zix*QfzvQi3|h52%tvRh9K;YF~%oMwrRl%my!#&OYam>O^f zJrEc|vkqR}7z(8rF2ySl8_Sg7rkYo#|;($xwsa^l#6i7kvNu2T$Us7lO);X9X^ zXNb&Q*I5G;qQjKWD3LizC1A0A#+yYfW2@^qRxYzQD8bS&w^BfXcpzL)mK;;=x6~k~ z$=rmDPt=B4DHa6tHUjS-@W#!Mp}~qX-=I9`t#o4K!-2DTRT@eb9J987V@)%*`9y4B z-&cQgwcl|m(rLwlZZK45B{Vhm1@$we1N$i)s!mDh#}lizW+=uo7QO$o)0yXKfM9FH zz(M(9N_=oY58!d0{xo8$&Mit&l3SrHqSK+{Z@%@8(=(~7N#Yn@A$611nfcC2|R0EN@4j1*3^ZjVj0Dy`@! zW0pyFD!H9NWfmD)oOEls{6aLh!5ZIBCx5RT;JTTq_A)Jgb*XK(4 zy@xpqIB26=tAL2M_6xCkw9kNZO09A`CqfGG=0HuWFZLJ%Pl#|d8qrKIy6T^dBSXWw zuq9dxaWl0#nQf|Z>D4sB2^I_-`}CB?Lb3_wQ-Fqs$S1<%bYP|;*>unmE!t=8YO?cY zq(O-Y(3m=GqcbXC>qNLJ^(*EIFVkcZB%ltadIJkPZ?(lr*#OKUY+h0;STmh4v-U*_ zDtP%5?2YqVRJSyhB0IS|h{1_pC86fQKjsTPe_A%5fcgskSx>5l>*RV%aqnz}W@!XtGV#Ox>Kbf;HAXXf1ALHSHa> z(aQv-s?tlU?-x(o=t4?6FDmZi-AwvNeJLQ-#K6Okj_9RO(E?tvBNJar2k2}*&{HeHGX+oGwr8G+Td35q1EvXf#F+m`1J`zVkA!4V zgh!+aIzg-eH&Gw87ep|O!&JEeAz0!G;<3p@5wD96-7yh6O`t=ipyv&7rj5jyj{OG; zhonQ}z9EFUT9i09nCA5gc-BTIo{|X)3zqY$o=;Li?JSC4$4Yh2F4z2F;mqSUtA%=F z#m6`2OmO3qAqjn5W6T1-#B`4N z1UQjZt`jV#%Q@yeJuNeqV=M&WYWWx=YO`oJ9!!n0kq&68Fp%OjQRW{VYIM}b1Qt!e z=a*+nu9msM!S|D^tMkjVi~l}WQwY|AC<>;=eZh6zQ{MoT$K)&tA#_`_LescWWUUES zt!6YJ90JIh-CQ%>$qy+Be1S;l1x#qBKj0XGK%&OG4{7p#uuK?fH?_p1FNEk_pR<#0 zqwO;mev|kMIBV24{*N65yLH&6iZP1=U*GRAaG2eJBetEhS+!QgG?e&lau5oedJRAu z{YnrH1VfzY(#B3|xj2;7bRz_4ta{r8YG!1Gu{~NHkkn-bPDm{zLRD^;|=EQxocvcNsSzsdt>@wIc0j7_-;C#PCuX%?a2U}H}DdMVum<; zXRnkco2KgsindjO#Mg4?JJp2UhbRW1#g;LfTlHPvy@>(}EHR;^M742t`)mMKGMq8h zSpvq=lKDJWQ{(^K>bJX4rH*D^rE?fxvn|&;nZcI@kBUrzD$V0v;wN z5bMeWE(Q_i+6hO;TMtKDA(4;fTskD4qz~53Jz&@$KuihPToL&QdlM}ow77Myn8J>B z%l(>WK{1vskEnf6JA1vJ2!thiiKm34-Oip8G{$nXB{@zMMgyfCi;Wgccd_9!_H{lr9}QPAGHkm*G;e;VQm^$-n} z0&WUID}$*d;2KB6#H#D`jU{@}SGBg>kP2{ZHyO*ZMIpL)-OLQz1S@Y^BJqf>xusXY)%6>pO*?ZB-Z~IQMwkM3w&`{hj&`=TzWNGXaRQo; zppuBKt#8p!@j&5I5^;>){Oh?|y9e!VtNUi_Tl6CdCi?AeyZZ*}|3D;cb#5e9+oi3o zTRu%lTOZJ-EoKiRXlN9Q-aeYpFoy8#Tkp{;8q)iCK+t=1LxN%Jm2qeM=}4gW=wyQb z!$Nfa(+zr$eqd}A5cI=O=SbD_I8!Shov4pn?~hx@h5v88N5B2!H`~P`C>~=jMQ4JA zB;2BPV%lkohtW>jSTT$$gi0Wtk;azL$m-l5iIxeD-ZGI@Iy(p;%#J+4;8OvTN^zD8pbOw{I|!NIKtB#9NNK03hJ;YZ!tCi$*z=8_2On=Z#>%gUph0DJe~4;7Li9hXqA(}C@@(XVRyjorZU-4h6d zZz14p6tDqlYU3&B1Hps|iE!|UrveE!AuMz+2T2ByYuxA{7fLq#M5gFHx}8Q!I-D75 zQ-7t_miJrlTk6sO+W%z!f?rC~-2RN-D|G_BM{l}s(0g>630MQ-DVXY`53M$+Dw2VW ze&kdVgk#BCK9LYZLw#;52$D=W=)Jw6^&Y)>*NB*Z5=wevzBZ2EHLT&`3>48DZ%q6+ zpyB8OF0Px`=Wv8eS=c!K?Og*hINF3astqSIrTVEBFpi?eu}O#4`0%0GkSqet>?bm9 z97FgoEm9P5j+u~ZDK$R){^5hdg9Q+lyGhrFM~d<2(ZPw#do*AyPy>@ZCaT(nS*>8U z(p032Cb>(T`j<>z6YNiKF%6}jSe->qwb%%mPn_igHCiE)ElBOuC{ETrNsK~D;goN7vVxFFaQvDYl7|FBZS?C{?I3i}6O0}d(IHr+)VeFZ(a{82(18uQ(o!TKJRA_- z&Z6}V5PT9G=_DzK9%&HR5DY2AN-0t*F!7RGUDlUNrlE+ym0C4};>e)$UJRzp3)9%* z>C`|hD}}9deJHMUqZj{veR_G}MjmUUOD2hR>{Z+n?$()jZh^7Psk%v&7==Wgr5y~c zrcJe>IELNDiIPB>=w{n*v{W)czS#kuOt5c85zql1U@cv13`7?{RmIVHb~w5{eIt(i zbR;fyik(7JC#DFxKN3I$D2_lgg|uMwKTbd(O+DX;BzaKV?F7tYnWg^0Y2RM99DtXR zwj;taVNoj}4@<| zWN#NgUsv~G8{pz z-`6(S!b+?flOrEV6Mu7_Q-1U0(S!Z!cq-1g^-#bQv=?E#T^KIk?} zo1XP@{q;i%yq@BuSXK}$*w@Ut%yj3LT&NG5*4I_WeL5mSHjW!&jQjfs$IqVab@vZ; z4-fVZpB)_a_x6XwXG7BO?GKLzet+P134V6eJsgl{hkf;K*n2i4y+OaXci1EQ{XwJI zP{NZ_pEQocI0%}JG|zA2xN$MOWb#U1AZ|1pY9}nHWL$k4jXkbZMiJ5BkeED*0D7a@ zka*NMZuHtm?fpix5esr=AXl=nfFv{2h0tSi!q3LOEwoU}nT(yY+s_ou5)>!HWBrfa zhGsQwwb|}?Ec8euMMor?s1|vku);c62s#Q|5%XKBpUzk6Uy%T|(3nax#g~lC8s|Mz zB*%y}>(L{Rm@nEW-{zPFzEIyRv$8E(mwIB|mLaBDa?+4!YwfuxlUUq~P7%EqsxOGjd@kIl;W0gI`72;E1z}N~!fDWn2X{GhOiypw zH0x+KVAFrxaM?{^Sz7;WY};E5n0p%i6c0#1! zGwssVO<0Il6yT85YNZVuYIB;Hw@}1kvezYkquBtXN8`8wF>Y1sx}8%_6_$e&-)EtC z9R^b+>x{0}IBr0?9MSmjd!BY&yk|N3i)pfnq|&&VX;UG%Lb(-x=iHK+_+ScgDR9n&G4AX*Q- z)PU49AxoMz*ofN7rQ3?6_4=z`QT0U)mG7`OIDB^SY;SPbJJ@|j4t96<_WQ&A!=eAo zCwtxfXZ!wsf6pho-J{*TXZ_xh{|q1C-rn9|uR9>!{!>&wU+tto=?mz9V?ON+Vlf@C zw~ofQd(iGxsC;UOj+D|`9~WZx4(R@ z`6?CB4C9F#4%=Ie9f$33i7kieMJHo5hZPPkxxlTx?z002kEbVKzm}@($*(tVuA;0KAmg8J z)FR*`92$FR<5*|5a5ah3lO!O@f&fLzDRDDw}Hh0&I)|;G3p^G6n z3fM_MyS#B@*}$$fj3)ynTTLt5q&;e*e;VY2yV}3n|4epG|7wc|Z@c~N&hbB0?Ut_n zFLU>7r&$02tN@${7CaE}6|pc6rYK+x+>ztR^%l9f(vE8uXnVxfM(EsxLk6RdI7Yp0 zTm7$heAGSaHIYkDZFm2tfQB-2@elcw&)5XhFuRJhhGbf``=IZ?KSxjl`MQd^>#Ww* zkm%$h*;Mc){r?%0nA~Uu*_;sfy8S)X#fupH_=KY)8}BxY=UC_iN)yL3e`pLh=$!~# z5Uw;p6C9OVCOG((_!qtnjsO3w5;g}Bh`fy`FTx1N_+uRU0mwB2B0GbN?U&3a*9?SL z^=YDOh=M4W%s;(2yGC4Bv`6QUjNcnoOdvYU{)Tw=tdG zV;)c~xpTf9lQn;)g5SRogb+&bB;bj5x`sXCfTy29o3z zMh?7mlKP^HD^cvx06nX#T-#uBSGv5eXBwP8Eo#6vJs%AP^{q+&J47Y}q9`2h8fg^0 z!jYbmAFgcajQbp@;`#-1-AM+c$Ry#t3UHXWBV=c!*$~9zM2f`tt!CHiU8aapyd2N- zhW4HXgT1}ZBZxGa1_gSc9GsC9=)gj`)nF}0dZ5^h{lyPgSA-~uscjGF^!m&&@!Uw} zV_4e}Rj+m>!TC{nYx$8^D4#QsV0K^GR;UIdwHWJ&x9;2juuAZ=^d z%J6Wizt}cYC@dwJFEu-Yd;S@X#zlbjw}etgn(HRA26K%8_w-6YFrpECfMDX-w^>pF zsAvdzeqZ6}6@Htl5c~bT{?SpVqz=!&n<|2GMXRTu{wTMstmx z_%5^$EQlxO7z+ZFs*?I;;#X)Jo&kB%hoT>afpnHUc@j=D)89Yn_xAP{nSQ%Z4O!SS zf;RC(vq=Tus*2m~t+xWIQw3`FsAl*j8DVd#lvKUSimN*5+D5H1*`2tl0|x-+yb%~~ zq7NxX3sfW|_U_R(WQf)w8%^?JHHnakDY?2COl@c;MZVhr#*Wf=_5B05hMHVtrBa6w z4IsmzzDhJEIz&SzL4x6Xpm_|Y5rh#Ta!3PdqHv9ghBS8$s<0FcY^K}9E>WsmfpTc? z57=D7NeX6$(%SYavD-(U;~ebo?;g~ddaG>YNC5mhtTLmxkki`)M`!v9u-YhHD@399 zXc#ao5BA`mPP^Of?`M05OE`l52U0LG4o;qGid55Ss-5(9(S%GGpXSVRl^3?mcBMlG z(XgYM>^RVhKV!mi`Sk+BE<+tY>f#DH#Z!Gp-xN+-WFeJ|pNvM_=FiN?86Eh$D4`AQGR4l_Cex{{t*Z z=uJ~BDV-tbiZ7<3GZdXcG%TTDt!!y@SnTKYDBHA*ID0#tEizSgHJ+AckhfRYumAhs z+QJ0za$=$~u@KQnYLxIeFms;-MNq)oU=ww8Hh(6Vu;4@x-A0%8>9Aw$T9Pun@MV#TU?q8=xnCK1Rz$+$_aUKs%scwXg2}oh=cfu zlTjRCu5;LIbFyQj+v@?yi&*c zBWmmW2OGeM>}v3deh=M^IExD=R=aIi{4KH6>EqAj&X!@AYO8e3m|}o%v(2&|X!oJt z$g>9BAEdG;`9!cOK#Q73QpRd$xI`SRrp7F(BEIeg;;O5}1p2k^<;CR>$LMEa4&gk^$v;wvYJj7Z@8k&dDbQS)92Xf>9D`%}Tu{L_ z(x!ecSjf*I{WB)$;w__ZP(@^2GHcCW3x`iGaCqP zDJ#8Z4X8BNE*uypM2uH;h1Y4aR^)`Vcne1~@$&X7%4mVGQ-t2utka$xsASHdL-;7^kvR#3mvp$0} z`gqd#mLJ;F9X_jPH}3{r0Y)8~uHY}0(r1!T4ce_3Lm5TS?W*=RtF+IOP1n4xmhNR{ z2X$@*^p5&Hg7NPDvqNu}c>TTMz}p|}cge0l9PAzK?jH@j-adKO>+X5GWOuLM_wnB0 zUU$EL)b9;fYXd3e(}_jIgubn~A#mylyOXPn%w2(5IJ$ojl5qlW=lf&)^vjJZtz>)K z{ee8}jm_G2Lh~j^_I|3HnV>F-l9QbLD0R+oIsthwYJ**8^|`>-T62XS=Bz)rgN0XJ zp|iRx(CxMEx^p{QPAp_*zJ_#gdz>XCuI*kb*+M~}WxcUlMdvd`MFUL6SDZf3fQ-nw z@Ni)6tr?dV4@WvL2L+=-!{;n&9RJohdHJ$ov?Le0g4n4Ba=Cd(xnUhdOw?lZ#9T84 z?CO~gZkP68YOD47IwIjK^V257sL`#G)r<5E%wRR$P6{^z2Ut+gcZeNQqr z2vk?^wSOQLb81Me%xk%b)!yD?t)EezCXtcT~9@ivAK)gji^TTvr9)Q?-St2nEv3&-hVUN62fk58D-+I9Je=H0{a39K|6&`)IM%PzF% z=l^pryXWC}g}lbkdi;OcS$>WU^SS5sK|Ib+P_NimrSkT@D2(%abUcE0mnOf#vHO{m z)tfs5mg+~^;+qGvaCpya@F-Mi4zY)?@?b2QysB0n!JpW9{j1f-%O*>0Xx=sgONfFw zS7&s}@88FTh*P+~VaJqW*)|f9`XbJ#G7pRjcu>h{Gv$F`xl8O}oP_a#!$*DlhVd%Q znDA%;IhQsvg}l_@|5YM`SWvg)Q}S_{ipp#_V;{@!?6WwJG%Jg`^}x`I$1b?1S6+Br z%V{W~*@!F1A1UAQHdVyDM>vvB=IcWb{8T`IxvH$pGaFrLUr6&J=C-+&KqQfz6*-#o zT706d=b>HvT&{qXya4LKB@z~Q^Jkd?fRUS!*MiwET3}cdAMs~EgVJYN+l0`!G4j;| zNipV;2Sks5xm--t^kgV9Ua1Sn?^L_oL`byv8D@!hH@Yhfl1VkV!mJC*WSdi-zWHS* zjljm(IgS-bVOX>}4J)PtE3wl2@^i5gIkPgil9H3%z-k|KFr`CLfkQV9phlZwMVeRN z=_}i%FlIhi!WEgYu0^N}Dn==3^rbZgIN^@HPJip)*B?3vkhQXK1PavB(r3-%F!%M( zH5RM%R|{k3B?HuEe{*L~cl2FCc78NI;n`35LX=aRW^-0s5xO$vH-afx2j#&C!qG^i zTD&|nJcKbqWt)_sa0F#m+ZZ-Z0L@b>;%+0;xN<;L05%(PqBzu|Bz;+J^|L#V@Zan=IlcQV7pbjc68W=Iwj7P%bKGL$1VNi;38mT244W zerqDU7qfybxIyN6*(52}Gq@464tozcfkNrNX8cflXbc zZPT(DJJn)$lQ0qlPLl)PO#>;cup(pB9I7}qiwC^?XDA*v;Or$#Jy$e->v{&L8?dO3 z-RlO|l(4I+6c9kmi5DxU{3j5Jz{6L%C%&OwUu*6EW01O_{?dA(T^H!XgY6n+hI>@pzO|b>}-12i>rh4i7;7m~g#r-JnvlR}-FQW*xyfvix9?4JRWN zR5?*97wLry^EX5~pjMFY*# z=jgPVzt7y+PN`G1u(D(QE7UFo$-=RTIegt&(=S{R#Ra%J1I`f6;o;=mlrMZvHvUSf zb#r=v;rf?JDtK@xW0j@IMzufZQjZwE%@n+@6@UBwW6Z~S67Wo1W_p~*)trV@M!IkB z;T_CIaP9rxe(zJu(`ux*?_HGM#p|(=7i>{$Hq>N=H#B8bw=ju3xI3|4q_^+k2d0LH z*Ia1LQDIE#c{%T|zR&RIjuo`3GjXc_B}auMMmLKPsj%5S%@P@r4jQUtR(kW%!8{IP zWvXPFL#H`5SW$@r5}b$?Fm73Cyx4?L1;i1a^!aphUSv4WWq)}%QI*A2hknZM4^7h4 zVKM%u`qB2bF<;c{NVv1%)LY?(4-9aI@j@b&&=wc1tMxEzny}_~ zyVt9aZLY+@`N^fZBWqK41U8mA`?hkrAn`ct48ynqWKt&Ch98F;!!T|zSwOO(fr&jk zVZ$)y>5OVI$#mu>Ja%kL{)C5mYHGHTngQ9o33Ozl7l#DPn{F(L@^Kie4m+1hLB&vG z7GZ5DilhI_?^N55L;crLgoRmI8*il0Z{Lb+qW`VOf69{@hgV=iY~t|%STGe)L3zc> zw?*PK*<6uL8j+`N!i3$WSmkyeMcN52#C14Y`juXj1^n-D=2T20HP&8K zao7>}`VnKd;Z6E#0$}70Q7hbfEowWGH`^n%_)u-u%eraw>U!Qa#K{h@DTw$fMqNamsC6Z%( zepLE>%hOUB0L)US_nWG@$uUsJv-IplKNyFm)O;%RMgshE>zUpm!_5&@anK(D1@on2 z-GGC46p9EmE-_8AnNx+S=*yhQ4$lO0K;5Dw_x8PBw>$M(9@f8tf4>c>8t$T;P&gL! z5nW2#TK~(Pn*Z zW`NR7JJwD_I_+U9c#%=F2#S z`kAi}n+xG}tA+3;@{WyQ_P}%7fc{S6h4lSAwNqs4K`bfU=`i$`-#7ErRM?EfT;j~1 z*@m0p>FF|=#|cy`K&LbzM_P-sFQ-YK71f_$6Wdnxs+n*&9tmF&3=l6G#`y)wE#(V_ z?2J$#%o%2=Il=OT8&s>*JcJ$SKMnEc!ZfQMpLfD zZlc7(qiR57lO5uUhgx`zA~uoohI$DEun0?%LS~s+1_kL=lEiId6tq#*U0B1b%F5ZRkeKDAttr3FT4;0t7S;s)g3k`K*QIbgf2iv;#5%nY-hq zxc5b3V=}N}sEoxrv;dAYldp}fhlC2Nx$7|8v|1p8B9FcZ1Qa^dBH1vK{!OiK9p)g` zVi*a~rAXdVhor5fWOz_5JR0c6z;(~`oPe3Z9B89(d`TTN`NX9OYG$ozg66!re1dd; z$OPi{=_Dqh@J^jLu?3W6YC8@YL39buK}Q0i82x;E|bGeKN#qD9jqJ%mdx% z+=T~c@yc`L3~G7YmxQ6w9x_9i7=uB%yLRcB<$4A2J%o0wm{$z_zL5msNsImk=+C5V zbs(5iRgOBnpm5zxM3gXyaWxm(Fb|`kZFWpP^yPHQn}L9zquownLmp5gf{|@k5sdI6K{{QbZh%8UibJUbp3@L5$aJf?R=v zU2-Hi36Vr{VQyW<-1SUVy1A6HPt>gGS1*|uBsPxE*VZMqn7T&X(LGk6S zNQY7_F{a^5VK&2rY8;_5McBGnTe|SsTdyv@I~7XtF)0F=%n7%>Da8jyTYsWgFaayz zzqukh-sMVRuk|}f_l?3zY@A% zQHA<#9%W$xo#l#uw;>1H6!%4KBN5-8WfU0q);o+HBoLJE8Xw!pciZ%nwefw{E_&KF z3Ls1&-a5xr8Ts(ysL`ft)fYi^isgtlr}adHI z-DXsDzD?OQ8Mxi%yv>T=7S-I+W8S6-uED zTs1%(SS))ORfxicC|>-?vP!WLI8AhAYgQtj0#ww5$85XTXRl_NR(kP1I6gYN>=NfK z3`>GSH8oa|tk&F;bCJHG(qK*@@@_rHNw1!hbKpy#Ra!=eJp`Hzd=9&g`xDD|rQI-y z#G&0>P8?{XE~^E3qdcRGuD^#`+gcG1Wv;aLSF7HRh7$lv69J5JB;`$>;8bEP6m0fY!2LG4mE+os#?E*_^;WX5mT#1AkE47KMKp)Xhp~7NAYy z%QP?{dupPEl$2dvom%ihEe+kJVMYd^tE*GT(?EW(OV==kS7j#IA0m|yAV(|R)y5_= zrsaKgb*hLdC)}&fcxC94R2*`D(v*;;zkyOmI6_8Vf)h6cjUkdeAW#XkDrG#)@}$;; zyq}e0{$YY%M{;PQ{blTUdcxQguC%8BNk~|AqGSEPU6keDUA8fHLEl_5t!^zKVGhK% zdih3tR?RZ+PL)^np@N83K8_Ft7t3-&jrP(JIus)bU{@HK8D@x#sqX-Da&i1U(-*js zdgxTyxTFOIhy`$*rLnkUC+BXpsY(}>R&=t{FdmDvQ1b0wG)IPh{Ib{v>n0>msv@r6 z8k9`HS$P0VN`YNkW?2;hX5ChmRH&d4t-VYs{y3Vkm(ahf!*h0VdA06RX!jP7;A$58 zlT^ld$dSWk$o-pfD)X46XN2W@%`oACLN=bE!$tpKZ%8}g|KwiL|z|0v6jtYWn3Y?NVD0o=Ug9le(?iv zNL2;_)sr%P6tQtY%L3D71Q5S-#qFlI2_LTG5=}#hkUV#jMw{yN6o3gNQ3Gxf%5~*T z(hln!I_0c7zvM<4Ng*%wr|8jG#W*N3DL?I;bDPfTX}Pi!s+=M!(rrEDDDy2Z)Ya zq6KT>jsEm|@BDFoc6Y&pmehW)}tQSFMcj-x&?$|Dll;s5|17-~uI zaC}oPJ;#IFqSPD?*~$6s)~l1(dJitx8u~5v>f|--RSQ#xY!sj$bZElMuP@(iyuNtn zrnsUJA42_1vT(t}+a?5|YC$H88$@19Xvu6xy%WudLE;eDlC6+f>GWWwP7fZ^>A^!g zJ$P)V2aoCWFT3i>z$Vq~bXCUD{eB8)-es5hUPh z^@xmTvo|$uFcN1Isf1~!1;;2g33ZmiL#O4mAD&Z;03>d!T5U3J_1n&`|o7TktW=EL&b)tk{(3tBD zv#I1GLi{vE9f+0)%*O0gO=hq>(F>_nKp1(4gGw?oGF0ninaw$+Cst*!k@$_6)uSD1 zh2(Dp`r+LG4JW&Yuu+|0$=i_A(=$F5^V_~4bFh>7Qupr}McgL25LYMXO8CHAY7wik z76;l9;HM(57v~mHO+efUv^F)Lx8L*$Qi`jr;Dnfwxx*LXtTG#_1!6F$m4;o^3=Kb4 zE(V#*>4TcX(X=@t2S(_e0&a>JHJTDQAjcz5 z4N#$^|31sH>)nN^80S0JC{NKT%yy(&nV}8E>E4gT+Bz(4G^K0 zOtx552@=|1ZMiY|#)oaw%~6EP;}~nz8RwX+K^L1}jM)iD1F$|RRrn8iE7Hi|csF-Y z4`Ndcmj2ubiH{z=<&xRMNZ0FB6la;U^)Xwv!wyjn6(Q(r@Twk5yDw|>i(e9vDzK_% zzX*|l!GB)#T$aj6K}W{ek%mvDN&OcY>wCB$>BsmoZNB*Gh=evysPn@dOm7i~9e$^fi}O^B z!uul;6+qDQ#_;&;M2oGX@E*YQ#sz#GQM?e5;7T0dYpL<%yzy+Kcfd_$preZTSKWjm zDE$7x`H5pnsh%yzhkeLFv*Sprg(a|bRUAiLWuec`NS8aYTFT4gvy=65=wd-Oyh32= zYGyt>&LrbWf>4{}T!>~i1oQapL|IB>e-52+d6sd1LX`1f&@w|;c@3w7 zZnDn17s^gKWE^9F18?8MQVY%tPAv*=-}^rRijGzn5&Q=(ZwFH%(~VgcGl~5-gE5?u zEZ1R2JRZYc1uqn?yNr*mHc}Y^%Fxxl3i(qql+$nZbm3%t7^UAG%mq}adh+}Ik95EVTJpyk;G)1dO zL&y%lQyYBB|B!K8++e7J;i-Kmh7&1oib0@i1bTvhMqvMH1}Qz($J@bgm#~3O=qsG6 zmp4D%uDdX@LFi|gB&{D+KSF=oSdwkfWWQvRrWj0cr6sTOVJxzfqZK&BSJ}ytn$Hlv zvpO3m5J|=3Oi#AhuT`7p8AcJ9v4lu%9>cjr-e+sGBmV5_2BE7cU52Hj$T~QaIaOZa zcuvt-Zabh?C2(lT+0B6^6&D2llQ%=Lm*s?}b#t|KD#O3p3Rz`Ad>{XZjXzq9j}Q9? z0Z%fKo`%yf`!(eH8mkLRnAQ%Rr}wS%!(Nzl2Vw)aayAbsP)qhtfeAzkrX2N^NT&+0 z17SLj?@ibdKEy&6g@HDfZIk}xwLF8V;O%>7GCPUAmWSu7xBmey9Pjr}`hSSe{pGL? zF|70&7p`CoZ1|Vh#mZ5v53g$3ZgBr1>q|&G8vBp9?4j^ zc(o3}Re;a1t3{Gb1^mpRbm$a0UVz3|yj6m9C+ubH!hCj@BXE|uU4W$K5Pc&RSSXj{ zr6K(Zy^j{lI=xlV*N|ErcdbThWhx~Cs6&>+#P)V~c47A%p0kn{m6Y*=^Andu7({}0 zq49*&cH@A+*)y6v8^-kJ>gpVWDBYYoikYHq+hIqN3aeQSsuo;n%JP}G*&PY*-E-Z+Y255}w%fg(&At9$tGhX9?``i6#i+lv=@0zDVC0X2olT!_ z_Vz}7G2Gf4jYd5&>~k>(+VQxuCmc*i9JVOD6|qq2Je5Py8Sya6Q*kv(g__7Hs0O{! z?<}(~$PqInSfXjnrJXFQ*=oURY+1I6@NaBV|NJK8xPw7_WnJ*YkAG|bIBowJuph5F zKZ@T!{Fwdz;m7Fr4?o7gvkyOx_kaB1clP70bypG%qTskJBMzYVdexc?Y7@Ud6&Z(6 zPC3GH?o=2{7W>6GGok!6O(s3tWJ1}c4ma;cAZ|BQ7RbDGG8UC=9FJvflq=g5b|=~6 zr~Ll9*mc*|pIK~D8y)(R%nR$~_Ah8_o{O|#rP4NBOmFACt4i72nj^E_uM?3v?3G{) zu`dNHPs2~Km?n#4rKg~p6oNyU-~+KL=8;*5Md9%zIY_56Jx`@xVp_(Y=c1j%s9@-INi zn70ZFgt|F)U^JuYOV-=>_Vxx_Udt;(59t|Lnt5i9MW}tCOu8ApTtizjcT(y;`k)mQ z8~N((d;juh?~~>R#B!r@eDJQX^?7z(Y?SLn2G@E^U4u&TyS}8-vB*v-8&3aZ6^+7c zcIe7-=fyzLT)O#!N?MnxLwlQ z)hHXBfWo1VWSbiN3=oe?JNZ;FNAJ)H=qEEULGMkjg^qR-Pb$Qu>1dCgt zRBdzF&_5cxZ#D9d$BBFB8v0y_aHVaP*0X7yfiJe(YJ*dTgtMC0XFf(hJ`8Q{$^B0S z55iRh|IN8_za>$V$}7a5BKa?BWIjL?_T0Q-jHv%w;6<%md$nZ#-9R@^+cw=UB##Z3 zH5obOAT9onhIGWO1nudWY{ z-kzL!E${Mgm-Kg7e{N1kP38hhA1h$t%^V~K2EFm_xXAGuOeAGwiAdand(eKT77$nB zR15C!l7`FB152dV_q(1g8LJZiIz##L@)*9ID_1V+Bj(~QSChBr?iV6~3%SF3-EDTy z`U-=LBU7VX^CO{p!Gzz2GEe92gqOX3S7Ep)`**h4W%q1Ttos zW$9I-s*0~Ld4VBob#bZD5ICn2^PjGh;TZ6H=umAe&x{Qeb(Kv7-xn#+j2(GeN#y?G zlF05bRx2St0^QOHMA^WK9ZF;yKLBOjY2LO^ogUtgMrZl-D$P|Uf;Tczr#omVJI1WI zvVow063p-mHj#-UXd@F?c;6n$^p2;22-;9;6tX?AvIE#OCV~fA4KzEO67WuUj3Hmd zBbibbAX!}?=1Qa`*=a8CC-IkKK8zp7{o$(BlT8Mp{YH!EbDsK;q^m%BhuWuZsvvkz zv6he3^Q!D%zy#KhPcU(yRC^md$3}Cea1&a{^r>ge6J!%T;oja}cdI9Y-O*lOjQm~E+Zy%x_Gpt2_qMlpw)kLM@P6Qb29J;1 ztHSfs?{qsmbDTJhNWjP>QeiJ4q}v=FMXWN)8%1-^qj@*>>uV|E*sW&; zMq0RjAFT6V=IK~?`@PmfF_D_sXo5sC7z8Kht0dJN7jGAm16E|{l*s*NZi?jviwnz8 z4R24`au1EPS8-4|KBY=f2j?dbT)kAB3Sz&**bIAt$Ws=+wK$LT z(FkbfpDT7JqNv8pGuUQYo|?tJqqRJPo#yKFvcXoffbU0|YMjAc-o#4g8IvyUPFvMP zv%D!iqVt0ph@O6_|SGdES)D#kgQ3- zh72JsDSVfj3dfU6pGV^4+^5ozORhxh#hdDP!b#aRFjZlKzgYv7ZjCAZnXOeu z6=Ud4!b@^WkOaA3oaZKsniM0*6Q?Rq<_i(~JW;u=r|k1I)kh8qv~Hq$Q~y^)^>aE> zvc6G2h_+PO&tWR=5YA;~QNut~u@QY-C$~zCL#%aBnB?UMshWzX<`OQImnxdl%Ws9z zFt0X?T$6k_jKp)N+QQrQxeRLWAA>IzX{azQd z%W*6~T09L^Mu?>%o)!P91`|G|n;7Ls^EUz!73Zm8>N=*7MMa4CUpsGlXxi+EdX0mi z1?388T>S;IX+c3y7ts?7mc~Lr+yGubRJTbj7;pyB@us8Shf=s?>FEk(o$DB)#| z!!^cnJ#=`CIDK$BsJ{o~4g+_JUZ~ajkRj%|V`=ybi-3maxOr0b0spsyx2Kdpuu8wm zORC|lY`58!J|DvogUYE??N+453`sIBiS(e%UEYP6Ke1@^ggj=GUzLbPMIm*AF=s@9 zCa%i|kyEUl*18ylG1lvJDv=N2?G30ICIZArTR z!L|+TKdAEd90C9TBZnqZ^cxQUr+GFrSY6#tg!lYPo2$G`Su`zNdEqY!GJ15Slcj5O8LqZ0_i8^1rx^1CDCx3 zmfH&!VhJhA8N?t9(*`7$LLnKjX&^qSOu+%yvTy10&6qnn>=;uD_8DU)(nD3@Vuo#d%^Hgf5~2{xU%C&Jgw zuvtEQPfUVYOx)b74IXOy6RiNJfYgJRz@sy5R&L=lW!S9T{$C+orrw!Eh?HJ>Scu_O z0Zn4U3@LmelMIesV1R^=bD`BXb2^=54m2TVllwd>Bb_wxhXYWyRcqlH*n!{JxofU= z8*QPL9?Hu&r*&Czc)@CxoFo+^k)9;CgQnB#H$pO?UQ(y<8(7kDEc4hGh^`=0gq9%9 zLjW)~q|fQg);Xx0_EQmXziI@m3Ss(8(^56XOp%z@05mPkobs7S*$!ID8amd|tapfS ztQA{xB$^=6QfN3DY~|g)eG<#8D3RqD^|=rF@EMfd`UoFk#`H%$Fdv~YC_PEQtmqqp zS)n@2d@2a(3!!ggVG8tHs5eJG!X`mLDMTeO^y)vlzxW$7oST0#)1%@~8_>4}72dBs zM{ErDF<@@BsitbPY-MwPnq;byNJUd*B`T;;HRq-JJnHu?@2!I0R*6S%*_zOkwDOYB zFYgzt!2I&pTn*S6cXki*Y$DU}58P(%FHlvYUHSbh&A#UQC$GdytAW3{Gj9&5MG91L z1iimFrR(P9>*K3+ClI8pwX8l7rhW7CgNN~n<7nD9ug)7LI=UACp%kN}UxqWf#YAV^ zX|aSWbqCOb6A^1gI`kROGlTyWJbLhmtFJbA)c*XzgBz}c*V}4K>=*sT3k9(YH)8gX zp@GKC*kGGqVPxDcHx5k1=ax_W#tRYFtNW2k3{v;3p_|X?A8rH9)xE^{{(7COAmmYj zuUId@7xKs!4J(R+nOFrBpGmm46-3b)CA4&=rRb7~Mo`I{UFEHJ>g+7{#k&@7-EY#v z%MNfSa<)uZN9tb^Ifsm-Q62(?)crk-WKP(+oQo~bF;h6ce_Ke z83g`dmj}DU(N=%Z7yaF>!S2lB=#4u7jm60B}CaP(7A|q);3mB0~=(i?HZA1%Pab_DMY0{ zJSCN=wAgI|L6!WsmP)jgzt5dgw48yzQfg7PHxEoPT5Sz2E${=PtM!J8f-LrhG>?md*Dg&2 zj+j;>;SBxxDW{jEs@{r;)trFnc%5-;e{ znT#)_%uM1EJbSN1I#&oObfM*}*wnlh zJEje%l%8%2BMq-?YzE%%7VX)VY$&5Eo)NJH?bXrqiry5B!dr4=+d-hOBFYfg2^O4b znIf;25R>UbR%DvJ%10xS9;G~t7wnkdOn(!Rwkg}2BGxq#HUugrd1nN{KOCcULdAlV z5}9tnlN(M2!BSC!a9JW=%uDcta6qwCMP517orxlO*PhBiteef&Yu;-`zfMK`mfts? z*`**`>jc5vgI?Fxl&Nn|S;Tb>8TZ!sUOo z=={y9cc_OQfII>L<(0h_;m>#!b1BmDuT?=4tXfAgX1Y}^SErX%1flAVXzFr@W~tKiT7PvDL$1VNi;38mT3tIoerwUo zn(iT@tZ9X#(C3*z=+88~)qlScGn5H$#4O<DQ(JgIfr9{&)s!$@fE);vK=pYw<#7&oOE_|A=zGT{1QXS~m^#Gx zypjDnYvmF1Nc~tej|=+WDfO#n+J#@CTo1&qI%O%G=I+o!f!978i+Fj1&Zy04^W@x# z6M1yA&7~^lSEZ=f_GJ9!icnM-f6ihQizVl$6`)*Pf1=cKQ{4&TMBRkR`}ii7ckv;} zZzo5p!7*@o6DH>1!Q2X*nH)c5{#bv@!m@;|q<?*gn|4JEvl`c2UlQ|$NuF`z<+8(+iTfm^-}TM6-eK>wstDUZ9GU)vig`349<19qj9|HH;e(~Z&r{&;f8@|c z@)3}72LO2m4=5N9VrXK~Ruh@Q`wkpxX_(Aru$`$+3r<4`cWN32aX6l2rqb*vOjV}O z4X9uWdh$r-DHPg?vk9V-t67}!`xbL;#SgDFt%s-mKXRxxelWAvM#|NhT^dv{%>hnJYKW`{4YA)~KW zh+wKV@xU-9r>(iMOwCO^iuzo_t3(a)?&j8TN9^s2f#5rP0}=EG!Dx51C3bqF-OZ8T z8*B}>d2cW14F+N~8tm}y=5V*)_q)Rp?`=PS)KDI_sRKpmP`A_D?DQ8$4msme4;pet zrHlZ%+&@{tc`PJ0x5XmtuK2Cn>Gl78-qNkW(JW`93bpZUe9ZSHU6@Lx){v}EQ-l6Z zNAC-w^ulpe(5?K!&ghGI4lRe1<@HJH-K}~=>a>-3pH{r88z4-XiVpe}mZuS<3WfL3 zFMVbeyA5(<3#iuYq7%2Nk;l=WPkNO@cTa5Y_`Uw#V7S}u?{0Q?H@AF$cV}~}&-Z$} zecvArcY33d(EsV}?exU%)?UBg8wPv9_TJ`R|8pF=4?b+|h%}zIc08Vbz!*}GK4B~` z4>@9N_m-V84t0?1LQGJc3&CluK3bzt<=k|EsA)){)JSL{UKW;w@J+Z?v*SQZ?vUaN zr4`x~>q42Nzn6oXz+>>?ch_{P4>96nzFRmC|Kj1TdH5W$t$A3Bu0X=yXjJPQ=Dtu! z>%7+gg7K`_mB$6M&RI0i6-!xD>gEJ00gg{SSV@iw838jq^?_U6{11!Nv0Z*lm`)xp ziP5p~y}fR?R}RT>o?!Hiey~y%{9+6m=BhaWr*=-4BXYKvMC9b*3gI|KS-9{1MY>8O4(u(JsC3%Bf#Ldr5<>sb;22&aAj(5J6?#i@IpkIWBt=i z=Inp;sRl*(N}E}qb7Y-0=c4`eHsoOHZ@FF3>%$g~2xKhawU3Y`nJ43v2LiLX@9M;y zBJF+@l7X23FHIZ{-F@wL9#+p+XFebHLD&wRUP%W@L&dhArLtAR9!?VTKz;Z9AB$k}stHjV$oL7$up{d7N ze35t>J(PdFAPeC7GelU_F8ePKTaky=zzUooD!)BDh_tGqhv2QAjB;6*A?B((V*w=jJCw!*>T@iP0c*0pkg=*Km7GPk0L@=SXcv(MoBYWKq%-< zm`5h)-faP8E7CX*tqZP06}p@5;7efvtQ}32KBO~7pUQ?r!%GExMYNDuD)UU=Qdt_( zc>%Gw=0y;}4fgEZKH#X;WO|coPFy-54Y(nYoYQPaCBoOooi*gMG3B0|sMb z6)p;tisxVj4|OO@a-Pae`f?G5RHEP0{~up8=2`l!Pw6rWZx z1hiuYDa5)OMKzE4MTYzNOXx{RDNU?E)&Dp8rfPIL!_Rn=;Y}e`#wPq00X?Zu zkG|{ShcGsS+F>WLFQ*{V(3ok_BFmD(Ze>7H@-$|f-Cg`uq$xmr1p+VBROrP44rhCk zhcT7gnb$3Pa{8)JT+YE%UpC#E?7q%WWV}0pa*9}}C-o+= z3MU|M*k@?1gtB}Iab!SGcq*(oHiAOl%+<SLo32&Gq{H?!Hx#^PWzhijIiFa{HsII|Wbe9<-4 zM-D|W655xXA5PBA)AB+m_Th|dYKVk>mur;y^^^9ndMIhF8g?ff9|bBAXIiR`1e`JT z&#KqB^%^Z1O!}uI$lp-SvFT!1@QybrZ}lE9-C(yKa_?5yb+p( zw!^}bK0nF6N7Li z@A{W>yIF*5I8m;>PJfHyHCAKq*ot!^*{O8^f<}ksF5=r693+X+_0&|wvT`FqNdlk+>LQOs zqwD1aKVjxYmcpC*R=f!l7K@A|Rg=c8M^95Ggbe*{A}#hBo$Un9uKyNCGbSMSR7?0^ z5hRTEP+7E{FlIrR>H*LiG>U}*yOZ$;WF0l*ifK#kTlzYT66gWa9w zMxwx(#5nUOcvooK}?Cm7N&dCS$`(em*?5I6Da{O2~}A%sbL%GKZ7IFCq|Pf57d z>m7|~7bw-al^@7HWn8Qn>7J1Yq+2<(G)!brAwyT(WzgodZgE3DLOYQ%s??`BIcEmI z1=C7k$Z&2-ci>#;47&G<8&NZ{QLtl*YJgoA#aHanxeQLu&!ucU-%rk20za8#1(Jxv zm`(F23!yLU(9+c3y^L`Gu}w$BgVuF&kc3^egm)LsiEw%6^z|2B3>6SZu>J1LLH&&kK2 z-u+&`OAQY`U;hX5`acS~-TnQIUU$p(fAFmRH`4pT`N^q1YhUH5%4h_Ny@6wJHPM1U zZor{_4~GE`9oUd?TEh`$!|N@g^TGLv>ac@INrnl56rG>R8xgZ%4mo^2Pq-guvsF0# z&-2R?k6WD@lYh?zSR)fG79oxI?a|Bz zJVOGOe+gPre|N_91#^Md2P}?yK*RxJRzoL~DD=ZDnw9Fi7CTEXml#NzE6JpcQ7x6A zW$qXQBvnZDbXA86X`xrmiSyK#+l0q~vNA~^R0cN=e#xCx4Yr;z&hKqLhRV2qQ{e9< zNi@SNbH*;u4z4sRg5`vzFGsimTfhGIISiNC}!NHyw;dyoxX0 z(B)Q<^#v-uU%IiOD+<>#_a_k8VM7G0KT@V{A|8lm`U*lmL5*t@p;5%gcDW8#uo1&C zS6%0W=UV53@9X^gI{&=u{ARaFTq^`hr#Du8D`#mFl|u-G&;l`BdKK9lt`lRK`g@`r z@UpTDPVp2f41{z0RrD%7)aJTn>;yxKIfQRu;2Q-c(~5QafY9j>xhw8{%myfz>nj>< z&1(@1nE;@QEu=EbA}1Caz=$J0qgj};JkCP6XH$^zhZqS;T{2~bvQTeanP;6jL)yVR z*$8~SE{uwvK)kklo&KQ1`o-g0kt#hyy-t7U-$~*?8m7&ZWN^!RA%GyzcQG2qRfM5Y zYJ%mDQf0~~a7b(vDSR@w3x9X;KerqC?=9wE<%-O?EA@fcIK$%sJ?_h;7dp^^kzaNd zD}8Qgk|AMEC1ZErkVyfYbLEfg<%s8fjI}=X$3i4WLZ9JUPqg8xwF~MOArf+#o0pl5 znkGBkT{*;x4!a{RrZT=Xe&2F~koB~3g9*O9q#bM~9}<#ny~xJ@+W`OHhJ!Hd3=uh5 zp{$0X1Rmpy0yOFkVCetxDvSfIA2*CSqU`@+P6wzh?G|{|J=2ui=*M;dso)k7#_fTr z`8MtM1?h$Ry>5Sd@WR^5wK(i%h{EL&D)!>T{{HU`FAs{BJM7i02!w#pwgO~O4?xsE z6XAGr6e?fdigbkqf7)lU{$waqOiYGMY`U!q8%T|CDn_YLlcqSGO)7!;jv`+QQZJO{ z-Zq*FB_n`4U__HDe3>2@6sW1d@T!#!JEwhC`L<4P1wn5P8A>=kKCj6g4C0u- z1PSHJ2$l(+)%bBK^OGu1UQP5ekx@_ueKwUTGy4KmK`teO)~P1Vh3Gbcq^$SXS2Vuv?G7YoI)Z6z`na9C(Du-e0wLFtvTly$L zH{=Jx@hBFPxjLNS0GQSacO93XO3;C6MH4Q)M!s{h?`E?2wP;ss6%ZbF8*=tvN9XJy zfrJD-+Y~{HMF;nbOpTX;gt8x6zW%SH^YxaIm0xNCug-tgLp)Bou^N*Qu7%&+7L{<6 zfglZUMfyT9!P96~V0rLtvfcuLS*WZnMk9T_zSON!dti0dZI-$%^)&|mK}t{|K_Sd2 zh_N)V(N4{i{K%OF7ugOwEiI~ga~<12c^=)i-0+dpSFHHr$_cM4{eVt6XWOHP)EJbg za3n=I1|l}4TFg3d;%=}sr191`p(4X}w9~dtuGg2!K65(JVv*n~yC#vZr4`gbP8)~B zWE{_5*yaG*nYUx~`o)kCxcf;mHe$PE9HdnXl8D1H;f}`S48fuiXxWgqkxcJ+N{K5) zJzmopltFM}+}I!cy6K4tepMZIuEz{K;TTaFNUC41Y^0FupLW2pOPMQ8Q^Qyr2Wa0J z%;kgl$RIjEMy3heXGi$Ah8Bl2c87&!>MOBvw?aYvdhE6V%EMnF%*!Ujpuf7re=6`y zqAD6gc`MAkb=W~%ROp53Ea;*WbaW{}t=Q1UIix%6=P(xSF;$qQ*e$acxjzZTEr!!z zP;~QtG^612=z>Ta9cR8!0SnQ*&d%LyT8=MgDic!(ds_ethIcJ^wZ~%^fe};iCC;HH zHs#|;+%B|D&?~--6+|;;decqJ9Y;0Ym&=Oe0ypmyTzSVOvVyQxq{cK;n&*1=ngX>k zxCuYYwg&fut=BvgdNn^l@-32N*xA9=)?nl8;A*hN#)eXz`U>5=`1x?R-`j3e+$1=a zV-*#qASb)5TCMOrl^HCtG`ryO*aq-vU5kDLkqDn6FQ~X3YH@4s52uxADN}8A(cql5 zFWpm{6~T&T{g{q)npkftWS4+#(gs6VqiL92!xpwxez)K2ZVmckw7a*pvo{&waTvP1N2RXyxfeA)6Iwj)oe?okX;B7^E8dU)N3W#ksam+>Sy zNT)JAPo+Ljy!}5-wD$WLw4GQ4R2A74H87Mahx7xO=v11mFx9{Ti8ZBC7!X|`(B4~9!KzE#lpBbb(lw|PRK&WmzK~DqgrUTf9 zCzLzGwa%n3MojUO_Hg{EO7`e0KFr(ouHzQ8bX(kn5QRBu%D`|e1;qi9yl)R`QRv@?8 z{3!TG8z0+Hi6Sm`nHSnIp;XlB>m`i#G1phbN6-jN8*DG)7w%V$9Q_ z)**ZuMQBo~($|{GK{{Tjrqua1wmhDWbM&z`kX@ppRjDq})XH?fUsVmPlCEomO>`eYvP#WV`k1#Odd*A176@6oB+Q#{1W&V};8_!foVVFE&6uA{ ztxU|vHTD~HS{{W%KID-0epJzKx3)I7cz3ut7;SIwj<$!x&F*k-Z)e!^cQ(bYAM6c% zu^agQ=4ja88SV~7TbsLsu0Q0%EzWy=eQ@$SRi!%E>IcEzra$Ng!LH~HdVIL;Z}&HO zZ*#c2)AxrXA$GgN?q0v!-{WG?A8v2&?d)-Xd$76J-_)ls|07RDmF{S;y}7fuyXE_0 zFBt7~`+^UK{toy3%^lwlv|_&FZ+C;;T`}D04MxL$Zzu5kd^p(VJKbGP7iy)4evR@L z?{9YdTYMmfoBojRjC!N3;b7a}*&F(Mq9+DB+dI2EL3fMy`u*+AVASsi!_lC>IouiS z_MfvPw!PY0p2|m~@E%3Om8!N>*SMq8+a;pKorj%wRLufxNK4oEvzZL4efhGM1KXdc zSPmMxplZ~=wc?-)dHdY8K^HgvS1Jwa_T+&UqLr4-LNn1y>go#3JR{2eLRI78AonYf zt6|*t7O-lAb{7+VvY}@Q`DF96`%J+4kL2draP=3qpNgrx zg(?wOraETT0&oGZ7$XG;sbDL_a4RR+9}SWq0!;X=V4NKOGxM#MFsYnCr^-gPXmMpqNSa0!HKoS-&CVlPejs7r4?+)cfRxwVf|pleO{4O8dcX zJ*DLlt`|XV&%j@dMIIs$itI!y0JCA9K?Jqk18`4K43NTMgH1(f?)JmN+FzlqskyZ` z56>(o#WVFqF3WE5<4>tJ@s->vPOqM`RPn+A%wwwHaxR14c;-(;AwATA{j;0LwjS8jL+dqP z=IK~?`(A(hN3T^Y;OuR8fAkg>ajworlLsuBX8EFou$q#_vk7~9_1;MbgHXXZU?+!f z&)F#DV;jiUj)e#yC6m&&rIX8xqw^FC3?`u21OMT=c%8r(iO(pS?aXQS^M+(V`}4yw z&_{E4a&+-lZ-`pWQ4#{1UW2-T5yi!WIooFIlVJ$$aI{NKjxKDVVuv6mlnQMM0l?S7 zndo&K)~N8UUS>@NQQVvz<;%!7NHm_&B5~ zqHsK!7mW&T_|NNtzu8r7biPyc{j<2-d}L4I|&yZvUp!qf8QV1(9Gw5i#m zwU?y=n*rD@)d>Bsh7hPP2FD#$jKT9E$Jx>T zvaX_{D^HPBO5CeD;cv}Nw3xZi?I&8&`d`UWRO-qDrBgH=I}4|}xt_&QR9>33CY~ku zbzv_SS;vYGzk*$?VDafzs)YwYCekTYo^s%A>MC(rxN^Q?WBnPWPgai0qrxbuCD%$3 z7l#Qs`g;(o^Hdyif6_G15dD)$NMgaDtdH$BeWvA#wy!_@h^a7{tT!;neu5Qdy?yUY zk}M}ILY6*{%NP$EvCP_lljePDE&sTk%BV_V%2g)PM}JO9we2M0u~>;p2f}l#ZLCZ# zk8SD>WcS}vn4-RFfjZJTOhYiP9t44m)w?*F>AU|Srl(>}d?lk(u@)W~%v8Hh2#9UpNZ2 zWTus31I}lv=@<~e!m4biYRb)CdL{?8lL$E1D_y|a0Kg`>p1l1J9_;_NABSx)f*_zq zTPsBEFfjld?P%DZ@ zawRvp!STMLlil(3HP36QX77_H_#*n~naA8D*xECme|4(XyO2B{V*A7bvmLemiN zS28fwC__{?z75oPWD3g25%YKyd2k1Mv5bX>Kq98b1$`wJ5H>!Kan4g7XAa0A*m-OX zAmX2@)T!z|>)45z@8ESMR43yYt8fvTWh$p7_Y0DR!6^@zxKC`^39!H=JmqA9&m8En%G^FRxMP(zK5szBDjbx9I2?idv)*NoiVs<(JLM~K9#~{1jhqJs zj72m>0Zbp<=rk-}JI-dV)T+5@Ros%gaiwiLp z_tw(95vnYd*Fe5n_U}ahL@kpur&BQF#_!C}p(#pqYDsg%Z)J+knOoURp^1Dl2gPMo z;qWyWh-5E{kz|MVt24fKB#87NRJZoo%W)jbN0 zy#C3y_o);}Yi(M8$EHz9cXc;|0bbS-%X~KqLt`|Je{q``g^*d~msJAETsQoKqR6OD zdff+{<#8+`w}>BBZ)}^&>QKc|-$?MHdD3C$CHFH`u=70l4;6saf5p#$2jyqgyzhqk zQkREU=k{F3L*!RfObm?`1P&5fy#<`kP5BU>7HU4 zDSZj%Q5H@B1{TM&V&?3)`T89w;eR<(eipsE6)EV5WwmjobbW|Rap)Go3D7`WK?`kZ zoA_9XQCQ9?t}27{RPcBB=G||aw_i?9Pb-xM^xrQIe)C%1$@$^+>AS;Uh;3V!pxU{M zh2@=D#G-DgZGb~{TMwTZNF6;ymHJUDr(={wa(wp?_gaw;sSKLIDiG2&-DGG>Ql+gPIC8@kN@AMZ7F~9}ar`u@RS>v3m=; zZJvZ!HLeY5o;(w6-IgMuxz2$>G=h41_ScT3)5#(QMf|j`Hj%+kek$YtD~F9eoaDn{ zB%ZZ%NV2gvZ%VB>9l>uN#QEyQ!~!U8CZ>r@c{(F>iU#9EDPW-ON8v`OR4&v;Z*y+| zxF)8YoxZ8b9Cmnm0=H89B4Z?JgFbjr)ER*~9W9nkax|XY$@GRpdEX9uqcu4)z;ST5 zpaF|e-Bm6+2w|*uh#h|u_f^Kj)uci%xk}X6-GpZ( zbJI74*%11L*gb$4U@cZgRHpK?s>2L%#C+5P54PnSEf+?L{l`yiyVL8o^s`9biPZjb zC)kKb(U7opydaf{*Ca@_88*RQlEHEv>h-2F!6Ar*NDfAsNKc@=V111qNfW5m)=tC%3mA7VKC@@n|ob9GEpaR^kv4(oNd**)tk zTg|ly;+3$*Cj2&(c{)#reA(-F6@uQde`lLrcF#7|dY#!hdTW0LwY9`CQoi*sK|O(r ztV^nsqD{!D1x+dwRB2W0rC$ixur&Zb+9?-GMMu;u_9v-~!#~6?yXp+%IO{1U#aj)z zOOoB(s!W>FP9h!2bP9#iL7cf_IwV-*+fbuzVzRIck6`6WvQ3D`YxN26sI=!r5b-9QxwyzzZ zSG)V5j)JS5D>2w&BKBn<0(O4<)_CNMfO~krG_i0L`f#$wC}!)w-w0CeRyV8XMz5CTgA?Fo3vVnR)`fvmAikqTNNM4GkH{E@m?nYMMX z&o~^o!`RV3GoQ{a&qgbq6y5poX&i)eWzvsK?sr^s&NHm@%;2^}ai;fOSP_t;|6 zhX=Y9^LbF?wmP})^|1%{$7}O#dEjs9Ll!uTE8sKbhDo@%5n5O)7-5G8C0=esQw=M3 z;7Y6ITNppw+)EIDp0dwB*3j|vPDmj#5!<$dOPEt(P%2hjnlP=HrY3QzdAn2(_b{{P zzQxIdI{QTb1@iuajY5HPg^^GRO5A0jh0t*ZlJf9?r3MoNq`6qKK;FerkGVd~E0#!h zjMJ5|Y9Lu#b2Jr5GfQWd6imWfo|s$CKq@S6V=m&pJNFRErC)q{dv*~u4b*!j7-#gm z7L5gmS>r{E1vL?q2&ba|1^}44%g>VbiT(Xbad&90 zK6jIK;2*nNJAL5p*Z(>_IBVg>y#kU8C4qhV+oPM@mY5jiR z{*STQzVp8lAL^!o5uge4&)(`cFzYz6Ikj4LDc-3x+~3~rnE&fyTX#InewOJk1`o4o zef-7=UH6ywua4X27w`V-Z|%c_vxAGju`tPS?W8#tMBqsxc&hiVr%6L)(gG6GtLlLi zUJ>y;Cg*)AD)lXaOFKF>WBF`_D8Y?%FJc>5@-Clna zM?yr=FR{Ff$-NzlRqzuXfkG2#Bf@@IkYN8<`={@Jp6?G$-o^33(c9zBH0X?FHcg^u zZKK=mb_ZKq@ZWB?TmA3$R=4+0y{(Qyx?Vu)qTZ-vB1|F zUeee9;x~Ab{92>5wpNr(k%@?ng;B{-20|`|eQ0&n!o{aNRtZPLBe@yl@tT1^^<^A` zBBj-A{eBsw%GgOATqkA|{+P=B3FB(b{nc?MA}>Ax3YUsQ3&nUwc&wWnTbR}aTJ~C& z@&cHpK-tv187FB0??ixo`o9L;6-i}U9OH4Y2H)5-R*QjYxjRN|wB2EEuCC5oYo8)wsyc`amUcLiY^P534an}6%!wmJH)H=pCPgwO^rm`lb71&n93}3wH7+E6+$tZzU*79+mm;#Qn%6EuQg^| zB)-5e*D_i7-0X8a6D+-)fCet&7M+?3yS@Y>ClNXa13a05fI@{#m&TP0)zqVY_I`n4h5N#gPP z+S(64u=7-aRZfMOP-|=d=Ge~n*~f-`sD%&Oh`0wouKgQ$5ck=~|M-b*cDf&d@N1EQ zX9p@0iC}H*Dz&wlWDHea95a0oYQb!n8>1Fx*!Q;CG>mi2%i0=dpX%lz95ouX-;#X% zi&iJu!Ib|YV|M(@CHn_^E#){8?Da3lkhSWkQXdKIK>xV*kNx(3?f=&P!9M)+2h+qu z#vV(^W8#|`?$Gp)3~3Q`s8>r8cA@K0lwO_50+J{3B1Fd0DQly-Qo<2LF2=`Zp%I zuSmEvi8LkG$o|^e$B!S^{slwHQ;}e509lX?bNWYzIrF+{g5EA2gsk1RAQV4=wuISE z(A*k$!kt=cHWRVMW-=#FSQ1IH|I)Eci9__+1S%G;0k}+&F7%;t=&{#1m~j(677rio z^-iNar4MBktcYf5PKfL`A=qeq3~Xy~BRf%L6}JgS;?(1ISb{t`|I7 zFUOG_V$4X2HISmIgh&raDOGYRWb9`BWD+=U9y_#Wl}F=i3*cY*4{e-L80o`dEaGe< zkt%~l*cNfv8FDpwVjD~Jzuwl)Rz?3CZ1ua}^}jFiM~RHJB@<~X?!#=2{+Wb{81XPN z|BhuIE0L|O{ebQ&6B881?BKUcc6@dMszeKmdqm+4`Ve@=Uhq2wfm$#0p}F=0oA7{f zI2{no+Z^PVX9rhh`xmC0Dgj4wDpVe2Yd?U?GnGk#ILKP6ZZRd@Z1YTSdk8(lKr(%J zOgox&!2-#`i~zbhdjU9Nda1u)6De<)3Nhp?R8U1FrbN^W(7Z{1mdtvHWNR zWfRy?D~DkmW-q8LsHUQ_Fp8k(w}J&c9)ktm_7H;XnBpUS2K!uVW<}-2Sg!rR0+EO~ z5HWaYPzPt4cb9GGd*CL643}18?yQ7#-bU-dqJdnq7Tk+iZJ1rTk;Y> z^F!TlpQEzK8C7pw@2rI*_TdBb{^g~av9{vi(HN&9VeMnq_SuVr-!9vFTHA+j4o=Sg zq5paK?(FRC!No85=*7Cn{`n{7bv^d`@BiOgHWBd}V~h!ZBH4>05ec}Y%Bz9`#R7x$ zsu$ql*$Mmg{E#J4J`Up-Kx8gwx2tf*{!tJBh{IH@jlwnJOvd7$U;MAQI!foiYc1X= z0Icgp8~zrVHp-TwDQ{`|SdSUJj?{R!L0i=bKp`vCu9 z^b~#>=3$hb#NIyZwThij-`5!X zq+j1E8i9({kqA@hDJRRgjuD}-Z&Q7N#J2UD}0jjWO5~?K90@E zdSNC^02u2Hx`WN`-u7N^d$0??MqzxTC+x%T@Xv(DkYG$xW+u$zQOFexVllfo6={kC z`@`;Dchv2>V{}J`UHz-y+xB}qc96_l-jD-D47*vg%Ro&|MLfMTN(40_qi^hqNvj|gKPB82LvpX}pL zPRlkp2gbdjy(n_`1w442@*sqaa8``RI1PhyS(y<%X4~#8Xj*;e!M#g^F>j{-UE^XB z27!n#MH)7YI1Js+!2UZDV+d$;`QW$X>US_U5U|_u%aq?YJq_cAr)nba>Ki?i8IKy? zMwQL{Q0r+F#_+344IbWw0j@yzsk2+)Q4IAQL#%bH7nFWr*O@1i@{!2Qf<3jQY78aZ z7fs8*sFhcn5HK|BC6X(5`tf;v{!%RqrUEwZD3V#huYw7G=dMu8cWIHsiJ-s76QJe( zHskl9{;gjufBIW7ZfagHRmT0B8pkRVT3$a5<9g?@(7^&;#eD)@Rr&N~$}{njY}vrN zx~6kVUBd$U^JZ6F^CbD{zbWSHzr7YqWbo7f`)}`G5*hrr_vu4#`uqC279L+FGH5}J zUbN2sjSafxff&hj%JpLRv}k;dgIj*Z#LxfCvG%QdAKIPuv!(OrpU~K+Ph~2p7{OKG z9e4}I+~yKg*05&o=M;`CbC=lX7h+5cxbnf}BphY+FEY5Kyki(z_OFb*dquCk%wAA` zx;vVuBB<|a1!Wpa=M`+{Yje1VaS+~yLC&N4$!*?&d$C~Po|b1x90Au&r91lkpBnKhv)Cvdx*3(cK7d+_R}P7x^BL{9J^H7RuesEU!Bh{CvW>Qig==6`7RKKOFLz* zwAiaii~X&Nv{*HgZP(Tm$b-E>&k)ZQbySqEsDp_I=Fo%%+il4Bz@Cm#;+6(qZd+y6`5B_UDZL!V$ zZfSqM<@Zf=w|BR;cD@^Y+1Js0c9$&WL5+#!dQbnm-t%4WY25FN)S$Ovyzuo$YDJGE zTf3^hmXcHvh?~ya6=dS|&n^Eh8|KF>>wWoFqc5M92j zL{CzQhNVGd-xZ?8eAbr#mv=yDAwCrfvR+GP$~E@SO7hHS7_-#J&tVy}p=cQxmTMlNfh(R(D z@%1S9CM2IZqxl$vaf873E%kI7#v7%$i*iRFU>0uBkG`dj^4syEjy_G#`Ib65<@bv^ zdi0G5=WQGSjllclrED^7%I+_kNepzni*QJ$>}m`Oiij&mriKif)QH3lU8s z6H^w(ev}6WcoPfh13HT-2Rr=#vG?zNZ6nFQI6nXWcoaQK&IV?&u?;qt&&i&{0GZ)T zLRcW#-+nf8eA-gm?v1Qg)GY|Zu#f%w>Wk#o&3BA!nm-bdT79jqdRKK-b(BY|YOS(% z&`N0sW5XQl^-MR$s|WS{G5UeAtG{^FqBM5(j^Aw$pr7$47{KonTSdWNfmWLzkltL? zO%K=lxfH!<8@`w-U0f9Vyw3)0&00)+T##9dMV}O``&v*2^Q6f_*XjHsXPruajBzb6 zQRNOh2&CbYQI=*=mR`T^L)!lh2}f^vO~X-rw^c;WjtE# z($5*)a4-vyYBoj#sicuP#V#exN9IR<6Msa??cvqa)*P52ie@?BY1e=EtWpN-|^+SxSt8QkRi{$ z2b)mEN_xaWTL!w$>4ot8=&b;XzF?GTfLFPISJs3z8}izn8+lD`;F<$^r5_sPWsq0# z8MHxOF9vxH;CF+%4C*qd%b>1jFn2ZfW&&Mfl8G5}mjPX?1-hm*cdaTq0wsq>uRsKl zsIf^UIy^5rBEG>#1|P}i1|JpTql{H*Mu4^7#DiJn%2IJ8-2oMVcuWcvfKX@vjq_y_=2sZPO z(^ko$69-Om7=RDM7?_eBHWDjAR%M{pGyB=ih_#mJXJ=rOflW_lVluF4MPZW(@-(o?z$W?7 zz@`Pmrh^n}N*kz}jhR5xRN3$|1T;tbi@bM>jZa9wGZ(eK7`(-Z|8=vu4f4!Y1L(Jj zPupVQCjmswR9FJ^+gu{*&}bR?FI2N41Yvc6(g6B^_{Iw=*-dYSkn(A7gMdG6;&v0Sj(Tjyg z=^Pgm!6Fhen+(v~FIE>~cQ%}4VA434M8g5#^BV>xi-mzo>j{-!Oj?I#HU>??ii5@| z@i#$Qhp)Gkk;LWx>zvwQU7{G9DFPbnnBP2{1+*UQO#@nuW4;MuEO`)Pj8_%gYsbJY z!mFd*yr@llv<;xWBoHdv69`l`S$g>SYKYMnoJ!3^E1GD<)k3ERCzT&3WxVQV1WG0` zmH|rZ1eD_QP6L#d0hCNe2ZNCeMly$;&yI)EcB9rzw?!VTMl%00!Xb0cS;lkDB2W1o zbd(#YI2tTT$f7{g7TB4o85y}aO+!n7Wf2$yF+wXjEYp%Z7)Sg$QJINUoS~lvdc9QW zl`%!lhP@6=GA)C>))9N9UGNR|T3764Jp2vrGPp}VG`MToIZRtKod6z=NfA*t2Y%zE zKN3JSo(n)d{T(+Eph36DW3>G(L}B_C%5t6UO$=myE(wr16Eh0}WFvfw9AivMblCXf zuO+N`F~0cw%}nJ20_YhKW^%=sw^{-Zu{#oc1=y3+go(FE?|cI%PE@-!DIxai;jEek5fZI#~G^-j($a=;~&h|9pjxnQAh zU||_yp@Adif+Lwz#+mR)y>0->ivT3~{7h_WVd?ng>g@VD$*u}R90PNrHiHMIHY}R01Cn2o_ z@Q7ErIk^-iyNAwU_b|U?OO$BU_ngDsf)XIA=b+X?o&BOxdW%K%o!kKAG0wM!ZFiv4 z?e6JiPmzlla#E$`s5{+yr&h}=9Tzz`taWRRq9TcYjx5?LXz=|$aFB*4F4greAW ze?~63yCLE|_EI8RkH(#%odwmSv&DJG9@mxbTTWdWl<+LqUSTiNNjNe4koDU(@i-0I zU)w#qtz>Ghc0w6sA$|LWkN+0&CtWze?)}>@78jmEfe8pBHgVs6v2IQ-vnq|$X5?qv zvcB0(wTbgTFoTMdS+-oorho`^pNF-X(g`Py%ZU7m`+4$-8^G@`ZsIabC!V;IWmBBu zT`Mznx$-KWzSCvX-HJ|MR_w)7cvcc)&iUo-9yo~>L@1cXnlqnob~)@M#-3yBImVu2 z>^a7s1By;aXENy28)I@{7>kav=opL6bafev&TY$BbXI>#*TkYz+KDT_MW?h=w;C3m zvQOV?Sae#(qN8`%Sagg<$5?cLvFI3!&U#sNG9EhF+mt3T=o}h@jxp#MgN`xiJgHlR zlBLF|vx?KYCWf6-PhRrG z8G5p=J2P2&cE_YpHFP3;p3I@OvGlC)w62Mzr__^IeoIfOr*AbZJ*zN< zt6}MB8B33`^gL@nGaF#}mY&#>(X1af?F979v#V;!Ykxte8YX+v%aNvV0G5-cF8MO2g(2_wf&x~F&2BDd5 zYOR@6y9T|e7f**?(zmWs6x?DP(dgRMS?`-z`mL)ahbGr@Uco2dfy`KL=EWlBa#e#w zqAnUFQiw#dc9dD5$bqq=ya*(c&(7j=Ce1@B^a2F!KJ2)t-AB-6{WkI(*@qYeY|ZVJ z8Qn*&H3wvKAZ~6o+%+{Lkv?}Gai%*MdvZXfPqpaX{63WhV^7mfX{TW{#g*<>{}bYQ z$Y%H!vwQHX=(|Ci#`0zGroo#_w|v!Wvz*)Q8oc=;@aFSFGTrj!1jJ8VAs=61`u2;u zK$attsZ3!3f^;cXFxgP)tzf4C;R^@OdI6%;Tq$piVJ`=q468X-k>Iuscave~A7mtV z(Ccj0Tl?Ff-mGnddcD47Jd-9zI8W;Aow2CR25}B|4a9i~5GS80#s;nrV?wS!EGSfZ zQT0H_;Q(ks;noYODijB53#p2a&LuWh0U%FT{k+w4UmoSEplc) z$rbeoOtlT9u-F1WfrN+c6M&MpZ3Pg@ zu$p7#P;x;eGqheI*g4oeOxfVH_jbK$*Y}P2ZE~!$Z1dYJVCS$t8tWvpEo6xC389dl za>2l)XM{=lOjWnsDK3#j7#aIrUa8rwcvIAtVYkm^p2}FJyl_w|XRoAG7?0x$)+r}7 zV|mA1KudF`{VVZ!^Y?3RroPP1S+e=i7!HlU-g*Lan|MrOq+zbtBn*DmY+;4Cw4TDH zjlC2u&Dh}T*;8SGJoW4Lv#=`KK?4|kb;zA(Wf{aJaj|`GW%eyY-4n-*ev!iR;ZwKgKIgO=BcB+D{(mNJyEPJOTvc7*|b3RF%Po z?F*LNcK6$I6jL6xKQ;8to7!39c=CrGONWw;Jnq;0N>1_MXLPW5>aj=ND!IOsi!F@$ zGne`kpl>U2E>V(xX2{=oPP`CIoIOpL2^8;+$=z%~@p^#ba-q(V@k-|x)3{DFO~6u7 zFtwO6z~`ERb5A%=7_S%BamF0XPeZzm*VRduQyfx3m zE$3}J!B@vsmXTd!VRM2FKS_L)ACT|&?PxvP0rK7ZHbpk^9NMN6IW43Eq^M1Nv;}rR z#zr`*ErHx;Zf$0OZq0km=EH;N!9>WtfSPiggGLf^nfyqKH$dqk(JndpfdEmTcoYHt z0oXjg?QIzaW={8#=T{4i`eJOW<%alK+v-dhzg`~$@{MtIJ)n9CjjKgrY10{3O(ip@fww&%*;%5t8O)Fa_r!{t`~+DsXZXx3)&`z9@GO6N%|1Eau=oO5uh=GSPF>u>BXMWVP2?XRP!j`6Em zHKY413TA7})uYC-#%QMMXHDEh(x~yJyNOJZRb!k($Q;;xPOp&UoRq;eq%5Z{xvn7} z;CJI1@&eGZIdzGu=@21d@)B7f8R{WWa}(ON1X*EEdt2LYHWHtD#6t!fKQ}gRrJ!-< z=w){nca+^R`7h>69GK!rvm)a(bIxUV6g%i23e2YXqruE;>fw@vRndi}ZRB-HV50%@ zSer7)!pZMq)jpjbU-fepIl*5kgE>1Q<;Pr~`+|vK?@NEL#94IxERf3ww9W&$YLkMu zjOJVt^p>HK8S$IGj*^$fPfSv|7X#F#m`Z80!l78weZfRWy6Xa%yrmzchF))8Oado6t%Kd9D|%P$M(b6p zzULh7rf_)j^ZX*pk4k#MxAu2yjTA&rR?;CX}RC&BYEH&_s^Uub=jVE05Z zA!Z!8vt{f)^m^0yd>ifQnS+XVXYu(yXpYvMO5xeE44-eKbj{}So!}_rl$Wt8RmqNI z#?_O{D_2H?OZWv%cnq+br#qz^;lj^!N;ksEPIAVKFzdZ0x7wsy<2E26v&jIx{bF?? zc9)woax8Nwr}Bgm1(zrg_of=v$sBLJEak}r)0SVaT5>S$Oo~;#v4EHX6HWV)RjXBt zrkyJ3dbuN9r-^QTPQZ40aSePG)hRAfG{)|iaZS;4^l_ZO>hrn!cMqMz?qQCr|Eq&q z3w6>?LHe5_)Md=s%Qa_bVb|VliNnpUj0QZF&yqYnK3j&-w>^O0+X1@86cevpj^IyX z0xs`(pT;0u-tn%0qrWuYUa8qLxY}4k*U}Pd6OT#kGrS=z31AcZW7*iV5K-1sh_bPl zLX;T?iM^~HPhiIV-BC~~+Cc*te09j3&ImF=`c`J^GW1>Ln4b3Lq7s{Z9c1GHbkpo+ z70tni3^f+#TlY9~G_Kdg0=$v0?d+fgdyy}fnHR0RJCp~_4q_!GC{iWz-36PqDsZBf&*$jK? z%J59ev;aYe%X5|AxV{YNEGjYNLqJBWEr6-FxYwvzEGHQ21dy1{B?4Mv#> ztTpSS!P>J$`Ii~YsSSsmwL)~yYBAH|d1E+58DwaRn*xPpyyMmkKnHBlV2*GBpiXvH?2%4@RHf?k5#v~;6EEdC7eFhM^Kjzwv zCpgW0HiM#x+L_6o7_o3(peXJl%)PtPW$i$SkGulIb+z!HNER4&&_coDP6BL5s zf~CsQ2=sw>B_Wh4p-~=9l`bAUUznStY-1o@54>M&AXPU(0M0qW02Lt2xR`!`-K-t7 zk|}*gSHijSn07=Fy^ZWu!#O=D!n?-knufbeHoDG)cUuQb#Jhd$ptFI`?x(}TsfWg| z@i}mEfP4fqGF?7B71vCuD{lJpVC#tA%!{of#?R!$l;ds*QpL z6BxUH3p*(A5JMDjHrP|w$7S~J>|xu+HoZ`=zCRAEXK80Y(~k*4japM>>rAoQX_HFn zU_Dh8fA!xeAd9;^AuZZWlB!ZVTRZ{yP3Pm}5Yu+~-fKf2w_V7PXWy$hr0L=B&a`#U z3UoQk#sY`sVbb}$Zt zWh`O)dov-hsgA945!g(p)>8toiCjC)dG_i7u&0SSG8id(SQ3o%%7(5>!6pp{0J^S# zNz~w^mvMWIK}za{K}y;7o*pS>ja4&3rPka~X(`64sj<>xY*hv-MN_*jP)W$_83R&= z*eF0O47|3TFpPjDKqRf<6+%h|C>fyiy!|vlX+42bYaCF@n3N70v*4ut#xyu-fsqhT za;Rs}&`31YSYtF4dylr!E%F#`2gpYF7C9p@5&9NF_w5&pcaQ&l3tjAJf9IufPF;7r z4WNhtSpr&;4GFZg8rhvc^Xc3MpF9uQh*B8bgWnD)I1}pwuGIP%xMbjxflCH1JuO_y z+LdO+OM9d7k^w}(0HU=6M50dy5e=Wuh=|U;TZ4&)qcBMU%h6ARiq;bqP4Aa2Ri7<7*4ZyE?gB_A_4_KRO@48X==K za(zeE=_GEwupHzftpIB5ImzVtq3`H5pj!9P z**CfE%Ym`7W~bReR(;I%?*?QUkhSt4%fPF}z$*i+46s^qKMk-lz-p<$Ds8-K)OKgV zRW*aF46a&tTy@FelR;B6qNz*b7z|}Fl)+F2L#-Kx${43+1W}Czf;lIrAJb4+h}O1aPA1qXyy_h+`m*d}bid@+~+s z-ErHf8-!yJ&YB~f3phZc>jvV?c0Lu&pMf|A;;a+I*-+K;Eys70UV}yLJ{rK!D4>|| zx*JXTnBB`=;lSXlm>!G4-f&M@3=Mo2GVBe(hUH?)G|G6$6P6D;!stuEr|%W)*>32d zV>eI6MJr=n=0c@G=rWx1nIe5FfuQschvI9g($c$MrPvmXTDW<8U4+0qU(vL zJ@h?Ylzzqjr@+0w_w1aWNBls8j_D1Vpbt0hj+778pR8QSlGqFsxyWXMm!;Yl>-=5p zlVeTXgP2DMWZG^+*R^sXWC7$I2YScD-c&QFi#^OF1xqSvOCA;p)LTj5LNMxCX~vRZ ziZkZ(hOw0A6K5KFn`#6@Vr$GtY)W$JB+Fn=P!#=p)HN&lHZunPEWfZ?a5+d_!J+6z zR+K=l;(soF9wooQgCPxBT`Br4kK9#uYmuC>7*P+c#CJZnzX}wnzE(5Vj*yHl&a8S( zll;5-AN8R;vzprPb^VWFZ}=NchBvg|>yD)3-=ltWtfdjf*jDt4TYANMD~&z<4=tt8 zzN7ay>PuqE{vZO*S|PI)_fJOdgAa%W*p{;sZPr^B{)0K;;1BuqHWn)NjmM22p6f^M zpPept5#5o0RFHQ|?ssgVEDxZ+(;b(F_T2W7|4ay+H_7##cjq++a z=s_L|#2*h26@a^-Dw$?gkXdyo;;P7l9Tz#^k+Zi7;Nsy3SfW^ESgbOTo2wGlEq5q% z9NwEFwRI`s8o+V#;jk32YGRPJp~&<}z(miEz{5jvCwVd1LjbziWhgl5L+q(GD(YF> zWO5i)^|tEC5Y%d)F~4mS&qG2DY!9FZd(n{i4;Ku|Zgr$1fd3sy4E}$$M@Y{_(8siD zlfjN`3w>wE<2;5=+HCc9xB4q%{z?4iL%Kfz@KwGHP#1rnd~xJYKthJHV*%h62P}l{ zuY@w1d=pRKu!**6qAjYk62$?Kgqo^_*JJ&ZE-JpyDLG|}01P0r`{{ZFNk!ANqiV>0 zE2_-va#o--kUKEHl`C`?V8%PX!&ivzXxLHvh_C#+B#DAzGUcVKqKiSYN-F6+-HJv0H4Af`WopwZx7pJq8$@F(~d5{C34 zj1oDro0pojQPT}QC%^Gg>i```Xn^tdL9xJs=}{I8UKgIJ#hZQ{+FBYRI)X0c&p5%^ z?vIe1vfihYllk>eBo*bSe=J8T1_(O$Mid%R__9Qy+A)F*V$#m2Za@+85wJd9o&R!v zas0l0dVM2bC>}SgIm6_WC9aW8JcokEM+H6Ij?A@l1gw+ypRRAtuG*JZA3vX;o}CVx zcFv-ma2XIrY~p^DHlK)qGy5g<<*?O`QGB=H>#29+M70ms%_f5Z@kVxlYuWk&Wu{Sk zeHsRkf1a63`7?muhw>#Vn^$FS!Qx7<(6V}km^UZ7yU?;W001n29_1+f zT448RWz@bNQ=HSMq|B!W_5KkP374Jy18_jN912K~V zPe#rd$~JP8y;V?LQP;Igv?F{P4ct zxj0{)`Y-;QU3>4Ub+`7eHJ>@gnEO6^f4w5Kz(G#h5bdZ_Z$CF*H~;$5IQe`xg%4*w zaFD@W?d&ZxX=~`2x&*9L#rIW#uVRpdqU6uxBIdtR#6<7hKGBO>ywIAQuKhM)L>A%6 za!ojshXbxZoJ}#m`GIthAJ00Lh7^8ms>$LQ$2SZbvm_>*P_Lf`z$VnD{i;m3x zhJ`tE(4+KQ5W(-$TPLARp=?2d6NLNC)x!suD=u=G{7I*6ivnO42A zdYnTVXuUQk2}%7b3}@br!KIqAJ0Xj%g=wop>Bo*w(KJ*P8;dKMGjWu#h~_kiS^c!F z7;e28FJ|=)#(xnLz)hEt?4=&hzS_9(bAb|grUJ!s2`wD7>GNSL7{6e#J6L?*JXTd& z6}n_*U$2cATf-)3f9|7Co~>c%FzQu!QyXDyd&^s)g?B(Z|O z$avYL{i1oeZ?noX%krk#N?atl5p-^7#vr|YdZ!p^cmt*sSP^iquhUs9-|y2* z0;TJxza3lH1#ASfUGEOZgj=o;Q1dJ4DHVv`1=4z1Oo6W7;c?=+pyxYi33&{{SDz)R z@V37(z;gbaYTZPkA6Alvqw)Mh7PEs4n92PFo3GE~aF@XD-N zxjGW!whHuaFf7pW(L$&0{zDm8n}N+`8VQ=%j0_-6op4v#JPTN!mR{w2qbw0gE`neV zzY{B~53Ww?tU4+p%^rTgZ0b8J%zxUz^weD9TN29&%P`WTyoj_t!;7jeN%hJBF8D)$ zgq(dnT<~wqI99NMUjvZtYcsKi-`+d}nLHDRSKzp*`usZFCb2DfU%bk zPrTP7qi|Z1E+A9sR|dmL)i8ICF}|XD!GwnvZliC>BZftfOtGBJ%--Lq3^!%h~*vyIy{fJ`Ak<>xp6! z9DCFNBn`|nnhbG)5$norSbGuOe?>752w;uf@MevD5Yx9yVz0H^mVkZHSQo+6Rixw0s~h?v-1?SKZ74_!1k7hltF@{OYG{ z3r9}iw?)6eXYUI~zm<131}iw@U*)*@n?+D#70`VmH&OyHqQtFYz%ZE-19h7DNuyo71c?IM+(zfi!$q>lhvGh&!@ zWD^0RDd3^#upA*v$0YEaKv_pOLAD}LO8dAlE5{(HpkV8*k!&|HU9ZLTmG#`^^wrxVKI7&|@AIfU0jRijO4 z>b<)9zRgyU=%{SH3|-}l*0e^ZJBh=880M`Zt7NZxM#J~Qous+B50*9m{JX9{F`XuQ zdL|^QNR`Xr90lvl;dQkR&O9dziQ`zpzvpTIEZjd^n_jTHou~z3d0~heu=qkN9Z9+5 zr$Hw5@6Gn3@h&kZZkzr{6Y-Viv>%8hklq+uL*E!OV zW}GJ3IaXa|mc*u8j%en*9&>OC5K&MG_dq%>7sJ;x?;0oI{+-2)ecz0xAIwqe5|o+C zl{mDAHz56C{KXJ_DVc}!c5KzvjKPrhiuL@E;MAkjluhgSsssl=CJs+$l8_+u5gx-a z66+t2^JiEqrLHDSvP2Vc$ubE^hEq@w%j()phZ!t?z~m5MZ{5Atxl#L{`MsaPaL0Ab zvmj~8qKk(gS9AQ|Zq^W=+-3icthl)ohP{Ya85s;ZMGp- zo|75>Ft~NT+62M}*%7P`xH$^~^}!ST72EEBJyGRuTuMg(dGW_Yu_im(+mrj#n*)wL zu8)`hICN*Cue|p?sLSEN6Ng{^Q;6odVlPZ;wa<F5D&IT{NWIu5}#Z2`Hb$BnFC9UPkd7nvg{jw zs}M|nBQfcs`bBLZs#(+|2_AJV+aUNjz(^xr+5hVWyDKV5?9*-%?{MKLF1lA8#V^V+ zQril$(Jahc;J;EllHt73*t~ zjdVMsk<)6#d;iL;hnUh&5;^|R$GIjWg>ZE!Gi#4{oyQqEqW}E6v==RIdHhf>O{`Kz zSwml%%5?RPyv|oXPQWl_jU@88q;-EYmi$O~ph@~mkJf&HsV9W7(aJKTr*CH{zv;WY zuVJs;P`)OlZh>>D-y}iCJVBU<%Y5n!Nn>on4-Oj=4n0SX%=zsYU;EgDmZTzOYB{2+ ze(MTfYQg6#{Jrn0sYDFBafJq7PAhXLId20V4=T{R39SZPI&4UM>cTBHYzjq z>3rjeI&iut>3k`qN$#X|Mmgzf7uL!nw`Wb$<391$Lm!^ngmpV(8~#~M>)yU5S|$iq zyx#8byHY5Z>hEsJSkc5NmB!gQO%0yN-iVs}3ngy2alchtDD5@ycPqwGT1b^3VP?|< z_MC0<`xK^nC3q>t`)-aAEC}+QWJ>$$(XH4YJABzY0>=-}!*K7Wc~25zyJ1|;5pz(nwoV-c=9HL*4bB-3`! z?Xkv-F$C?C&IN{Cw+;ksjY7>cIlM$GERT{xil-#G(ngzDh>-~UY8@O*jT@QR;mv=$ zI1wX6yEqsb&;2k%4dGfd#r!6n;SAk0((_pBx*;ows$}-_V7LQvnoqC#dEQw1;_YN-}mMWc7)|2c8 zsZR&wn)>9rMu*tjh6Kg)W?$BG&BwRzj@iD(9d4!KPVo@8@dTxhCVvXF* zVEo`bW1}}d%XCxu(=WKSY5W$49e2Yv9>%Y0+`6)LUzMlF^WiwGA9OO;j9$aLv1M0wJ{RI!EB=(=efTi=6X*QdSe0cnz}Nm#^?U*1+43%3f`gJE;Nuy2y@-oi z-Mhx{vqPD`axRfMvd0tzol9<~0B@&VIV?#ng(08&kw7w)-T0vPjG^4qa7;nZYdSSw zq~Df=d>0RxhT(3aF@DcAtN-C6+3U~XyCc0fXC}uRl?Z+vTCAVaHDVu$bYAojLH`H* zDy10Jw6}V#T8%_#uJLg?&3EQ%#IS^b12+ce_U{+pee$Z!L{`^iPxUI6M~R`JCr0l>%z5|l!I!jJe{vv#j5mK$#ZufxErBTy$1 z**CHTGO^BlAXx`L`H6d7%whb1fRI7L;d{kCof5q}jk-Al8?X7M7{#YZ)}&Xpu(FEx zwrJNh_tsHmWqu#=xZ&TR>vXcHe+#sWlLhrWyyMUH+6XafOLk}hub2K&U)4y0nfNL1 z-7|YoJn-k%zIkzINGXL$pTx{x{)lxuCqKRuQ)QYZ-zciD2BnyGGhg@9z# zFLXpz%_HvNa2o8-r3o}8i5smJ3>3sSCQD->{tJ`rFhA0zJk2dHY?KIGgscvKvb)ov7lI70=|F{BxDQYgK^(r z@%#fELL}^=yC3*}zfi9#MH+gZS+d*xzDP!`p%CaVl+G})IJKnNnw2!FPO^x+<(#Zj z6gG9hRpRf$Tzi+By^~-J((!%X(qHZ)HcAN*ZvVp{YExl9YMz{!7z+0)9G*ALQ6VKR zznGtkq77(-b#>b4fbLR@nR9b-WXv&4q@F4Rb#+JjK5N z>@u__r4*cNz!d}(L7x?`8MK^>>do3BGRLMuQ`Sina}fu;Msm_M=^l2j{EN=f5h&dO zHN$^?zf8{5XFNWBlzfdFXtGU7(?j)hr1vgO)PI);7Kcf^IPXX)I&&i1 zd*w8>%~mn100{N8zdt^%vO5pEEFnyogAHaCi$itY#`EBN(DIt#z1%NZ+Oabm7cf|6 zjXcBA+yI2cKN=oj+|b8$)f+f@7sK_1SlHZmRg~;{3n76%M}TYFvczR91l?SDa?Zy^ zEEMlsn>|b`_|$)95JAR);~k@RG|Yd$r!0@!;$gL~)o`R`S%B@492#|o@*kSzeo@v+ z{Rt(_F2nR6zJYhSB@}pzgH4{iC<+F1|5szCf8uxc5_@_4m1qU_G$2eXHPFr2E(2=g~|)Ex8?NZJxm?mZ1dI(Wsz_Y?3Miv4MVH5`n!s7(N~N;-1K)qY*YnoipK&C zB9TNw#OUJ_gCdlfTePlYww&du?j1i!<<7t320EU8irrqO{ZYg3S=`xyZmd=FsTz>Qs8w`O1W$|CW(OpK(SktDp zCYRv7w%HsnnjfS6+>%k`2`s(RP#P}Zk~13;7UCPHECdXRr?1$A4S;L9@pffpBM4Jx)qv3f z+;D2cN!Pee=*Qr)(HmsX$L;}?$7e2|q1U$JKN&zq?d1BZI4NLm*`bs9;js+-$pjVm zWKX1}r`R_r!M0JJe{I;aZkWH!yfi$6YX zcI6i&Vdc`M@&s{VfwvsU6uq2Ldldj77zz1sw>>Edz^l*rG41hk|2aJ<8%*9W=aLj0 z`~^H_&WZ&L5R3o!;wq#@vt{$1`bi=#n@w;+Ns1O(7pM@=4~Gk$*pKcVFQ$;T0uh`E zox-5N{-KZ;{0GV`z;}0?^R6#qql~6BXu;^#xzaa!BRcjle62}K!s4xXmm=3SH%lLq zpy;+|>NROqr2kbYwl*2xu)U~9mqkn^Q*jh;1MIa?aO8KkqS3?d`7rF_l=?a?=DUXc zIS@+s^(3xP6DhftQ3wPT(W$1G&Pbz1(qdct>kX+|F-P^=n6wvYmdK6iUKZ(-4~~|} z9YNdHLk?va-uoGjwtg5wBMe4^5^%}UZhwXr<2+s#3QyF8FyH%$KK=>iRp(|U`0x`V zkJFn(oT2a21~MW=Mf7&@IgzUyHV$M={qRDA@x$-pFwV#bHnj1#enmh*uQkB5n$FYY zsj^V~yQ#b=Ik*ik`n?bn+iM;?MwX}CWL>w zg$>qHDc3**M0FVWcnUHwv5fT!u@ce}KZtMuW#50&<;+ zm4R+@@mVX%Mb9Q_@j5A9(i_PXBO3B;@XhBXpVq*T{6yG$z9G8iAKmDc(eMy0`banH zKA9VAB$u#(Z5M^iLv+lNsHYrpJE>6L=I4vro@2tPmFhk3G}>D$-5J2VV~pjaJn+mY zrabtP5cRA)`1FhBRpPOE5YiNT3&{gR0yw3`NM33N-+r;-kBtJ@tLH7asw}uhwQhZ3 zMq^<{vthtmft>_jAc!NQn6nlf$Z+YM97z!*=RBUT2g+jZd zd&RyED!BadIZHe%^Jj>8^Sb}&H}s4wqAhrz_UFBDiQpwSUZAKZngGW+E$xO|W(*hJo=kg!JYy=^u4o^L8LwPlV!sx%c$HYG z#+5P_TJY+%)aox(hdk~|5wX%PreZ$}@o)b8Pb0{;54^R0q%fUgg>lV+t zT+gv$9w+4+bct>00(OI>Pzln+yn)I6&TbQ*n6FifB`tcQHiQYu<7F`Qe$WPgPR@}v zcw2iH34RlCrjI}u#p5H-uf)ghornw~Pe-RE{YikqTvQKrD}&uJkp$E^!H4?3av}`a z+@qrT5k){Gs=?|IpCm?Y_sN*U1o|&&AZ)beScBaY(PFXJgP8fsR}HGbZf_yu&NNEv zv)besims7&M{56P_YqV|U|&Dn){Q**t=yw8ny}~@I#8|);YY_8RA2S|@7Ie3Alff| z1Qy5+;DHM6o&aF`dryS}mUx>l#{wQfbQTY+pbTLXnb8N+7$0%u&X~Uxs~gQ6O)@$s zQSMQR42giV-P{Z8-B;)xU0V)Sm8Odf3K?A^DUUF7&aQQ|4_)G?iq`Vw1SITPr#@?s zH~@!*#E3dX#YlZ~Hha}~kLj}v|5c%w-uBt(fZ>QLbh8&7iZa^-jkb<1LQEM^wlkJ!gHFE1rI|p#(K^DvD>$e~*MbxIN?>BpJ#{gHN9Ugj(+LM6*eBu%uKX$V}Cl6=v0f) z-;!orjOy!=7)GQZb4N!P-F zK@GNmOHjY?{)W5R_+k5s6k4CAiRa@uUPSlmzI9+0(m5?0yKl;kRpdiFZqCD7>qcr& zf>8_etKP?R2<_S{+ts=lE)7 z2roh6q+pBbwF>lPs=ro>ZLaIW;4R%RLFVSJjc&&XU!Tw^CI;96VY=iiXR|CT_OY+B z{;MCSW{UNM@u>-qg3)i9>hh?bYq#>RpC8 zm`9qhjOJq=n7ozQvBq<`p}B03n7EcBR}3mvJekIwOS>v%q>q*(wVmSo7p1Bk3pSfe zAu7pD)qV5!EP8QFnwi!qo-xgKS$lStbTK4;gQ>qeV1?I*ua#DSHE5le83+M(s z1B(#5?OfL*8x_B;;B#96GepsKxfktp0n%Ly>mD=D6DrxMKX+CHAU9HK{YC|pdv_f5 zX+L}j$yBJ%Sb|qbueov^+U8yG{z-ut1d+fJ)K+B)NV2(o^+9ItAGh?o%gHk7MtD|$Z;uaskIkgasy<3I{(e^P?{{cK zxz7THOZxSTn?vNUOiNnP@9k0H;V*-Uk7?x~(ko2YZXdm+WWN1mY@^dK>Ypos&n4cifH?$X+9c(Ft2V&mr-tND{+C}(dG}*hGStbL#Ns>G%{N$tQOn{s`aZ@@ z=emt*SyjZm1#GIUV|$Jj9MyhQmj93PO3!b;eJ~PiMK5|?_dn##2pjebB_H89?utEtmUQT^Jf>^ zgVxo>^!F(dbwr+x820?Xo&R@^5J9{tN#Gh2tRH*Ee92q!nuY76@V{|FRae*gKGtp5 zi&ewMQccT0u{qLjRaP%ZD*8k4p{ada5i4t;5~LOyvFNzQ)urqH0n;5REeL)tT&#dG zvFYO&$DIBDpoFW#=LUG)9Z3%rd(lymmLgLcZSsF=81%Xz>K*4m)NtS~-66HMlfhLt zCOVk?gkF$xeCdBs!kJCnZ#UKdE0ka;)IDbViim|h(mj#&03ReiNsIn7hiC@Lz0B?I zzZ{`NB!EFMU^2%vzu?>^sVTk8+)J6C4MLv7tdQvw58BBYC=mMJ6hZ!=A!`4|o1brs zZEHye@bv@w3@x>g4iQ%9+n28Ip73PiR`&^APq5m>1Jkx=Cm%j*3X9N24-Joytym-c z{o>7M!Lahr%ueb)4;c}5!R5lm?o{B2(UoyLUMly3G!y+{_mGlZ@wkNHj3 zyKX{uI(cQd9U;iXSUHlfi%-wAS2!$Id*GtE<$Urad;W4w4-I5fKJW(^k0ngB1y^{W z)P%pGhfx$Fec6E4RpW!yblKNcisq}~LEn5j74=ixQ`(z;8-=uWm;VIzfJ3DEh(Yal zW!t_o>|NNTUa(L*l+ZA-!-B7ou3UgOs*t+|LDY6xqefuVsDT6ghpXy)MO{>!N4|Nu z!-BRS!)6;K9#)Ksw~!lWDPtU(sjU5#vkkwEok)m-sGUQ3eD zbsSHSC&9x$x3^AUcg0|Gfg{N4+U9M)j&fqxFI&!A;9%fRY5PccWznaxZI4Uh)+;<+i=S7N~NzpDr-zUhk7Trn7<&1=+LBMNwZuR12aQ>2n5@ z1L`sg<+!y$4_pJ0!q~!CL;)ZysN@a}DEURq51%4cn_eU(iWo$U+6^*4TR``?1Y+5T z&YOo~VpHKG*nT>LOKBJO^~bSZEOU+cLO^`CaB2;(=SqrKEhe|uN~`mds0 z8ST`frHgYGQ^M2uPu;|Q7Vs%Xn!51nxvX4YI8Vt&i&05OE$C8`6;_bw@s;(?t-h;{ z-Kv)X3ElFgC4X|{GBXZbC$dQTeCq-vBSCl!8J(@cr=QZ=d4!{{=E7?}=Dh}LQOos~TR<5}= zFl^219^I91#Lp8Fe;V}Wo1ddRvdXsbW1aC|W{OPEJ;`tr)mkob`uRpWOq-aPu$b6` zGYT{nhJ(>f{AQ1mnOl~exsgFky*07SsQ172%?X1493b$Da!XreML7PAx>YbDHROCe zi5Jx7=U$1LRvHtwzbXn=w5%cFYb%aZom#*LrFuU*|X;PjzEjTU6YFZb zOzWA+=bPR3OIaIVsa48PR1zz^nw@+=9d6aZT8$uYP=lx27rPE>qUBHLFmg?|hLr_s z*RH$8H}_#{RNuahWPGi~y)xM*{d$9_F`!lk?IjMzYcgQ z)`(aCGl-K z_K1&Jke>~oN8ATM)@VhG(?xutdNh(b5YKg=QjfK!ft19P)~qNZ*%my7p=jKiM;4zh zsh!8lIRw31|FtX#f!^q3#G(y|PLAXw!soQH@=J#l1cPWpDB?X(6e-KtXNN&3K|QG;iR=n9>FN;D?ZZAQP>B z6P#_pE3fS4o95~&Xd&f3U|zooJihkU6q~0lcIIB&(oY0751~P1hR>%G-{gpSy{SuR z$L^Tkq(l*kUK$$ehbu)T7O%kB)1dux3FppIJq<`mAR}u7soDN*Yap$l9ZFvtGJ<*{E z_l~6gL)1)qe}4ttUu;=8MtXhQRW;K6SZ@EyaXI+gFwoD%9y8z16)9D8kueoxQR~y; z2iGJW)hcA{!bUU@-2%aYhrr1ESkzBSN@f_ZAUWbC>CJlS<^O8%-zf4>##VNuNznO! zK&GARp?O>S^29rN7oLd+$jRUHuuud)PmslidIrFE4d+ZWQc~$daYF%*EGHLb+oxTy zjD8jYd&X-8d;nO9LmxXL@ryAhYtpX3YBZ_5t`qRbJ`4(FMfR_Y4|G=4aI`egGa|c9Tb2T~~leK#t z(@-&c_kQbj(9_ujX^s(FO_HkSBY#5d)3h<#6_ z>qN^-&kIq=)|8(6B3)H~L^_SU=3b!eyu>|bW)O)wr9p9(e>fenupLnoPLkx+7b$#L z?Kc)LG5gj!(`Pi;*QXB}TcWJ?to1_C1qzE=^=N^}Z$FLNq)~H|!f$p?`?_)bRpW95 zjihK#q{8abfYX*@>^_2AU1Qr9o}$aI?1YgVlBxVz^Ky(g&in#X?a`+sq7ujze4^Bs z=k2lTt61s)lO2Ul2kB{8!;1k2+)*N|IZQ>XFoKwe;#t>>mlRMh{tF50c4TJ^Dc@1A z4|hxeO8&fQ!>uO(?XkRgulrG!? zb8^L9Nt{_{~J3@qn)BMI)zGDp_^llOgpryGAa;EW}XT6@hn*N-P=&{JH3E$HTjA|>a6-FJthkELGRK>tuO=rKCqvR z+CIgGKiK!VsZ-__1A&p~pfHmTGc-GSz|pyU>u16TXPizMeE3lG(DDk8xW$McrKs#Q zLI^!H;*M<_V?0h`;w>4Wb6rSqhkd!lLdy{#lYSL=jq)r*5&)}eIcwXsiult~dRm=m zp}dAJ!rPXJ`?)~DbzebH0-S>sQ6i>HUllCSkIDK&!U;_@YzAtYic&gUJ_YudA_AN* z8_mSUs1pPPW(;PUfY_<-VC%xqTSbA_0;o)j!|FOxtwzBeBfj}(`03XpWvDj=2Py() zqT>LUB6@Ue_acwVFTs!RhlXBEYzro3l%#Bl446(n1!I)zK)5fh`)pow3A1I)Z%u$g zMQl)r8>XINi#RZv7jS+{aJ2=q7aP?>Sp}n5_mnoc}MIDfP_PBHgN94vlocwdZ zPVSj~Rk5sHxiI*0#xp>i9OmuOuaHe6k1Yz=j_^?*;UN3HUs1@b%9OW|SUxEwnB$SC zh#1wr*XuCS`}UmYG$Gpk4ek{ly&$SRTLzhFPP}hubg*qIFO3{Kx+}sa3?C+s4Xc5aZG|UU=^nLa4sgI|G9fAiLvj^FU*cYX8Pn3f;c*nkm;mVw%)7z` zaKjQ>t%KIh7GbQ;dY`j>Fdyf5IBHN`KI)lJFwSyOd-?XX*b{gQp2eyiZr>Vz%QqIL zo*+#yYnTJFTrlR-ndD!QJfl1!3+C_4w|u(bWoD4E3VavAkR|xm2KPAoeVK}ztMX{0 z^NQG6H-M+3kCIA+R7(Oj_MtS9qTrO;)=t15w)~V9I)&687FRKRWd=52os3(x6B&XsAcs0kTxWyFY_4k<^Ar1HJ-$ouU!s4{opw z5_^L;F6^%-HpmX*d*cJig4IpLSL%BcoPsn{V!3(gjjw$cWIpZ%%Lc=;aw6m%NCgR~ zvz#DdODle@tF}0vf|*!Umao44ib_?|dhprh6ZP=uppT~SgX}V&c*#yu2cNH^q*C%y zQ7j(j5p#Kdpi5x>fHC&_)RNL!zk4H%@0=PT$QMk$tb_Kz58vc8>;k{}F+|Gg)C$Ru zCqq#MwkYdB-LJ_WtBHlJi%y}%Qj~ajOjZodl(S!z;3V=Imvb;oZngtCJcdCUP#MPf4vBN*WA4R8GVb>a^P0vzP> zj{x6sqJE>u4ChBWd<5*)3=SfIX9U$EDLpCvQ+CU^?m70A^v}k>fz1GV@|V!={xR3Y z@=$qUw)2a7NG6ef^yXB~Tb4DW|7}V4?8lWuGF&6uN1u=rbCMdmZ#?kY z*=$UN3`{>Nu+5&NIU@pUs}iWe)Lx$uZtW^aHNzfQ@tAm+OZ*O9E>3S%VDdkn<&u^< zFn*9!g?PHUbW=#6P3$@)`7$oWE#3G@>bk>EN&t}RB_dn3aArW={8KLYi!=aq{(L!A zdJQd+a;;`Iw3tVh{HJoNFqcz0{LSxn52S!185nIX;&7J>1b>YTXOwhdOMt<_`SbV|w zi`3w1!Np=f;U#bSN`S`7JiX2L%uKWoyZVW@(_N5l|3C+bvuqE&#Rd73q;po?$T;}r zq6ZieL8b1SB7#yUB7B5{lm}@@IQLFb75-)LdsEF0$sG54ZOX64F`5vc_R|F^zq6q;-+jPM_qyrH>D8v$AJBntTVQFss4Tj~{a z`@n;}ldK*pEQF;E*x~AI9t~e!qEJdX+-yVi$fA^a^aHTEO2eE9k}(B&U^G^ASuPQo zi###K^|%+9N!b$3!QFK-R)7sZtHMGq9Cg)HQ25df+Wt9&gjkvfb=Q$kRLcz~b}~sw33gK zW)e&iY_w!G^&suN$a5O9A|~nyiLf6rTwKc}y1x2)AJ0_Wkw2V}zdC=Jl3np1y~Wun z6#ho*^t@DJk%a~3KU(4EfP?d%q(*u?`#Rfwf={$waD2g6e)pfC9rBWEQRz~794|uN z4>83VgUqcA2D@E@@#Hvr&VpwC?>bQb-L$#tR&;GN-2zGr0#o0Es=>^l+MBUnB)#zKolYzl!KJY~vr=9}GZ86GFf0-mcAaPJ_bG+F&NVsBHKqsD4y_`c)aHXMfC`! zl;yDWy+x>8m_&fMVwANC-hieA1qZ5e##CV4zS-0c(EKD@rcPnL7_G5p?2(C=NKNs< zx|+U3GYZjSZe?UhJ6iDRrpxKucH2L0U2VRM=N~1}RB^b8+l(u%W z@(L8_gB}R-Bo6$$CrpTR2moU#Wdj$a#9`P^mT;);k;>cQ^;i$mOq)AD%k}@B%}d0$J(X$!8F2@QAl*JNvo0 znsWss@RS()E~I&yWay^0g>-SfJ29HguhWo0tE`DfF-sL`V>@V%(sdgVe!oZFe(!n5@9K zG+Sd4+hMyjc|Z(<;Qb23rCj!rInRiEv$RA@ko$1wd;|cDS1!dr$Sy)>?39vZ8?8_I zu45A*6Dw;HL?b|(Oo1dHty7#mAU{%A)I(`;h`HhBDuD~P9uxi!(z*>;`hIM5?#|K^ z3Xlc^^C|LF2$|lZk#kTUSpR5Ln9TNZ>xtGd4s_^IZS(+JHZBkgNoiFsF%67zWDcot z7asPwiE#*02dZ|Jwu)fzklYHp-f?0}AX&P#HjwTuav6!%i`}_Yl&g?ew1PY*@{~E1 zryI8JMN^{KCh6f>bxDRJ4df=^QB6kVf2XxyM+bJ z4^pYWga)NI`fXq80+3UYyx|TnFJn|aTYIC+>2u8-=Iw6dZh%SZN&DZ74s8ftP)-tq zqOTD<0cnmD&nVsf`EnBUTd0?{gh*{*`q+UAuJ_0x7StupYOgOV_X4|X!V_t1cbgyF z_qhCh#~waI-&BD}HgYsuVcxH!c^sEwim%^h-kfTdIWNV&KKLNm0Nbh?QJF1oPH#UK zc}}e`fweb9G~!{763HiiBH#c#vOvDPwxALARA3NWyrxVUs!SgXYI5$l3;SnvgcJt@ z<6PsMrkcrW(LBSZ8d3jF+cA^x6dNNG$Kwuh`a%Hezbi|Dtl=8nc4OL3Rf|{;1fply zd1WF~eH-yc1x+q5myni*(!CvKe&so_wM#f(G?JAd(P}lbfH-^b{VO!RMJr59N@1zj zNX#ZqSZ^AFsw^(r5hbuo#arBOJ3q`6`nM~U&ZVTYOYS0l&R{rN*$Sm7ypSMPgS?HG z%+(gsvS>D1W9g$Hap;6a0%fn2OA1d8oB(wdxSXu{>>}c48jG+%C<^m3yFyiRRwekcL;VrcsD^+ae)YlNmRee zeo@-b-8eVeAtji`kgaG@tT4eh6K1U^9}wcX21RENU#njT%E(`a*wEbM_jpMnl*FASsoUJS^~cZN|SUMy23zD40+nFq~J;a6Pf2)WRDzt_!v6{-oe}qv|C+bxQ#HhH0jCl$=ka zd-$B$OZ;r?#6x-n&FgwG40)FXabXT)N#wJeOLVYJQkd)H2_TYC$5~o3uSM8D5C8i0 zzKc*SeID(M~ zzg1X)9NuFN20@*we?LEAHuz_418AsY3@HrEq_oLXyPAv^Sx$d2KP-UPRdRQ7Sqyc3 zY|%8%0&n#QzPPd;&5ZF?>j)NP>aq6(0iNyvC@c5mQEmzh@lr$3=@Xa1ngi#N+>hfdgnO|<+FrIFqUbg$a6AQxa;_XRpu zDKk~4>d2HfT>Z#yOUyT5{-mwnuBQ7VZRLLd;YSlBos5Z~V(u!2EN}F0)SDr&ie@{Y z(pg55SmGQ}`>7czuW62UXrLCKQIOY;0V<|15k(N#BY zzkLKx8!@9(q!d(NWOS%%XmSZANDK?QosY;?{EoPRp3F0QpM~KPgB3MSHTxFzT_o!; z{u7hY5(GzbLwyXlJ$!8TYy+Dd5NfkBw}rUoa98M4W!*oBGnSgBb(R(Jxdx@;iHCZ5|9A zEuUYBDkwFcznC{JM7{l{QS+!iF^H&w78&0gz{?wq**FVNeM_~QfPoj{HhhG(e?9V?P!Z%SJj$X-eMc=kQrkgHa$OSODL7|;k zaNo(tLEvPT077BI{bNaamx#AWC794O__jb+Q}WG+iDHX7R5K zV>@~CfD3dI9Y2y`L}$wCM0!ryCa2}eqR9kIiK1jwCdzJi_^lng$s&;a!IU_z6b+ZX zB$nQjBP07)EI>JlKt51VeEFdgJk0wn zx3ubZasoej_*;m;`Dv!@4S zZYa_PLU~ozgy*F`P$jy#j1zv<&Jn1oEqsc)*h3&`9u*^|Dh_?%6AyVzh<1JLPNFw& zD3FGZZy|JX7b8b}U)@OAT=`l_n0q9{V0C6Jv@9{tvWR#fj{5nxS|R9pgh@Gk1Rfsw z@~G47f?74vA!2ogkuY*o_Zjo)(auf}vwqmA+GL>2(~*_hcJYpM5!$Km9=3$*AO|-C zf%2nlF zwIN)IL|3Yk;5p62lN8DIAv+X4xM(P2bZqQHhoXoL++g-weVNtb_Ez87!A1g1C+;s6il%2?(qbL6M0oX0|g?z07uE zgX|RbN1sg6#thZ_0qVNA*Du;8iTeEjRb=TYTHvU7^vuhPLn@+Ry5b=PWNQxbB-Ms^ ziq0ywJ3zF5>|+TkM$gRfg9?nkn4&=mn1+?uC}2G_)ID%lZ=xDEQDv?$n~~}Ou|9F$ z+5z$Y59uVwplX|b1eG50I4z+HuYyiHAeja@XDo6}Ms&Wfe>utW&e+imQboZa@}}{DsH;{~ehvu)q&$#S1xih!)eZ3= zp@AHWB182;3eSWvlm)Md>k0!|qHYljnska2?w?W&44^MP0URiVRX|+#W5^_rz9hW& za_8muMW``Vsp{}Wimq$p%Gv+N-n)0VjU;!&|MOGyOJ!m!A*qWk*~8~e&O=@7;jtt$ zqCB(v&Y2@OfhLI+h{mEpNu1r}v%jaSs&4>DQMM=9&5q}JvLeu^uD(~-ac19s8lTgvu^Icp6aeT~wGr;MIK9$M%Vj*s%4Fa@Vpifh19RKh zdiW0Uh{VU|;Ci1(n#NsL%3P*$w)!AeedYoodx_lBH!(f5q`CwI_%WHS`&OR`x$7Xk zr|NqM>vwNem*;t9T7aLPHGS6`sSi=VijX0x`YcVd<)crnl&MvAfXgRL3(vv1l;o_5 z!NNSrN~JGfV%oO?Raj3fBc(5~k;qH6=SX$MeFD0QJ)qL%S=@Ei@3lNu`mL@zCD}Pd zbQ&##?&WaQe4MYzG2wZ*EzLGT3;;*AZOhw^6xRN;B z;^jf_n8_?%?V9WG4FG*5ve1(a^2O?YS}M^nD5l)0E_Q`F14HNRYE0S?6!fdBT}$HE zTlX{y;i~JRruO1Yb#oba>D}HA{OICi(89!-vdh!3HE_Ync7sri$1!TcyiL#BImKgH z@KakH5{=i$@yf2li`jC4cMkY|P|UXcQe^i@At9eFyQjvs5f(r16|>PL)~LY3^m`}@%Q`<3YxCtZ*U$F996%s;HC<%pC(tY;H zeI}hm;;k^f;qs6Oo=_#s+4iRldNvbr`{^FDJh$NRZE7dl(JVwd3fJ4%J@EKD81u*4(GH8s*DAd%eHEzc7neEI@X8{XVf!b(+86 zgM)hcql3fCgxcz+)IXbsh8oxE`03x*0jDjsX107%Xelk01lT4qf(@BnW&@>5^x3}o z;JQ+fiB#N!Ng*$i`y>@Jq2Mk`KdM%o@%e=oMZRoWDghsV%t=@P`ARRwK6|0|Ul?RO z0`LU(R36=yggihfepbtYu$_ic49O!JpTHak5oKsqOF*x}S)5F#qR_n#@wQw+&=N%% z6GTTjH+m^7bI+3$>>3o(ghr&Ex2RqKo4NrJ&pFu45_|pYRp#FtL^CXRp^SS-@!|>uF{1m&M z#D;~8lW77dm>y8A>M&78yS>L%YG^bQYu z`z!=q#TQ4teXob6Zn1Ve{_A0#P`+z8bPrBO?<}7VoHj_VD_GrI5E&N>^M-hTY?{Q7!$c3!E7)^)`|dZF(`?B(F}LhV8%Tro*X(2XOk z+Os)$_wlEz^Xq!uCOSPY zXjaH&{<(5GA-sz&a0>6;mJ4Pb(3xpMFQCi|hEz5zu21HROo^m}7si;i5@?I2x3!oZX17*ih^rJ25+I4NZH-L}C$)|5QFBFE` zV`aUV#YyoSws*||nF^i}vRD^_(Holz#qbh7)`eUvFQqE2;m@xE?8LnAfJbB|EWo1` z0X#()oYFodwFQs?qmO$`yAAoPSTf6uCNJDWNM&GU!;S%^EyPqJU`3)>n%s$WWk8&f z4*ClTl7E18zh%AO?)d)w?EL-geB$gRUB0gmBj`z07Q7U*RSSQQ>8D<*C_`UM@Z$Ld zuUm{2{%>5-3;y^ivt0Jsp|0;VP*0#2bM4;UYVkWYS;;6%s?JJ-4`X)n$B(C1LxAL% zIbWn5b|p*ezz2q1fLB4sxKt1#$A__V5dc3v5Nu(&5M6yU!D{t(fdCv{UF_xO021f( zuECh?)xGIjKJWf?ont{@TgwAt~GeeJ-nM+%Z7>)a=NEW77&^AABoM8>o7S)ZVD*cW_)&UMfDY^g*sAl&64#g}beZ;PkN zgikQTeQjpoyHov+ormN0#8R5@EPhDha!!D0AP>pBOcu$ng58{sU@QJ~HnKo2=nw?z zQ-VGN<0q(i;BX}*FhHzLvN*X<;w4WB|3%Nn^RpZF@|+iGB2>xFM9zzHS%}sSyGa&e z7aZS~GlY6=hbk~J)3c~+xo!i>i_=Q>TK3f?wx`-x^#&)|wBV{NmXYRyy##lVybyHu zfXZGs^W@}ucdeW6ASxk@TbF`vi=-CpO9Qj3FEyHo?u8a(lSvT{hqD7K$e*0bOt7oL z&Fka6tHI6jYYTRg@$&WYvyMb4-byIc_9$32Ee4!EvCW`$!tE3cq)dY zh(~ilWziLgL=Tqa_)hN_(4ILUlIYJ`&SeZ>R%}__e8d?uc)sy6a;6zo7D=Q(f=jV^vr%^4Bwvx6gLxPbAaT-A)11XZPLBtkGSz?peUO@P0y^@}sX<21=c zpM`Wy2#`6Z3qixpQ)Z69MVpWQl3?}72GCu1!lL;b*VLS5!%cO zQ7VTMkPtAReSmAup8&Mz^m4fuE*7AWpp{n@ioPBm9X`z$t`0L<=uIzhcd87|4RL1_ z3;2yktdzvFe-XkM$&D3@o6isZd0YKI#HdtBap0_`M} zK=ol3v*GFGh)oL)f;&vkuD(JGOx}dZVK!|R3UE!k{3M_1O10b%*5iiRZycJK4Ghp1 zRZvS}8|a2V7a99(34PY%OSR$F9e$->13?ip(t7N$&y#%GE2JzxW7EkH118#ZrMDi) zgp5`vW$~O6^V6-Xx#4 zs;fLNMAzE@<&+6cWx;cbG$M4&B-5)%2|UHnKFXF52{l=wM>NVofDe~Ns~)<-8ic}2 zp1Yf7;Mrek7Jme>DO)Zkq5ys@&?W>AFmOSr#y5OEI(&Wj_U&h!>+?&VU-Cx?Z2Ho= zbODlkDaK0+aezxinc*gBL!gDnNL1cLU&n^UVU}4paIDYjO2C0dCwe=LyJB;C7|EjNnXUu*RB3Nf_|Bqr@oicKICQHyQXw2vE8 zkMuE~=SeIhX zrn$a{(LqU08K2DxF>@JbjI}$HWiq8uD1}V_$kQaYZW##3nu!ie>^T~V^Y++c1--iNlU=dW; z+I`@zujXLHfO~+|Gido=69aill{~s*FSRVO%cP-_Vt&481T5R!rI=Skz%71~PgyEw zNksjI_5Hm6iysxLH%Ir`LsHK5Sn>><3`)2GHt_lLFPh2wlxHVE8W;_(?4x%qN@aA% z$jvV=q;q+l;65W{_9Z!4u{&A{59TjpSD!QjsfYmKUiPHwZ6Igj6T^>;DV{2`| zzD;PycZ1$(?XaH)*H^=<_kHlQ!i;^q5dTjL?8_(l6phPkm_cM3y77G#FKoELn_8t_ zj0${`(FQJmGI%#1KAoI*VFq_k2UmmZKZa8ld;s!Xa9CAsi*E%tQ<#ly@b~pC@N9w> z>y$6EXkLYw>$P^+r6lkQ@n}OJrvNWzb6YMJNof%ho|mKR*3OgBpL_W-P4^D> z4-Q|oG{f&?@nJa;BV0Sw3%=dYj{&OZ9n)LeB*~nIO_Gs*)?JVwL>H;_Js|IH*W^Il zlC*w?aU1hx8Ow*P=XA3KGm%W$N-kfRj1686L_4``K=YYRb(1I`C!V-L4Om0wL`?|O zBN)%h*hftyK#&?tKhQ%9Vky~7vSo&r1S3pZkB90>==Lw0U;|P#<$CB5xZGt7P(3!n zJdh!V1?z%@WB%q3svBjA`8iE_mWkB-oMDcs?!6%2Sb(!AN^}@BTwKcVI2`~R#uy4J zpCohI9WU)GB{!fW)jC3RrDcyfLI_|P6QghP`WY7z;|#2+^D>&F)EY2AY5x)xu+&2h z3pk9>iL=Z=wc@1?TvEC*UC)H8M0s&5Yt!%b5c&aZsc4RbbfYEXN-Y<{w#`ZuavF4W zj|~)Hm!Qcc;!1R?-vC`L>EiI=`5%zJ;RPT$*fKqD#PFp*W|#cVjtVAWuVqkYpTk5V zpS{fS(%n@t*-Rm{)J`zp0Q;Xocrke&80JQ( zO@6Z!Pfg7I4$Tw2U1<3hly{GM4@mAsWjCtaD&)+iO4U^m*bdICLX?=M)ZES_zTXuIo=<%VW+2QRj(JO*yieLqLZyK$PP#kSZ)M<(uH_IqE;eMk zHTp{}hyr<;O)1_en3!AELTAca@{~EQ&j7YtQ=cT6>tc%d@hSVsl~L5?F`g6d$O5Em zH^E)+PnRhjTkL`3jz%fmR&rUaIf>-51YM_I=02c|7cNG_Yy*(Yu7m&~kR_OwflHA3 zLQg0Qy!FD%vxr%OX2u?Cr;9s2!oOK@UfC|NBpssfxXOQV-$ z`LeXV4|{hE5Fc^Jg9w9-8&$O!<^XBZVmtx$PVVenJVPTUnAY+_5ZV~BWn=8%n{`=M zO@gTe!BZYcJM9m(V$0kcw@aS)bsgP+-kJ$}Csuy`XUDpkTBWm&D<<8gu{p)JSu#Ka zB6uAmPb?Wq9V5N2b=-LB_P}PKDeGP9(hD0b7x~_{&97Ac#NOcSA2h1XOzFv{t2s-G z%W61d^CmJTql6+zBomcnG=XsZ`WF?5A2(r0HrU#PKe0;q6Z68K4)$&Ilf|xbeKMC1 zYzm}6Z!qJ0hZt3z+@cpT$cB%n> zHS0NvRf_e+d^*KAeD@z`S125Sdx=-|C4X$_R!_>W z`l%WNSrmHl?XWYE7a}rVc~gEb;krD@r#)+i(w}r0@ksXZ+4@OF{vS4hzmEtBfT_t8Z0@AaEuFr) zHqDO~dYTupzUki}U2iCEc#;ATEt^YuM@_}dl%PYQPz*h)DCzJtt$B%*TNA09lVebB z^qZ8tdQUXo@Md;^_QYMLFo6`LV_T9a)2;}RO$uag*u%4X%5rm$tjHb&nL%nQMM8~B z=9(zhrf}p1&-+1qbH@WhqDSo>AZ#i#XMi5gn+{WgJg-EWGaDle_p)V@#wKc7Ehm&6 zveCH}#Q^WRiMb?e2$Aln9Gh@bcU2;dA$HhD>I86{<+jUQv-K%X>E38hp?-^nz0_hB z?ou+vR{(A0F^jvBk#S64vzOvA61mM!jEZ}iiCvq4OzLgZjuCJH!UV)epoF{QOMVLnZfSBMGtBrv^KAqddD$N1BN3Q)6zq0Tqub_#EMf(t#j_P1WQsc4DFbV_|6zS<;{Zl$tCy(ZU0Hi1urIs|1AWf(_zt-Ld(I!ELqI!c&x_m^1kN%(S;+hM4mNHBh*mu+vHX3MLmJAXsY& zXe&Er7aNlH{~f%()^EKL09aHP%Y_Co^gIvKvFm!RTc`m#($!#Lmki!IkJbm;YLRTp znFTNIV4w-~A5D}%7W2$g!AoA;1$bRV&{HFvllQ?m)oa7JMkX!=Yk&VoDD_nSVCw%0i;x`&EhK?aCf3=)+TA`~qT9_Q8Zi-B$7N@}2iSZ;-!D$PI5_4kQT#WHW zlCdH2L&97n7k z)qu|*z$P0S8;ommAs<)z|CTbMP#-O$4b7kuKdR?lX>Rq2dCNxOr`6CGy#({pq1&SP zXp{kV9RQUP5vk5GV&me(2R$-j;BF?snGeW+jN*rsF@Ng9CKUlWp!hS0XYrmsu`DQ3 zPRL(mXEpsm>iY{l$8B%`Ng1F;sPQ=>M*8gM7gVq}5hd>hfnhH?%)YI?fS9@7Ov)K3 zq!%xKsV%3_>ZOE0!> zb#Qr$b?r%I0MgAAsSd@a!>8(;Pv5Fl7MY2Q76pDM7$g(Xi_&i)%<0L>)()A$R~f~p zRomS|2vqA&PvT_U31}_|VST}qjBxagfep=p463MXBzjGBCzUn9z`CwCSBvxHu8^Fm zB%6Uvvgy<69^_%YGE>MKNXn$ZEC^`O6SQI>^iZ%LZf-_)$2K@wjTM?tu80l- z)oxv*SKoNM3d8Ey)AglZX8`)=TKYDRqw+5s2ff4B|GI0#yaH0st9>}y+_XyBHN+De zI98N?&>CEFJ&>IW@#lcWHb9tK=+aX zTLR!izvkACK9-g#OaTDogPTQ6I6f4CSG8oai#MzgX!-?%A?Dacm0d21bk$>HAy_P< zr48q!Y*{5wQrMV?Cuiz2-P8A+C)Y+XxSUyg=-YVfMfs>)s^0`!p?0WhWPY7c8aYO38*iUT#1N5%Ai8bmb`$Q z03K@au6jRl)l%-L!oB*8&t^xW zpOQkn*I%-cj1NaLw&(yP{NT?^0+tDz_~ge;-EG(yo?Y8{x&@&qi3?QaIavmA^Dx?l zrBdE4b4(kx&^JElWFMyhKQywOJ&|x;FSAU50@GwJkhNe2^M>Lou>xp^eqy4f!7`9y z2q2kUu7M$a$P6tzUXQ@^>DRYSJf&#xKHF#Au5~9DEb*6q`kKz7nqDp=?12$AztZ*#c+^+2Jx z5BC!q8LTI-f6{0B%De41`gC~vCakm`Er+#k9bg9kwrlV!H+bZ`<*j7BaGu?8yLvY^ z{rUUCw`!damoLg_FFaSbZKc=q+m7&;Y+2u`(f-C~uKxO6_lt(u?tUBF{=7Z)TeY;Z zGn+u&9|TX!`RQDM4_nQcL2hpMm;zT9Wf7IN(V+%z#E=ZFOm*v-4IWk{FPC7a9K&BE z+V;sey+bv2Jo|&{fP=d}^7pim06s+Mh;ztHyqO7))%{9IReQF1d(F5m2;2xLY=fBR`ZP4?_a<`b{Pa*?K3j~;>gT72 zR1_@FKPFsX_5&=7Yk`nZ!;#CF=n(|pa{i#G*!iOt35Fw8;mx%3YDoHheMVClG!i%` zTY`RhX5UrGApdTpamnf~i4F&&p)vBp#4&OcgfdHn=QSsJCLYq2^|w5~8Y28sasvU% zE}56{vV^g1_Cj4|yeuJ2JLP?6<+ zvZ7;%;zoEj0|6cixe%t3&#Yyq(h@S@1`@V=sthnQ8Pg1{_zG)L!^EE3@N%rjW5l(E*@Ly1Q-7hUjz zaqCbl7I`XHB8D5qN-oJKi2_p$MwMxZ9&wncc6!_Cj2T>w{di3%1^KWj_7Vb5Y}inM z4~mry&lL8yOIZ~3KdiEscmH<3`+NWY7X7c6|Hc6r{4|CgerP&kn-QsggnhpK_?TX= zaMv=bOnsJBPciq(?i(ZnG~+iKbg&x}pJ3qOz^~A}b>RyhDuu7T>@|=wLdEQ^1~>U0 z%+8@x)-1`yGcZ~=37x&xJQP@c`SRv;!~(ZLN+^aT2BTrm>Yrd|p9@!<_HQg}YC*%* z_ix{P-ndYm~@#gt< zlOu#+MIHCWNelnFIH?Jnw!~T;u8AC_qUnT?-m`8YWyvz-I^V1dq{N1*Or#Z-$#;51!ruJeVmy#4sgM z%LPiW{A8dmyQf!x@_V+8vdN_Qw25AyOd*OUG?{7f2h?RQu0E2BJQZ4={F9tewkqsB zEvci3Se7Y$KG0vkOR_}G_53?{yKfU-HK!+|jI|~$ZjNBYVWzStO!FkVS11wQIn$%A z@1mzRovv@Jhwk~4oLH+pO)kIQ#x$RR{0Gq4xL0HiNj{%vx!pR=C-a%XL#ci>Fx_Bt z7cy4QUij2!CWB${t)Iit5D3!7A_?8mCO<4>PQ&r3Zd_};83nQAZmS_BGF5uAoK8i-RPw9UiA8e&wlFP#yjKuL8;Jz)DG`thkUx|~aBv|= z*7shN2*3H~rC18A!b?E!`Gp#it{`s0P{n`fhvVc|tJcT4YoXyZUs=hfkYm!@b^*FN znv0%s27^|--v8ccJ4471z0aDQdj}o%s>9xN*uhbUy*lWy<5wMaaQLRf4v&sH?C9X2 z!w&WjUv;Sb;r{D49U$FdZx6oLZ;xNSebr&F4&NNV=>X>7k!un5`6){ZK8-Qe$ZC(M?d{NRLccMd#wf4!mRN|n$RNZ$v<_=;w=;Io|`5a$Hsx>wmW5zpwgm8DNkRG}hwm!eqx9}HO-39UPH2k#ue#MoJZeD= z@NL6smt^c?F0xC*yet;H^i8|o+a|M*ADym6LwAx<6%3QllfSNF#;BB|;QRgY(bLXr z?wmDj8_!fSEy9m#R#ETOCGI>fos;u`83!dQ(d?r_YN1UmmF^`#_U>64)%z~EESusG zZA)!1v9|8h;8gY0EslUi5^sDqN#$g3!INxH;4*0{)s2dHkM>-BuZd(Kq|)`=Qn%1c z)m!mJYO8UK8 zKpCo{g6F~3XN}IN$UN9r!PnOUnhJPjupS%`!X{=4`F97 zVYj)AZ?)WZ>*bk85xkg*k{X*~z=Qc$h&<(ZMeMVG`pZGkrDh)}zN}H$Yu8@W{LScn z!KXaqEaqxHk-Ug0K*3SiVKXYDci99KOnz~Tw+b6CjJ=kP;b?DiHT0Gi5#H0gayOdu zqEvfjAp|ZXdO(GCR1%Ab7XVr3s>l0_{n_8NU{dURB6w@RqSw|v&U6rc@gpUxKWML+ z{?;gxC66qYnt7YSmF&8>(-KZdX0N9~gadv;rq~LC5}i!CNDZvJ5HXM798p?)0P|py z;i&y8f`ZEz~3)*!;%LVnN7ZAQ~XMPBPF{wx1}hneG`b z!%rOVwT->r(@TYEd}N1--mKSQIalgI7UWGe;VOv`44srM|6K#1L@~^!@^5?qh|9_V zH1UoRQQlTh@vpJ9gp zQ#eKRqZh3#=Rq=nGFgZYfhJ_aIEB&*?|BSYM!adPH;s1Fc)JmA=pgeCd!yk6$oU12 z1bT{YDgoC0H3d%fy|PQbO|Fbr`ith>UHf1?Kq@Z3Wt!!Hcup^;>e>AR58hZ43& zMPX(oM5AXaeZ8#jrbbrib5dfKB;xpV-ipupHNeuJ*#lvPazZM@XTQF^6mF7VI`S+N>dj0Yj-cnCk@lnE1pg`uxsztTN%b20X$fAnsbL=nop-N(qT($quM zqHXf+zT=c2bk{Q4!QdGOwY9}z;B1pm#NKqwR+;ZtETI!e#~w(1`FtL{OzLDEUT8jztEt&?P;05b_V+yNR)H1~g{#t~KrU`~Rq8TQKc zUD}97NTlFY>sy(HK&pT!n2LVThh8bY-cHjs1J-p`$!1!-$uuYU zl?`vu@}-~TdMz~rg>6|EMM-&W`!&|#ZP&~5YZ?A$4D@K8!$_bHUF;9ud-}REWAAyI z#7VhwQ6))c0kusJ{78koJIRX8`69hp`RoBuml`LRw{DVO$n~MgAu_v6lz(`gDYc=x5b2C|E+@ z1-fc3OZd+72n#yiEI!*fURshtmh)hsZ4uvyxJcqGnaypi5CYQ09I)*KBukviWl_%A zLT2Thz#3Os$sYlp(GuOper}UN?|H81d%739f07fmw_}_=x>y4dH&6Slz0da90sEJ| z*X&;?E=t$^GfB_C*eAMoD3$Y2sii2c#ncyyXP7SK|0J6ImqnMld?Rw63%ywuIZV@7@E! zeg)>9z7x3bV6nc(fM-Ow(^7b7QaFDs3(n52#;ECab}YXlgjOqGDA=Rz6gE0y@L4#^ zFnrm&qT`M4}cGFJ6Ukaic^;sNr4a}&i9IzOknr&G<>>4#4z zCl}|p*XI|5Kl%<&p3BqlV)*H@;_-xAfX7Q0eW2*2afqtWC#NWTs{tVsORA2KJgU zK@^6jI=KHd+3*;;>iii6J zTkyy6op{i_?RM&*z2-*<_O$maf>E1K^m6=^DPAUO3b3<%b|$Z6X=Dq3cF0&5_^P_c z8I}7g_cEjgK?#|R#oi9Mu+fM>1^PjCh{9T=>N9{QsB->g4s&1KrgbiJjZSl{=KmI~ zIWOgfg$X8^5%&G_?7m9rdA?~>&(ILl$E}j+D5m{Iw8+(lW2@& ze3qzU30l9CWjq61UW^VfUsVePcUrn-Hqf!9ww^LhO4Lm6#g?GjtKJN2R=&$5^U|&E z<)Vm0R#KicIC>#w!(?+5Xj?We#+Cawh!miRTJXnDnV0{R&{K>*m%fC34$Jhx3GB1E z3?ulX2>r-8`C2* zFA>&63t>q%!}v-g^7zl(;kBs;*KI$Z>u$62?pEJBAKndKyiL8KH*7%cf`P@Pfo8pq z>G>%>Htn3M_jva?K011JxQ?)sBpb=AX4;tJN8gsOr7Yjs6o7#Wr&wkKbtSWF$eLb# z@kxoo@a8=Zc*f@u&(R|^5uOAR7c%$dox#P$x;B~!kD6C%1?LXG#Z?4h;a=8mjHq=Q zFqH98McFa1J1z;XESQ1m>IKu(7OB;U9*4MEEuxb0A?utYa+P*wQR7}69KL$(UmK(< zLo11ecjp;7vSLyMI8~C&osWLlJ|fkK zQWcy)Pv3^a37$eG)u-;n%=kdwiB-;%0&<|CjTR5t@QQ6Ur|5`$b1`<>&Sg0lS!vSs zk|sI?@bT0G#*&dJ%4F)e@d9c*O{<;)CeDQ=^}rmDAe@?3(EuIcmQ3Z|-+1$XW?yfq zf2e98n)To$Y3NzfFg1vTN8x26`ltvV#Xxl&~!tG9Qt3EObAIBTuiQTHC($6nk<~ z86ol+3MHrOabvYcmlROnTAQ@4f~q3P)nOR7fPIjshDUb-cTXBSGwYnZ<&7cXvo7*f z0Julq`~Sw z>Q!x&_z%jGRJu&oG|WFx&Js$*FPCM*IH0X`ZUP)l__B)<$_4oe8VAsqqBrZY_N#r> zrmZ4Qcxn?#{g+qw36JjNbn2`cK({Pb4ckyNBS{9iV71UOfg?t1+|vXOyH{z&%< zpa79Yep;dl1n`g$qbPoYl2l`wV?~K}F=Y)X*YWIpnTx_Cx!WbC4)*tnB~1&XgDymN z$sa?ueln6wE{ZPEAnx>s#Fh!annFAcM`Nzu$O?k2ycdNr{X#A*1j{g)!u0Wqaq%yu zpH0pr<^vQWfVKxfdeesXf$dkmD7YtN5+as)SelG{L;V^0#O$FzjZnhv%R9@WkCIdLoO zrE1>){uaVjHpn4chpcqN^QIQM(yW+z2F2ze7PfWx$+`fPK&`*328}yVUrdhYMKg)V z9Fs>%HchhFBUha{-emSCkr%*F6;+-a5L4-^q8>fscI+<OSG%Skm#E_-W_~ma)tP6#ERGtIK>=@K~Uq_CuYR z!<5s~4f%0=rY9tl(O7l6%j9$OEHo;ev>NL`dDL{PJP*@P0J`o5wL^xM)E(;H{kA}V z)TH$F23|FoKIJ}%1>>Ywq;TkEW-~_C)gCImr72WB20J(i6ovc~AcFOS$2P49=IYsh zy}w_1AEZQfkGllwRejE^cLlAfWzt2a0X0H36i-UAsL^Lo2V67t{WL1dh5`)Y$@zZn|kp8+lH;dw_#g(aIEc&su`kZFAAY0!WuQ#8eTl28;r+I=HqQh1_q&ZCZX*K z`Ie!LwjsvS) zHY|{APU&{-Js%R35u7TC&@ZU27oMq$Tp(ZA!FW+~>8UyjnQ9r)4+C#Z#{d0A@Yp5( zKYV@Zy)pbY6KSS#{Q5G%)=vw)1lH~<=JmJRR^DBIyt>(HU$G^(+*oY+-*RWM9r8`L z7EART?k$$)S8pyAO3&C`p0Tn_CI2gymYE7w!<3&8AV7Z+1?Kw7@0~7O|L_IW8fcd1 zr;*hi4&^kwAz{yL^f7#$i45B|^V@pZ@NBx}5_ZO_-;==iJ^^@nxe#G{3zw+qfd|RCiZ**x225jl-Ac zvURY(f8akQk1g-u50>Fw@gyDxy_nU=bC3uyN99rZz#A?GaXODrfc!k;a!? z-BkpVGFf;cB(ur+&4XB|l02}zr}v_WlgLQ7U`Dz~^mRL+{o*feD%kG-+&;VhI2xUw zwL9(e>+6r#?O*=$1(l6Owrc(y*}2VStSNY&KYQNiRMz8k;|DYiD3_ucmd5qJ(@>1$ z&_Z|{m@$!YR1(W64J>8YqnSkB8XE@|iH285FX^CD*MqndI>7p3GEYnJ*Fgs*YnOD* z(BCH+M${FSJt+IJjEMe0JW12CY73RJ{nHeY*@WSMc zq*d~e08)m9eL%<+lhi_qbZXPkQV23;FDL^bB&_4q@Lt0>0yfW^$6OyS>eZ(3puPLU{G#sX7MsLD(6xZ$N+=Itlfx#xOd*lU+UmHYdL0lra{ zW-4cUgle&;N4KjV?xYG(Od1o3Eutw&C&PYAIGnw?wk31 zR^fQV;Oe(o+-L2>{liz?gZ=LQ_cw>H`bXdQkG}8jzv=Jqw<+_&rNyr>Hwl{)$xA%` z@^e(gpS585`R-novJjtldo5FPU<;xJwjC9*q5l#+_;=Y2_{K^0z?0G(ub90l6A!LZ znYrX#_904sy;d#%m1=*sT%1B7FOUcCJmmTVy9FEOC81$0Q-onxcS+7NQBE<9jPq@F z*;Hf(!fGz#){af*w@oTPms7`VgGolYa9cYzO9szka4uvYM%S39mtD$2$a_(A(3y~G zacFMF0_fG!+NspTZT>0GQ^{8d>!H@gqB%M;o?huYO1aE?E!teKL4JRC9dPi6=o0QH#^#FeY_TEKO)ZP<&nu5AypTjC_6oME{{q`g4hgJ345 zm!AT=2`>F4*&8Ke#W)?=f_R_3hK%g(gCp<;GhTF_D-B>`LB?W$C@=IhL=`Dyp@YAZ zEKcr|c*#>zC`TMSj z1022%syY6qtvjx?ZW{0J_xpPX`>!jlJN}lfJ9e!*INbN^VR`~7i{L|YM3S+^GA$FB zUO?5$p{)xdtU1oAk&K5UT~Tx%wAiHqt@hjrP(e(!(=J$SdM4w8N$ z%eg2j)f{hC4Rrni48Ys{H5Gk-94es}w8kdOLX|i&uo1zU13L+9YZfBQWWr*D(a_%Q z2dR<_vEC@Kppg`p(Gb#4o@j=U=l^PE`OiyHBtk*9 zPX%$F8(AkJVFMaiC#DNAP0|#Ib!g{tDUzJpFrf%G`f8aQ=X5 zmbTqIPr90&F0{=%Nc@~;`lNX%5}5RMMcKS;WHmGO*Q4UXzKeK@i^%UU^Hy; zJki@JFzYxC(;JAB(Yr1JkSBR}!PTF;aC>Ma`6X9>24W34Uw1r@aLsfkGhKWs3LXo+ zk*L;=cLCaIC}7jd=}Iq>?CwHFJoNz#gBN;N@zPi&bVOdr=#HH9b+3Us00I_`?9uLa zwGh-c!$Gcq_wvqUMm6a;8;SOrmV5N!i(85B+5l%lQ?@WP@|kU+^2zZt5{&s7o)P3E z3g<-Y?ro5biF9h-v1YT^>0Z!t2J+rIx}Fmu;2z>vC^^9CW1J;@7i@j5n**JMjB34A z1W#Au_%t;IFjEjZL#-i-C?u5u)u4`kJe5x97 zhYA#byV^B$~6-C2?{Ui3s4waU(GnEpMAfl3i zQ|+AJCvs_x`Bh(@mj{RY3iJ1~e`T-s*&}#7WQ#_8V%OM*AD-TMMQ z0bMk}2{>%k+@99AIdp$o!`PO5)o4{1d$r%H`$Mf~tk>80rBsC0Kxk1 z(!AbSzkWqXl@|H*1zjil2m6Od$1kikMjt4x9X~WRi}3>b;^%(W-eydfG1YH@M>SR!-S_M8Ha&>#q{^RR{927 z-;_fiWC?aHP%*$p<_rDUJT-1*cK^PSMq#Dz@uUetJS@mkX#vMvLTjf^5qc`%VZCF` zHmEB}4bg*=K)JG7DO(s5g_yk(x48 zqO(-;xRuJ;7=B(zzE%oZ@L4ME!D|8>o=P_7YMx}X7QXQX5LlIC%9aP)x&Hg5&1u3e zckOzzk~#z!bNL|tb}OK9Y?lr1q#i>Sdgo?161xl=vi0sWLLs75*y|<9YdsF z>9>WERjJQF)FW*aO9j6-;T0foz_=Sln(DQ1MbIsuWZs6!*jUZ+7gTfnoNDNRCvA*5 zLFBTeVAki=?rOf)B*s9sDLXNxPd*vGhjhaxX);~hvZxSTp(zjUj!^jh_~YLD>yN&_ zA?h9gOhU%W>*`J;=9Zio>DvjB4PL-bjCc@9;I%p#N;QP~qh&05b4B zyX5K)Co#|SG!Z7@k5{Hr$KlaY&;0*=O?v?bs^>9(IXLx1D;Qt>Y`0R^W8ePA*sAaS zhZRSjRm0j=26$A1t}RcKC@IrbkV?bpieca2V51-dgqGY8Nrpg6-5@UrCbXNrMa6X z1yFI;MlGL7K#$;BNQnwj3NywXmP|dR$MgneVc!yMD zfX)uywNY^8f(B7aVjj(58?jDhHV#b|Y_e+Y7`-40(aTbHkymid>=LPI%4a>$Ph9~! z*T@ZOI=*c79{)ajf1kbo&9nD-)9fXW6_aY45?S_I3x^isU-DdRcm0zmb*|>}q3=g* z!c(3_`nI5dp1?2R>T{9YR+&jEEc5u{^~DK^nz5uz4lancW!we3JWnWs6>mT;?!Tf( z<>^coNjYDbdrwCGL{~Z{dS3x=)BA+8Kb(z#&>)A{882}^ST||`is75cd%J$DRX=l(wvgZo{in=npaj)r=Eu4U483K7Y~LA5EfW{5Qk zVU9&c5R62&&K$wLtyxZ8Tk-DNNy-47@wSkYB7O(p`$lI1L%W;z5 z!uEDs2oRj&Tb%646hbTJ=v6K9xmalV9}inBp;##W5rOH= zhT#~4QDf)X-bdrhss6H=G&#YL=+IaZT{-*%YB=h#DxU*l6Eh((Pa;MY z+h7?dg!ALe$~VTOV{_OpOO|A%^fn(bb_%1aBdA>KnFxjpTJjyn(+9pn)oLa{1m4#A zXM)uBu%5j{?^%00_^a#H3UH6Am6M*(J|`DiV#QoHu$aDsTx$8`lEhtZRZU>pu@^-0 z(?^*aw@A2i88-s35JK3OQiG`L%UqgCiM z0;6pOidG?H0nzH#=LXoxwJ4JgUoym&thLu-N*=bF@g)6jY{ruO--RRD5SCI2v2R9u ztc+tV+N>!Sb4yB1A$CCSPjUim8CrTiJw?oXV?*f3{rm~!$tKiEJjuQZ;^Y=+lTApI zgtxIR1Z1rWe&_v3?~P~DS@O5Di%2Z@FLQ%X7=t6a4l14g!R6H8p>j+ zO8xr$5xk~D!O67EhhOlb1kh80u>4FeZ6n=cqhQ}e!RiJai&RNDYOTCK@eLf4mA+Z) z$n_Pj{aa9Uv!qN@MbkmD!FVIspG9q)`mV zqYc(2e8k*~0lu&s6?f~U&U%L#fZs9w-T?gXw;hXsYDfg*`~q~?)R*5+y2hN;rd zWG33+zhp14^rff}2O3@l2A_Jiucu#PfSmD1+SZh(b_2O(R^CT?Q_} z-6%`C(}a*Px=gLIsH+d)ZjvkiSvu)1c&@ry#_is{Rb4A_;z9eq;8UJ)7IQV9NM6JZ z2fc0=meNvOvVhap`vA2nbSlFtgwBHwtqN$~sYgWj^m^<<2(j8L3nB1?qX$%IhqSWY zsVus#>X!Sn@tjX_Ga@3lwsed6+vvY(xKoFsUSVsuroR{7^r<7BlhKXKe~$1QNGE0)%MSp|ldtL0w~e z6g4~*$rj6i>sAPp>bu%DU1(D|>xUr-=n~~6v@sXll!LVf5(9vSY{nKsDcww)Gh9xQ z6$%NXd} ztyi}(0qL%Xrh{3%nQWi!`zi(OsUL1`tRh-h=Bg|NUtr})jE9+xRHSGFQ!9LbW^6lPgY=M6K|BUkL@rPTVsSStyT|)_b?JI& zJZelHTP|I8Qjk(a%;qwOT-Usajau%pYgNHra3IZM5XE$HXwzKqSQJheGq@QSZ<=oG zG-nCy3|jm+on9>$H^{;dGFKOG&hQw3vbmVkRPEX1PBS6r{ zUn5F3@aC=kE~vVmPV$QY{rq(HF|*P2#Ksdn@nPzbty27Az@G~WHBlv zz>SzF7BiPV`520fgKxV=UHIqqBQFfl9Yx=mk)SeV{P zW+U&(nfafs2=ctiA6Rqu&=0n2$G7V`YOmFV?79=6y6zY-*4Gx%b;mVMA-q10 z>rM^B)&J_~;1IEqt5I>A0-68wpryzPqjzW+D~$6bzc& zC#jeTlSYev*0uNJb994Tw}ZjP@Y!u#@TC&)k&PR|Z3}kpK6|0|9RSxgp_%}DDvxeU zrjjfY>_M`@R5Xu^~;Vf>B!WUToKMj9F!c*SMHEg^pcR7%i1h_R2dP zjjli5e7qfw_J*U|o9n^Vcr>^^zq)BTCH&R-&F$dqa(Lwt|4AV7SW;5h$pf1#OSVX6 z^AfI@{@Z{5H+IxJI4~;D)K4#I;kn0u9N%7_k3W98K0SZo19c5EB=A<3E5r+B^ZT5P z-oX(#I={aBcolS~$G#?9W&cE$bN$&n{hcPENm+Z^ojzSsU1i6U@rz~VVb_5Qsz|z0 zGk1T`J3Q>|Gg-LuF}*nI?FX%yx_Q!Zycr`kOCF zNe>qfvN9P251GZdJX;n?HXB=T+Tkpd_VM|nh#&~k*=+(C;qXw_mx#bZ14_;+A} z?!S(}6ViKdC2K048m1Ymji5^j-Sf)9RfMm66w5cRpE- zy3SX5FU}O8f783Y9r)43$GYYgV{xX8it4`_^ZbP%gI(wC(UjrU+Q?` z_s$Lr&mCN>-ayw`Y4BFrUdobF-ihEeHt7v1EY3#Pn=k(ZK5XL<)G3-(fdaFrs}Gh><2Y;w`1HqosrWo{m06h+Ak8~aFrlKRP%N2O}* z*lKQ{PX~VHTpW)gxz~T+iIwr;OH_dJ$aPOH7BbVHmy*pO1i2JJ5?(Ai zS(#kJFnMt)FNB)IdXSkho0^==8A-)9cSa@)q=fbzQIv#GQFv&Wjsw`>Dye`Y?n%z5 zAmpiOd5;aJgfB&4e6hu>bv0lbsePHn?N3$uf&muL1%uOJa&Moz~_I5KP6rC;$j$?G)}IQ~({CFbc+P zLK!gy(4?>qAo~InAiqFpdMB2soS?paMmmViV*HCv2behoP(JlRlj(EqA?udOLf}L{ zfyUM^iWKv1>Z^3l`tBKCr)9txi|}J2`(pyke*4*Ehp= z!_&ddIXtyxdc9s){`~4}ed%vUk%FuMDo77-UdU2LGBsKweTdta5FHMktv83bJC_l? z1(3knVTp$$fa&Nz?jKWtwa|a-UFBZXVE>^=`*RDaj))l#oPpL5hO2iCoCKl)6ctvt z;Ry-R5B$8w6h3Z$Zc%j<$JMUMKnVDxJLo+$j|egZ;0?(_nBCjgZw{d|;mZy_Jl=nM z01uIf=?l10)q|TJP*;Ncmdup!!UP~t60uHT#H^5MZa@YjK{MAb#Ggb|wo%U1G8KeL zp%F!q@n>I)p)8dTqOflsgkH1hgm6gOaUt{ey1wr+tcJa$zSza5`lA-HcO^-Lx~3-e z>7zzm(K;*&yS(`nqF*B*2*qLQ*S*%|%_rBLCJGz3W=bK6@}@x+#IayS-c6j$l!XE^80Axc8_+5lX0aWA9T z>gc}4*YM`_?Dpg6{CaTn@p_ZbVNfpgJ#26-3`%cwF=SgLWqyV}-0zlVwYcXG?YlKL zb?cWpBQ_Fyrcd1l8r!Hcn;d7f=%o9wV_X5`apVjGggK08J1wh3b0Jn_=Q zHcpXR&e-2LW9;*7k6XJOh7H@o=P>wSE2qOX2~*egtZH^c^hs}M-4j%w=O6e4v8ub^ z6N$6erp*Qpo=BSg4O3>X3u*oq>9W6Zvg|dfvXR}dPn6x5?{~dq@ek+)`qeAB>ip*# z5qS4)?Dx9A`L}QeA1{lUm*Q^yTo_JKG))wnh&iCz2Z%F>SMSoE86X!Weegp>?2-oF z&cGItZ!83vh0f;Iob%^pQXtOa+0_{PzC|6F0iYzf@{m!jDk4(`Wfn_VuIr98(CuK+ zGbq`&Mx0RJYap1QM;u1jOXF2%)Tjt>L`c8?ru$>J=OO9Zpk1?GPJb9&y+7|>p7#%5 zz4^Y1?Lya!n~U+@czl7apy053JuyoO4E5McxD%N`@?=Wr#vT`XPAZ!tr{$Z5s+St- zC{F|?eX>3uQF*-EkhF$1h82V>;~Pa5R7rpN-N#-~P6p%i4R@dLwzFg9&Mqialc#`6&C3w(4XYl`hx;|e^9s@F+-d;^KI=A%A4eBPCF>)U3t7gn%D{`(!VJpQWP z^P3()hezM7IfRb(zuV|Z>-G2Fu`g{R$roY|+)-_#zTk%WcX_$|JGxhHQltE?LHR-r zN?p_St+Z~I&KHLOR|);Y1_M_+;>%Q+U#Pu&ZUpIb6grhpzJW^Tn`?8vl_KYN9nL?X z4(IPgeRI&LzB%|J_07TI+vlrqzEX1&_;B0&Ix}8O^l(NpCA55B3-JEbEg(5v z6FJ|@vzelPwSn6BF3G|B4@jtzmrF%Xl*^p)(%n2LI9StEcS(MFH5xTR@xK1* zD3oBxzeONQh2Vx*lQ3Js!hAYvx8fn9aK``s;CDHq0|F*QgCtXgNC4_ooeJtBQfq28o1{avI%+|Fha^iH^E=p}poFK%uefepO8B)- zWf)?WCFNIGQ?5Rqoe!^uHyaEow#ZZdW47RbhwGT>H)uaiug#i#*Ii$(BX7``SSG$$ zb7d(!Q&UsYO^40VH#$MG2`#t~ns0gXkrUrOh{hsaN?G=0GlTC`f z4fVVA?P0_{Py5zL;=-5dLp?EgUf9QI}_kykswP6JacsUJVL_h+PMt2;-a=VhrVtYCx9` zo&XcMmaFA@Cd~WonV>JWVxpB7Vwxtic}F~Tj_T`yeMX??kn zy}gqXjlSo@6I(|d`s5oq>TEA%+wR^@%yz}UshI7^ezBbGNIX-}u73D6lD4JQ=n3<8 z;RRELV_SDg2qwEPKyM5RtIq`Gxaeev`qiz z6x^BqUhi3Mj&6U^vfq4-3q`QH*IjhByt;+7zU1cCd{F)FHSxdLYr+lp-?qQR|6I6B zZCO3jQ^KS)!XR!?M|8WTocoe=> z4Y!6bOC)i!vgVWr8=OyBXbJm7P+lBBp%O7Wy&BnUfp}9eIps;s@8Pv9_?=+N2E!{+ z;B#{;N9XZGC~{o4*keGCLRUO1ixoE(TsPBjs96dT&mMD4w@eMPqL+-YBGC6*JFMqp zVfQ{PC*ns;R|g>+ncf27%Oqo;A-keEG(NM4r>W|+c34vCHi#@nV1l=brOBk=g>D=r zzr*$X1Egm}WTZUNJ<4ZXcP}Q#bpc2K=>mJXaLqA|FxZo2?S?`*!8J<&do*35&AJf! zyaW3%SWDsI?9RfxDcw9C-}5XIy%u(xkrPJ#rN;HN1su4SE9x;!w~Q(s!nUyqD4|>> zWjQj~+R>dh7!=X`_%-Qn@FuBPjBsieYmBLRk~3~Q)#WJLS;vpPzIvEE_Iix#$cO9c z@;vd9S$l66arh7|ik}N_`o0zUk00@GCcV_Si1P|Ro zsO|!rxxtPz6g#l+wZsz)p-&hCr|8V`oMjvi4POuhMvwi7h+qHC4v&s-oyH<61Xsc? zNz*(%HzSU#f-3KVYfuF+Vuy`QUP%Ke#tQ)zXx@(w4thaeG7%H=s;>)uf#<9T9JYJ9g<}Lh zMqR9(A=W$HKBC`vYxU5msDQoB5Z)9%J@CI8kSARDVF#2x5FVOL*-9=6>?wv2s6s@N zd}1sq@8dPy?hufycb#FV*Vt7QMN^a&=ANXF9&Qkk=~2=HwG|<3{9>7wNe(J=bk4{u zG2I_G+dT*IOck`z8gT|1_(B^KD^W)p`5gQf`tqod zO4%9OV?&Yybn%WgP3X$nh~wJc%%r`F(89oN*DldQRDx1zH3Phv>+Ul3y4!;lbdL;j zpI3;Vdgz08yYzOk=0SGK;XplHR}j}##225&H|N)v=i?u4^{IP){o{E6>GOon*xMVz zE#6lm2WsTT3Vp5eQ+nm6HOq_{*d(*<5@^yS$#}8Sb0^87bQ#x>cpLk*gAwXc1r5nn2?MlebgD+T1RBo2v3VC>Re3^J}{5xwv!TI?&lL(sll;?h94~LC69ockA_3KKLsfbpQcSEpwLIZln zU~7u|gp4QFb4Dq&xvv|l=D}(nu4;ELD$hfkxBo62wCB+Yo9&r7Y~UVb+h0Fq;D+E* z>(oO0nyELMZN8u;&%nbBcCxwcu!^kt;m5IljrR}l2Lr_g-N7V4l=$w{XX z2pnk)FM1gZ9?h+A2|z4J?`}B}m?%HYRG=KnF;|}Rwo~75F#n+DoHIa~^n`>Nd%1J? z`uO|9T|`qcyx^U3&a;@5?l5GdYnCkX6zayCG2}ioop0%e>JHmJ0R}5f+7fJ8pa7`t zYk;zBX&UfS51Jh}#00#4f}04?gH#+wXBWu=F;Pi-iZm@}2JN~KF@!^ts)N`gWYi9& z4o~U6`Y;y&*X_GI#X^-vnGADgB2t;nYz|K(NJ}7Wu9-v`G1X_U>=m*>EmOIDg$I`X z=$$Os`w!sGjk(9S}eNFy2g@=0TZ8^5*ayTTetCn_1TO^7+87!s^lKFv7QeirMhad< z{}(mxZstS(|C}w;w8K9VO(BxO&xv8cyyRIST=aFsQf2GIC3jqV?Qy;*&|uYGD2rqpwB73K0tkQ+tJMoGK3;Wy$t zEKG6qN-(?kf8-lU@h3jBe^d{Iv|EKZ){bZCq zbd;NS_ea=2duMX*r3Mj5?3`KJfh%3Rg(+M{H;Ll=U2T|%kg0Mr*OT=z{QpFvT`0U9 zyDgwDb~oGD^&=LG{MFWXJa!SlE8y8=qp5JWfYb^>pk+CtYlF8>E)lPiPBA>_2H6^A2Yn8gB~YTkg(`_+6BRDIJ!NU%IbqA zIan~9#vJN_;4!2sLlesO$b?a5J`4KruS_*!*B9#+acTZY=CV9TV(CpHF>PR37*6&k z=FTGn(^3lJpE)E@zXx)psCzIe&!vwP=cEa7;{MN{M7V-vbpL#+yW#3$!>ATNhWoLRLry2y(8tj;N&leeHBS)wUSMTC4? z=>`|mV1*RS9L$M@qd4esurOus6^a!0+W%1wTU6W>VVMG?iqRECF ziIwmvc4z}aRy#)9LZyxOh?4hFugX@47S18$ig8tKhAkXO(;~0T7Occ_{znu?XSAi) z#hr2QjVbyZ5ydn(Dit5cn)yB!LN8|AJUELDLpsF(_!8O>xz($3Rlz1mFrPbhw)SfsFugLnC``>(c2Ti; z;DkBuk)~6Vt7lSmSBiSlG)u{o6OTGzDo4pxk2@WRbFWIR{41uV>?U5tABs2zWyf=S z=bWJfsf(U;pivEMBcmZCE2&Jg0jT{r;^G!2tCab$%ILW_#SWyd-WCCZHDeuRR_)Og z*n@yok>#)&yxFQMGpQ@UY5!Kb)V-{;E$){~T?o*SSSfR3e2%)ujpRq7wb;@Zzm0cH3 ze^B%6!C1A-rUl+F<9qRyb$$v$a*YR5$#ps_>k25B3%3|UunJ`jl{HccyLrCoYGH0F zd+2A?=(qCD9=>2t)d=g5%AhC5YVSxLsaNW&!>`p>dlH=Tk8>mw{8jwT58@kK)ECy) zrn)w>7;M1SlqYPPkk>%G%FH(XONLxlH(+a$1aCZpVJt zALG*dvk(#Q5uKj3_yo5`owmM@>>JyhIvcC`nYM|SzQr$js?+z4i%HC-Y9lvQ3%41X zfBwN$n0N+kap3|M4SOFvnQ?CA=2PpXoE2~alunGOl8Y;e6aV;nE>1CTrR>5KU*sr*3GT26$q9I#p@lONs zLFw`xNBV3t9!o3q^P)WN2^e;ws68yj8_*O9`r~xS_hUq-iIA1VZWCfou){K!E%7*s zUC?}3!Noa$RL_cOWJt%<*Wym0oD_m5cWfVu+R+OXGb}Y$03c-Ck%kZV1I_rim^fOl-)WpF&)z#7Du0#`@UM zsZgO_8c+t{!&9Vhafa?QJ9UoFqjAD#`ZM1q63Q($A{)hb`u*@zvQy?WX1XV(_9#$@w$9 z_qMp9XMLL~_-)OaRgFmvvSJERU${3B(lE0>07%_<{sC>;;6)S()8rQhBF{iW`z`|< z(g>;;b7a#i-cEGhp<8=5lvmhcfnzzGDkw~}87{wCH8qpWdYgQRSDV7!50c}D%ZpL< zYCM1~=(2RzzDJOn0FXa>$OXqt0EbX)6!`4+1_K}zDnG1`+j)of8~*+S_K}vhxqyO+ zm&+nV`23$?aN$}Hd(!Kbb(PUezOgUjct^vwsLVnoet9klyO6KdJ}CY6^h#ad+#W~? zqgNY5XuB>Zi>fTj+U9G%p>wAv_kOb|FljIs^HC8ZO!!8mt)L1dkvDVsfJTHvs0NsUE+tn zrCR{$FIN?!SMX&Fkc3p+A)`XBD~O{lbfLa^^;-R~_NO}Q2NrK%wGMPIN4n$)j5;%)G!Yd@9M%2evdznxtoMs3w*ub3SR})B!)~bIcIA2{|`Kc_ZN*IP>7iHyl zqTAb_hB7IP|5yUA8G3YXZ+~JO6|)qL)lFca;=lR!{%)!g03JAl1uqyef0YGI-P_xr zEHzFe>^%5i;-D~5(w{zrc*6Que2%(6SlMHJtKdkW%-BF4y$`5^Oi&&KT)GAFh zV1a0cwld;hvxE}LoYHw&%*X0F>?RtYJAb~hrPl)vrdJs2iPFnf@6rE+V?xoAwfRM3i;G{YBN_vLr6h~FE}SM5aqVggGFFds6qb7`F@-@OotcJkY{JiUjKWcgE?1L0q8SmDcOtNcDSsF^4aA zI&I+#by)%gKs*1j;7bWZw&#o=ce=_8%N4iSySxNaH^REZ>*VrnBlDn=A8N-u5neyO zNpWL?DDop%fux6eiTsQ@MRt0HlO82gX(~vIq-$-H&VHz8Gw{6s-cUjC z1<0N6kzMF}=8bD6sv}x5Kh(3@AI*GuUX*_8^46|Da^l!S)TfwK>lJs(mT0^zQ8X?1 zsF1Mte#t5`uXQ!`3;g2h#!QBiIxj2ai?}p^Y6m`4&`aUvxuOpWMDO4`f(PofIvOPe zqa0qc!H2OjmG21oOfZ`B_3Lkrni^%=Q8rAFnOj`(hO6M-AV>Qd-loFAivET%^Yo?1 z)p~PvagCWNtT&BP#3E1xe42BHP2q+{Zqwc?SzELGLDk#v`$0jjxG!B~=pWvlbyb<8 zbCdcZ5K#b|?8Jr@7fMIZx^g-JG#FmQap`+Q6xjjy)Y@Zl?u?;tW6~xNGRdgrrdTGl zr-}bSgc2I(2dx3@h$s$mV?&jjh3Op6x4Mud!SMo$!M#sPjNk}JNe7` zWkPpt?P0N$`VtDn!U+v-(&3g`OsIsvczQA^>%xhm^yCB|#9G3IkZ1;;rD5gaNn}&q zVv7^S`ohBr0T`qfB@$8V=rxYLs1xUmb!7#2xN_sXZI7l*wBccT)4yVwzl$QBu6Nod zZ+)fbD;S>*rFG`Nf<%kJPJJnaPDC`5oi+I^5{2I#^4^@vPyu|=GfUZ5Oa693;NG`* z9Lf1am(K9lN`k?^ZIfZ}uP1>-N7y6DF`h$QH(AE6Qg=+Av6c94GL2TnJxMkA*OOeM zXRh;2+un&DWun|5NZzvCuxen6e&P$C_=~Q6rW*eU!vflVA!29arPg9sAUBF{6j_ce zT{}do8%stV+&61%1?Du^Z6!Nx@0(N!2DA}m9&+U)>e}H8Yuv~!%&7eYQdevLy8NuC z$s?YizlQqabJk*;%}w>Nd^FEuh;_88B=zbu+C3?1Tl~d_50%@}@`?FtKc(p-;O?Ku z_G#JZ$w5gZ{-oh4G8Y2%F?2m8=m};NV=uR~qmtU|+ooHbgR*W($rQjj3EA<~g1hB> z8{?`gcEf>us|xxx)4ag{=+QQ_6m&JI@M15qXfDq z({1%-Zm>*ZJw%=tAytvs*z8L~*Mk~``*u5l7JaXO$r39HDGMMnvXe}_SB~M2Yon*X zRGBkxi%F`%_&Rar?bLAbW<)-ucC#)7lho&ISbRG3tV2!qIjeD3`>fe7PEUIGhlx&o zCi$&bu2yTzAsQ_z?0n8AJLoij?lV^`U1#z?ZnI71u=0TfMMZ+td2xtRk_ zK(8|PmRZ6-G&C-78}bI^Txqu;d?$4H5XIX=C^Po|+pP(KS)yCJAmv!C@(kmtKDc zVQyr3R8em|NM&qo0PMYMcN;m9AUdD`6qk{wZ-@#x|``_Ww{%G)b!-M0a!_mR$cz^JBgW>4l==ks0;CH}f zT>stRFCMEtx&K4{<7ujLo=cUQaf>mob2;ZxzAR=vsrr$Q&6u^j>|Unvn4QCB`6Tj3 zrSGRIkIHd3Wy_As)4rhFyF3^!JaZ8O_e3e&@1ds6*t+G32ce`T>WS;E(! zpc4+jXarUc5epM(ndLCdzuR9}V`C`{z|FLJkg;Gqj-~Cy6E-i>2>&VbHJgdGN@Mo8 zY|Tv0)0pd+eP7IkPDM_DE0brYh?a~SmM;b4Gif0sv|KTsBp(_u=pTmi(nOmd+a&)F4U3(byu|C&|_ex4L27y41gVkg=ISfq4R zc70|kSmP=L!-iAeZB>HK8!N<2k!4Efh^I>Da#3iRE*PQch=u?WVcU&IS{Wp^NOP$L zqv>^+OrxZTWx4>EJj;?b093JHbE(B6PZG01*vqF}=#(cvDt(_Q9`8UZ2xm$Mv{miLPY&T8acHVFR7e-`m$ldl@V;?+!cxVA1<`9CZ&x9wWh!NVO%$G7{ z9CuBw9LZT>1dF&4h9&Zz&Vq_X7stK$bZbxZ_mi{h4f@$;FL6i}@6Hzq+;iN};;u!e zg%N3PXf^a&L>vy}QbtQ^J6|&+av(&)XClGfYdbTp z1>?rZMJi&JE4s7NSU$+O;0Y6HoGEEn{DmWZNey>7#~$boOs)?08A7_Y;XbWygJ5Er zijfRT!>+Isq&v2pB~j55X=9fMmje^&T#VVl6HblexWJRu1MN=R5cotzbV>w&5T>#|NKLy&Min{|j?x4;S8&@| zSt11YoJe|^KB)CMTmc)5nP?jlGmuDy?TMGiuMkIJTviUeqc9qFbJrM69zSV;lrcc7 z?@#Q#r5|KuSxc75bYZ-RKNGktcETb?_D@4DmC4;pKB`gtSMc6Xis6jivaT9H&2JOp+ zt@HvhXtdo9W6%M>(%#x21~PJZCpS`suzd|x>>9j|B$o8Ar-vP9Ct_4^YLOa(vq-I0 zDz&npy>#6cs8154cb?i)rNvxmkwzkB2!6I^t0Ku|mLRjV{SBLIwc4^rHki=+kJGdG zLFil>xS~Uf0mx^l#>0*~@TB)2rvP$ez-K8}X>AEt7?6!wEao!BdDA6FRIK2|xM7bX zN$mf^$2`|^R#=%lWbnRE3=c#SL6otqOw8qz*iacl{@|!9d z9>3>_y&&~uI#nv!ogRte@r?S)EK@4kkr;MR=0>&hIgZ;*i3@xDKyq*F-Xam-f@r(~ z@tMmeQ&7}|^JTXvUThw@@VxCxjV&i`yD%SY2U?xyH$#aOvh_q7VCa>+mKERpL@K-~Cskvouvjim%OA$cV{^ z2OVE2Cti(MIP6ZcViTsNV348Uh!40}Xk@N__ikpn6JIQ2wJ&`>}wEsFsjP6 z;O;ryu^S3O!A|RdSpOioQ&Oe5&~x5&{-!E^Ei=kg{2~tuPv2CKc`(o_Scb?#231>DY{0 zUG{$Zq16SfTiwtI)(y~abv^#AZVmY^yVI(eCBiI~%5CHOVkSI|-u#b$vUED?4Z4Fj zt#Dto=uK&na8q1Jut-K~Rh?Pup-%Jv-S*V~)BYoJ!sTOrCkKFM?f?772Zw{>y8Zw7 zVE=FS|Nj;L?XeFs5o{q+E5oBq0ScHT>EHWP-@oSfVlET$-`T}y_UZE-yS$j(^;&!E zZYd43U+e|tjob|oXrui@u+w_^Doc8;R%?$H*+O$GEv+)1OR{J#MUug*P8(_fHc!Pw zY6>k#L%DR8ylb9WPTtyc_b=RmsmfWj#|PpNK(Y@MnHCm#r#|*`OE`#c z7LFEwDx{_hBNdNsp7bDZ?XeGFgypL&5tb(%ci6CZV*j7hEt4FxH^bh^n^r3pvtnU| z?mgD+vdfgu62ag{mZ-%7RYwATj>l}y6C+$>Cz1+#>CTj~v`fT;NDLra2SO85a507N zW)Tj$62{mo0zi1G*f(PW`vmNx(GuX9V%9ZllZ%!2hHm41;A%kHjjW{2%6Rorb7Ru>C;~T zg8uE`(w~`=^Kb#hnEhv~rDr^GFkt>;#9Yrv{0EJT;dtFIuueHY} zbNsqeMG7xN+S0(9?3Vzmv*`pd6PCe5Lc}IueT5crYcCkNvt?N1iT29NaSW~UJQ3x< zvo(uF!q=8n>{%cyR;0PKg0s-bu#(W*7fZ{icrG@l5Fi`QVYGb? z0DRnPk>=aa$3B(FdBU6h+2=G_S1&B5rN0(t37cY7BW#$z_1BMgcv;m-S66u~hBixZ3zsa2*@P+`|t)4BWd z#xOKH|Eb`KoJ$cy|6Z#K6!soUK;%poGGkEfgfAd?m>0PMz7RADoi*9N{YMKmLNdh@ z)&MoQI$}$ST&yysxn9$ue(8PHjS|_H#uUQzhx?~T7KXIKjeSTWLG2(;4|FwQu@WYI zlRJxzUD4Y!B&Wr-Ymv3gsX+E+ACg?FB8_|OJCUs1a`E|Gkd~Ro$gRu*sPu5dgKLo^ zlDpSScG$5yL6lRsH@@X~x)2WS&z7e>N+W<fjM-1AiP>Zp}^0O2;mSpg~x zgx{w+CAw}|*u^bd*~0*8@R8;3nt)SCK+h+=0Zb2+EV>YBwCioOaL4ceb0hD(V>9yd8$_wNS znv~BAygVzHAvU~GNn*vzDs1pa14?js*(YoExD+W}11k=eh9ZkOTvl2o$>$>XT4Li) zUNbK6jOpQ1$1ajH+1bY#c+1(Bw$ke{YyYX>YlxQ!FVA+1v79f)to=aRN&Aa^7NJ^y z%-WOrCzVgp>#mI=5w1ck7Xqv!_>VDbnSS*l;Q4b9>#aH#S4B z#p#o!LZ;%gG?!<=Ps|Hv7vPqD8!bg#*oWf;Z&UpF@<~KR>1Whsxyn?c7VGcDddx~y zktL*kwAoz%Eg9OlZpp+orXLww@jO~yfv0TwBeSw|muFdR2Z8m@naLU^Y46)`h$=$L zj9DVn;wgldtHkR?EI{0p#!uf09B8uxq1x`N+}L-Tn3wAN>Nv5AjD7D$BFjy`1l1J} z64V-ffYRk@+%1Riw*pkgt!{atvdbrFa^n;dmeTEevBu37T)eD(X+_w7$L8)cXg2Hy zw*TvX6d;?8S^JZsl@|X#1#?~S!zYEyE#3}$N8n@d&b^%ND=O1cfs=HUaAQKZ!|I~A zcIUPknuPod{NmHCGl;>Ppw1Yq(0;QsrOC3~EnQ3daca7mio5i!zXyMG@n=xj#HsOT z*`ak-h8%fbVi=EORGJ0+F<`RdDcAhmHy^W~e%=JfYYd9(MS&hR*#Ar~cI+wA2dPyG zR>=phrIkb-l*{ztIUx3APIj6zG}f^`(pI>VmX*r8DDU;$61W;lqdPN={~5W zSb5s~fPXzlAM9`)66c5Dr=Y7_^SNBOkb<=;K(Pw$AnP!Ctr}yIam`l(p3dN<)Y>Dz zYT@6}?g5AJ{e@U=wYgk?kg&(tYEA9Be9YRz(f&cZ`txke{=I8|Vk{RXXT#v%qu}5B zjw+=f;@zld|AUO6vMwm7t3rpG{Q}`RZ%e<@B8`QHXKTVsD+0EE*J_q`oJkT?q2eju zlx~naWFW;rQ)=loDrZ@CrdLW&wF27)>dKAR=Y0#oeIxaS3{IG3xV#DkZ>SvhZ2L@Re@cEu}QI?&~D)JdBwG8`vum5_@O;y>Sef0b9`gQD4E}j*9hN5g6&b82QCvZfheRM z+M9a8p?U*o+6hEFLpKB|XoC0<3Vz;3GfygfmBBYSCr-eGX#tZKIWsD{7rA{!t)wx| zo6E|!=ihz)@l)6ZFMoPV2~^dvXekrB=qeJ%_`csyuCA(Gk@O@stW=Cn_*xY?NSSbb zcbLedUa#lDe7d~*dUkO=`Sc7de0Cb9`6ErH8P3hwkI!P1+Lw@(;S5`?c;@>~rsrR; zKA(SI?JQF!GsL=QxwMfn*z;EE!Olm~J_CImwd>Bc5FYI9A8)^2f4=x~b?E{BC|D{i z?3|R1k8D=tY$X@V98`+o*Z=WP?65Z+;zetpjUJ5K7H{kjHJE z$=%ma)0@xd6~GQ#5Bt5!mlmB579We6MUkFrhTU6Up4h2$0?AMpX)gV{YmY@*c9os- z!>~6R^#)AovUN-!4ts-uLi6&lb^`pXid5ge>o6>Tct8E%p`C$oC6*@A0z@IbW*jY@ zE(i)uwH5n7#-n>~8BSoIf9HvHad!Uo^ADFdHBRl#Wr1 zpzUp6Id2T>i#+P%V7-{dQvZ(g-Eu)Df~Q@&Cc$UFajMV^AK|yQQ8~+Ng?`WQED47I zlQJh0x&bR@Dx4lQ^&&u^7lT{+jR_dmOhmk}H}X=3KUdxDJ(wb;%?s}kQW<#J5PeaA zvCowTc|~H-e}dta9uB$|36g&R^>Ajr-|zbV+uixMyYq#+lhpp9J`8o>%;cKqVzF-F z-?#LqJ*p@}-zaeS`2wF?jOp-iJkcxu^d9(}TKB?UM`hg9Y$uuG#x#A1bN_##)R?fRr*aRcCMCJa;#2yh>P$f6x4gZh(4?5-|^yFZCvwJTJW5m%Z|n^Gc5c71%j&wlaFk{|jUQ#4 zFDY{b$U`d2llwuLtk*hxXf1D7l1--3XlAg#z$#blMa zytrd;FS$;nFgd#r8P|ECMQe}U$(48qK61rk3HL|4ssh8r5M7UT8-i3ytz@s|KV9Q| z>a^ONuuSKgV^EwuU~j>5B-4WK9#GlaX}*7d^KPq0@L8102g?WEzrO*pMal~HaB|$U z$pV>Ta+TRkSC^wS8Dk!_M5Tg#I=eeK>VG=BJ33&L45O9u{NU*6=zx+{)Tejz;rwJY zJnT}aY;2b!HOUTJ;XAol-bOqTlWD{w$oK`a49Z(5dN7&cw-u!MQW_lYn6cejrFU5m zEKaU1q!CDSA0VYgTtua5#^hQ?R`f~b%HE!7+}iV-$n_Cv#4}TPN9c%ay>3eU(`zkj z^QcqEj#*+|&Ll5U-g^I(t8_JDVVs(2RpZu7ebkNi+cuIFNLV7c5v31&Djw;X@h>c< zD}dr4%r11ym8USn#;RIQY$%4Kf+slQ$p?b3p*dY9 z#&ZfsgFrR}1PcS0xnF4t@UI*D;X?2@k*OH7DqJhEsUr8+v=+IRcs~O`p0GKWi3!2w zdW~KXV>U8Pi;)o<2p5EO_s^}#X1A^D;zIGtSFsT(#7BJ8JTGLf3d7EBrj0OtAm5m^ zMZO$t`ZT})IBm@qSsh1++NP{%5>}Svi2u&SY9?Ys>w6vsWI!)$-Ksz3&}VQt>T=A5}JVZ@WE%L`Y=LrHf}`jIoN~L;CE}u0y3iZ8R)3b|1+No*6(>hy0Hc7$c z!0j{JYKvTiHZv`9u&`sc6^S6q^ecGg!VN%&&K{QsQHBj@1zLGigBa@JDVqoND93P{ zg}(GEo>8^Mxg+eHVgdUEz;Z=6hYukG595V=fRoN+QnmyhSN4XdV3t-ZTjj-tG?97` zdgF3~xRC3+FQA=-7N|ZUUzp0c^-3m|{=nU9mJUl>oaAlldFN#8ZY&!0?{H)?gbV z)Ckfno6k*%JVk}MY(;U1IhyCaq9+aPO!&!P@^1dm%*Bsn_o1ap&9m1#AZ)~o;A&w8 zHh0}`ES$x7%=%_+`g7BtW%E}3lxJFW0|uz(5U|TuJfonb9gaWYy*;eQ4_Tp+Rpj z7#-RrTJh{+o${58ZsniFjb)nKDU1$EYR>p#p#{2)!7QA$J5{+)lxq;_A9x~T@0MXj zjZ~$g!xHt_VJi_s225Hy+k-%zl_QL)*%qYobp{d4_SNE_ER6_3D71yaBT$N$+{XkX zPv})OX6-}MZWt6rD9gU)311kNYq?m6`dHv}Z7eRdqcM)+fHOX$_bBW6wl_LqyEuoi`%3iJ@wi&NY zD|^Wxn->6^tut;6OGQrU>*>w~jJ*urz=%EVY|AUL;L-X9>G3QHA?JIULFI0G^Z8%? zh>4^@Y|$c-_Bw;)17!AMx=2JBQtso{EbS@PK5?|?@XR48Wd*rJn#vs|o-DZ}tcl&Y zB4$5aoqb|5euFH2md#nB7BV8b;dnm{ehvdjEpsfpJ<5D(*OI4est_R4MFXBc{cMqZ zNO<}lKm($cC;RjRixL&xyIgpgRx`(ttJ{a=GP-v!I-d0G`{UepooY1`HQiGp!0MKu z#OdSYF{c}7TVW@qGWR~V-Ub}ng!V!==)Kk+`|<4N)8x~~G5A@5hBo3_{68(QFTc;` zXk6Zc3?`SO7vEom3!AL)rB)jk0|;M4v;oT>IayG@|MLE_3p==be)j3?=8x4a3qAl@ zCODj`_9K^hcdm37UZ$`c+u-jTTHx6XE!GJy(r5`G`xQU>J$9|M&!Otg`9OkbUWDS}SP1qFm`Qg9ZGXeKjb)L?>M`w=x>?FdA?IwZiZ@!6 zc6QogeslTQ=L>qyg83^4)%}CZbASG6f*P<2M&Mx#fM&vicAk|n)_8CRT*w{Lp$i$D zD!H?fp#?ElY@t|@q9wrzliuT@eiN4bD_U|)*ax}!GrRN(E-XvEEGZT;H9a;}#>jfQ z3)Te*$Nif>nr@Uz_vbv}X(|%;Cq%KysQVzuHx@Eq=o}pe9TwNBdYzsD8qOFBDj%7a z+8v-PE(W6Nu2k;`4e8?mx`?Tb3+I=VD9yQ$IA>rBo#)XKrPc}p7|COMhA8z=!vYQ? zbmJ5$s8(c|iw#t@bj8lNktT>+d1D&(s@)kq%n|{%R5VA5ykiIA#uO{z=jJ5}HLno5 z$Ic*q9+^xcZbYZv2jE&s7ncw3{|e|kt{0e%mfG}!6~m9=nqBjIzbd)?w|Y#4HcOe6 zDa*fsXH%3)E5!HWZzR|#y(Z0)-`X9fg_YV-Xz|31Od`RYKC3KW2NDX1$!hUI zCSuI`4_xeo?r$Ndam>e^G}uX zK4~{8flXm@wX)>g&dR_W-O3?!U2G0k-(?_(0(h0qDc-2EF?Sq=?v$i6rat7&uNT*c-c{e2%U+WoLYR;8ZM=^xLJpJ_m>7c?bY7gKCQN zE;6Sn;PH7XabsX8S!DtHgTt0(e2g9RX=L+Qm;t6PG0(~)X{W=bHVph-_evbcc8qQ{qipkr zy%%dI>RA*@)ZNy~OY68{s(dsyAWSwMj|D{VmWaG?WFEoq)9y%5+7g>U-CnU7Sjd)Z zT?b)<`6}!0I{n(lLr8kqLCmTeCWKb{9_r;XfT&3aBkm12GEmj;ZffYfuSp({D2m!Z0S z-h>vZRaWGsq7U91Zrs)$9ML5Q@d0C?@r=G{nAjUiIa9qADF+R_x=Y%FtBQTV78xs2 zygAF#QO+g|jQ>u3_~(mH%;zBvX?o3{8ps+*`E@_lGV6H7i*Juzh)j#fdF9RdgM#Ps zESvYdjm|mU_1K*X^W(@AQ?a)Ea5p&GhK0ImbeDIjhABxux2U#q4A^Jfi?meqPkM|5 zAjkOI_Dx3qA2O|)i-GrxCo9pYk#_yB>Jh{gJhh> z?HPcF`=-N;AkQn2WX#71!@I1IN$jGgO)-nqYQQflYE4plZ9?8{SS_Be=^`^k=u#QTYlntZx|P)nOfO)2f{z~LfkLJ$+Ge_4SAwSovQ_e&aR^frIfLqw zaBFh#)xgMOVRDxN$-Q>+uzVfTskIl_c4GF{_Js~vk+WD(mi>nVS3T_=oikBuql?s# zNJ5O66h(GBc>k1+*U6(fppwrh7P*x)5Fl-+C4aCWIjBOYxTz{~LG&m&Qh_E`mlnxF zMtJn>1~dxkr8XKRxU}4DEuJ!=CAc$r`%x(rdYEbs{(n|d@@mXdVkGcA_u#@Q1;?{$ zOMnVBiGn4t5S^*7%;*GeL~R1p_G$hsbeJ_3>Y?vYblD9Gse`ac$)il-i0c@ipnH@? zkla>#ExbiZin)O97jC{aGsF@Kspg7~6B3=&uV8W!0)n*)$hNZQxY&fW{~zGxG>&|ZrqU9r;@*Y~i{xX)ZcKnoUn zxY&VfuJ0@PqzGW3MyS-m^sbRIb&`jyq!F!HDgAKYpO^}mrXd@uZo1LjayB&aYbna| zdv)n+qduRN453Zq7vNkj^&=Wp!Aj`TQqI=I{mEmXNod>ILDLJcm|EcW;`Vp)&}^J) z%~{iFKz|Qs&E96tMnbgJk}2$Ua*a(k11`vgkJ>r+*hJ`>MWD)rOZQz&o#_NbDk1jl zy5-x)B)8u@lyYA}EO{0n`Vlx+AZJAO4I$Z<5s+982dv|JQNyF=E9Q&^8&5HeUyC~| z7F3-G+F22Np?O*)Tsu5F%o$te%E}s8=DfJG7&C{k4_oj&jCQ05|H{HBQ&nwhe?lI z7E(&} z>E|AHfyBrprqcnx^&g1IKQq{GGtT(8ytM3L`>ign;IMAJsa!5~w?*np@!aK8ch0B2 zW}QZE^tX9##brt< z_nmHn*tO1sg0!&AH3}AziY_pp1hhhO z+M{LjhWuylaY6`Ry$?<{WfXN8$egFamKXr+zOw) z^?4bB$HuTXI{3$TKFq5E>UE<}rq55U)^-bU=>x}f9tN$!BR5F0Qv?2*WU&n?OnvIo z%Ao{3sYN6R9>Kj#@gCt$bZdqh8w&hAn@;MIb*L0@wd>EO6DW9I`c3>E682x!B@;}2 zr;8>e)qlCmW+P;A&XZ)uqk9%rDuEf56y7Z74dYMwS4zN#e$A~tdo2}MiwqL-!OJ41 zJU$eGSGQ!ct7E1Gnts7xs35rjs_ZhMlXZ{X3c=#Aej*jt%DGH{F%eFssn3yT95_#I zjbbRvYxU8$@oNz6Z~A+Xc)E*|bQ+9*xx0nAe;|rEl8Q9aYhTX4Vmi`1H5rEpZKq`| zTR(HcE`1}0sqA~~9A_ycs4rw@M8u%FkQR%AYj_FZr3T;X?~`&^Dl?^B42+`xb!sNI zS*{E;duV_KHxoW{&`J_ zWvWwrv2zo-9U7C18^2F?02IksqbjeIWiW3ZM!T>?srw?s3R5fl;sYXwc?z(jljZ!4 zg!{TkQvnK0SL8r#1T(lV6jzA@z&z|PE?OEa0||xzlF8);5VDud#G?~X1h&t3lWjVW z7A!qjx@T1CcmYQ&OTs|KxAFmlNZgAxI>S}SHzjY8(nE!y1w%QnQy0@3B($!;3Q=xE zFDF+_(14dG<6f+3VM?59pnYP5IVsUx7IgxH88_TXss{eIH}4QfRPcQjx>x1%+{)}w zIgBP#I&QI3w$UujdQD)$GD_`67?{)q8cM52p} zwk*TJzLI+1ZI=eo`2NID9ds%H^2}@a`xC<|jW-uSY^FrKOt{HqGFJJAD=BoX7$VS5j6$N^AD2S$9^T3#f`v}P?Kp{ zTrmI$zUBPUP_xS?D-ukmriPoF=cftj_w5}`VbBQRl57d~rk$y4!|ti8YG<);azUy^qapj_7da$c6Ow#{CsMauJ>M@xc( zx3+zZ3{1OrbvODDGPZ~+&YVuFFAIX?b_#x!T){vd``MbVA&MK}=>i0JXjFzU<$U2S zJGGgR0k4AGzqO_6Rbxdks32~S+jfI=!Ukx@@1}eu+T-^B3&{Y`Q$)o7qx~N^%MIer zDF|nBcFnY+KpCe>b>%e6(%v~_-67lHFZM;^Jr}6rGwe$j4f$w-x=wVxAc@Xm$R+5F zPoM(9Q8`rxm2--r0Q!C>5~A*{5KRG+{L%FJivL-qU2%_`8)uEYeq=>L1r-yZ*G7vSv2Tj1~$M~Gckq}~Z>zWe$(s@Je7`rH2E*Lie={UkI{Ngxbqq11Kj{EbcebO@0KkbJZzpAye^+9Iej@oxRqw zDzJv~<=y#|Ron)tpcsNUn@)OO{{)yv~Xmvx}P>s3Tl&CylRAZzwfp=swr4e$9?s_h5x#Gzaeb; z7F%_=E^?HbCNs+Po_4iTIZI7Re0MGo5}TM@DlaUP?;@k@B;GuL71x?45D87mVg#c~ zrcU#VHB{>XD3h0JsCLLbnyz@~4$>-f&<8vG9Q%aqq7wA0#iB^K20^g3$KGo3&edB5 zXY2rd+?hTOaK+OC4I4o%S6?r*UNQ8AnxP1xjrL$H8#?F>d&6V=sa^>5C>jnAPL2+v z!_(ox$?){_V0g4YJUSf=#q4-?a5CeA_*4uI;`zaT%ukLFPa|Loum0EEL)N5rNexz1eBCP!Ri<(h&E2OrS>`|o5^Mtye+1?*yD$UaF z*Wt;)7szTZXGXH4hzt zN|{*%VF~T($wH+yT%S6dBW3}qkgP~TPm^~I3XPYyLA4Gc4!BDNLTvf2)lhAj27O=5 z=Rz|h|Lk>W(Gn72xEVm5EC{=egoJ#Sn7|7%g&3dlp$u8_zWO4^be(@HL?Lj9ptgPB zN9sV94UuWX5dUm{yp@0UW&qr~K9!usE6v#)YElMl8d6`_bUwxC5X&>|}Uq zzaH(M?04Ay==kWk133y0%NZff&w0ip*hFpHvz!{?zY@8Yk*Osv*m%Qjx6{t-?Tw{L z)_sE3-y?29e`EZOQ+8RFsKrvG5E5UtA@i9k@)~ZS>cxMK8{9}xQgP8nZayHx!>b+k z4(5EY*E=NhEAettu(Rnnv)|^kaFZ{106gr)K=8Lg@8>#}J3Jv$M1CvsPK~ zXC8%r+3ghkDWg^R)e^rl{vTBV8b=ePMyOBR>ztM%S+Vo0Nqq|4u00zhOgIwLy#A^E zZ%xzb*hzHqDC<~+I~L83MeaY^0c_81{&UkxB%Z8b2l;TT+fJtJb0*SjhrH4&o`((=~FQ zviwu&F>_mKQHy?`YGs8yQ5Z`l$OPW=FzWAPaB+6cAv%}l;9_&ju9aIgP|uVAeCF}S z-)4!L^;cY`eSycMsZ}@9ai4gueFsHK3uWv$Ux_UA(e&1QmAI;D8%o;-hejU5qSR=I zYeYS2&Wz0~yaSvjS4IN%i<$vS4pN|%YXs%mlU~cisVGlPqW4PLzToZ$%MS%O3D|e$M*SYEES_nKw^aC~8Gcpzt*O20_ z93J01{?GAq2PQ$<6Tq*&7k&2bbdEsO;|D5kKWuN={@#d^$|n{pi@dM+N_Jr6w3JsQ z_0QQ#Lx*h>WPm(9HB<(Dcx_+O!u zf41XSoBKvh;(@P++$#&R)^Qbg=U(L9Y5hhP3<;?%81u#NS(z*I8UjV|gwg64IV+2*e>M6b!WlC_KrzZ43 zRF7l-i+z51m3vS;h)21IF^+1?zCkCfy!|Z}>G~UR2r!*f1_1QBRr#_~JRp}V(V-*@ zsc=rDbi#W9!nGA|TI=UlyJ@}46>lJr`wywn@B!p}%_D)HrO&khZ-6Zg&h5LhOTJKL zo1pct+HbGiX)o+GqE8QqVSD;%cghKNDpM&T(|!Ug#}3=i?w)bE7<^ zaHG_)!2ftAAywR8>4gF~_Ylcy4~KZwsB)Itw~*6^QZA61!puvEPS2Ew89LkJgVDi3 z+ofvcnhM?7H6j>R!e4h7H(}CODn`5)NSrtjrSSz}-hzgFpYX-kyrePRBz~mq^aP2L zhh$pJsT?(xzhaZ=!x2-ulK2D*r6E(%wdXg2m}Cz}HIE(2?ha{1X;jpFqDH((?^E@d zQh9GCW3%arV%O%Im96h(_66IYL*>rNh0EEK388Pd#uZLOL*!JHfr>$hi`Tef{0KSI zL%0&B%9#*c475Jjx0JZMx(%d%^oCai5!#&$FcK_xwU)Mb$n(4#23kZA3l9(0j>=97ombc{0mS<33gsCx z*_MftJtUrGT4h?=X^`OpGT-;=I)};~N4Y&9ME$Y*c9Lvaux@FUY=*^~OyiJGGq8EruI=)_?Y8jQ+J{3}`7$huy%m~Eo$XN< zI;T3g;|=FKZ3=C=B!w6suBn}tvBlWKB*ax1-Smx_83Pq+q0k@;T57a0q zRysI6tZWVPF7Q@PKY$j23&dM37F@@PWh7`Sl-hCy=@QcNT)ZnO1i4EowYH-Sq(#JK zY{NN8WzxYESSeT-3k|u;e^E34cpxv)67U55vN|qnmauPuu34%a`UM_j6~ep2=O@Q= z53*7fy|U20NoYh|$vBmZrH?Jb1a>ipTzCSCE6!D+^CerUG+$D3$901`MlPYL%FVlGJ%N+rT;46g&^RBFZ=~m7X!>_n=K&Q0LyQb_D zZugA|kaCnpnsFnJ4wy(IWf{Tr^4h!JlGsvZ%b`0E1P5xYRedjz?_jfWfIwhGxVKVx z)ueFwlxxl|KHZ|Gx3pu017Rw*{EYz~?YFSg5rfadMT+6h!6Q8wU@{Ga-d{b&Bs+6J zoLhvpm))j7Q@IMu6bH0)?p*_Ap_tgFaDOx#9L-M#qy54D(f+~V@NjrMiVg;cr=$4zR1A)yIgbyIPovY5gW-_J zr^iPp$ESP{4Ngx3W#fhu)w6C?!E@w=bl~A+oWoO?5WIR4Lk|w9;AJ3hW+O0U=qs<} zaXrRt7={st-a5X2mT)|(-fPFrS;c3ypO_COzJMYcx88SYf;mU==gTJ%6)tKW|Ld|` zWhzmNH9Wnm4Bg7gHA5U2b#5u|Vbz`okEA9;A7l3?rpvR;#g~OKz#!8h2FzrTfi6<1 z$u+oaRY~F~;yTw{ZY*z)&DbQxOQFanf;O)o=Y=_ zT()C&p+2eH2_WI_P_b}1u{Pb(sNG-WgND!`(jir`= zDs8VF%vc!&;chTo*Ax;vHKa%g{HBCnx!lhS%_G4wp{0rgxG0OUi`v~7^!#W$PN5&U zF;}s#>|+$4n7smWu|RzBw_Ezj?Muv+VkI@1E-+}*$wlFB<>mDp1-G%Ez`wWI!*{3t zrK9gNuD(swunZg0fWg9IB|>*zZ|V1Q{MpTOZa(4z>FDrqf3yv7DQ-4`H_foIaFBgc z-zb%T@O1?%LYywrGxJHMH&BYb{^5%e+Tj-bT)`QCk9dY|qf!JCNUT*B$~$LQSKH?3 z!bUb+S8K`hurHn>h&2yNfn%6%sV2jR0O~P2E|*VPSyyT1a)0$I+tfGN7!lnP@wECh zO(x8PS9&YgVW&P7-TrX2e-OScl*y~66rVGmr{wL5l@{Puk!g89hOzv#XAz2pSEXm8L8bu~q^vYaFhyGt6j27w45yp>{%Dnpp4@xgYu zwF6xW0yvva-jz1`MoFUyF&FA)LaRwlDIa1;Nz{x(D|8F^05%jXVVtV88$hg;9ItFq zN|@15PKiutRUdV@^6V+vnA>l?#a`T0PKbPk)~Qqom20&{;Z%XXv&w1P0CjCsIfg^r zX90xbH$1u*$USN8+^#F-tzZCPK%c)2De3DfO9Z6M;~MG-b5nMysfV>-QK2=K99wc7 zg$v39bd>b+P8 z2Q=__cfQSpc9rwq5mLj!fKalqFoftzq}TkZiq?-#lF5Yb5)8u5b|kc1jMf~2Za7+e zEhE#E2lGK_XCHniamXo-!LpD-) z8VG3a`nx|VL)L)<06*AxkpJ&?j3_5jCb@SJAgeN@rltu=gR)PMe+={QI(1h+42~)R zKI?AoqiK!;NnkVZrHZU0unz;NGt<`ux3IUSng9JY#I!skhwL2J(yiV%8}Tj8im6vX zY>sqcJIAPOi&Ckm_1E=C^fFU0R@@6hp2Smz6)6>)%QOza)pt&?nZr#KL^;$&mDfhi z)M%wQLrhAJ{VQWwYC|-}vcT7c%1nbVBmCa15PHU8qtpdKcY5ggBohsw=&YZd?jt%` zrq#R&ANcShLK`Z;5+ejaD21+s4e+>g<%3&zsZ72D%$KKmG&lksOCiUV%MbJdWkQJH z74l`WJ(H~!31Xh8w4#}y%M@Nml9Gqw;~}V%RT%+%5<0#Zh0kvni%g`kNFy2gzf5w9 zlAr?E+`F{#L<=6T;mJV-bOMhfU3wfP?t)sYg8fni#D#=8C1S{xeQuqaKGuVJkQ}^E zI^h(*Oaj3&R;hqy-@v`K$QGK%0-d=ZH-VLj>a27_&K}?B7ZS;6t-9Sperw)^PNkF7 z10tw6o1InQhWZ6ExtELDAwx@2Uh2X7R-@CZ%tO6FFbvjYd62PSob-wmGhL*<4CSWU z!&F~s233z04h{l^R=+@+v2o>RU#kQQ01XZXgWC5>o#pc9E@cX<|IWM%2A!!wb`fbv zEnym(%UrBB;4>Hlo|*Q28tJ^D0fV5kF;QhpqX0^&sdxrTb0dNh@fT33nA)jz$vJ9% z2i-#chE4~&Yt($2r}8D$zyWvDoYnji|02a1J_7AZp_1jVP>TjXkW!#R?Q%t(${=v` zsJVJHIeYBn?or4tK`${Y6XWdnMnU{T&9j3K_77&q;`Bru34VNfB;wIgJU^Kqh~we> zWPctFj}DFw`S3Iz9vzAK{OFhu_Gc%fQ8bv%dEid6#kb>S?~bj6s+%Z!{t;2NQh0;s zR-;S+^cll)lWBPukQHOmHgnPLfI_R$vvwoK68RufVa!yknP`4Zxh7CJRRkALIB@AR zRel@vhNFMn`ci4Ry~$(w0tUX#?}Gx#?Nr~_>j0o48s%>#@%jyo^(HWbkq_k?zZh>e z+<>=%52EPgc>iF;PlqR?D4NZVhx56x|1&&29*UEL z)6r-+i%;Xj)BV%Y@4#w2g?>|B)#r;c>fJ5kN6&ncwbvkr-`bcibCE4-l!`Ioywk(7(t zf?w?iRfF<3C#e5oPEbvLw*{WrYNk9^YW8>bRwY1xb5;GDtLkpvcC=w$aJD}on#jf6zPv!?!wT)HL;DJ|CDoz4wiA%N{W1p~iX&{a8gH*KN4jYiV28Tj^CF&nbBCvq;Lb>tE) z8tCxsrn5*rmjta(*>4lI@I6ElUvqO`6G(Eo3Pea!+6O9ZYZ8&EYzw=9k4f3Kk#$)Q>V49wZnIBJyDoEDa9{LxM#*N$fw7J*iukU`nQ zQnwG{PSsmB6q9+9gL4mhGkGIsISt!iri|Nb7!sWT#-U9JI$iUuQZdXP-b5{^qHrC} zJ6!@Lq2^VhnDPC@Err#V<_lqj-Ae zV|mQmFP`(jPr;$r&V|VbgHa`8QXOWZ7JbT-(YLGHwLfIMPkn2ktFxr@+4KGq?GAIl zL5felf}et;S#2AB2s=2o5qeqH=@@=;jLRN;ix=4@`&9r8Q{#LEQLFl)OY+yS3bn>l zENG{<>cb7*Jv}}=Jw1vKqVDl>xu{|M%;MUwX*_1_(O|US9S*vK)4S3B`0#Xmc-kEt zj|YP`)qc4488O^T!dGPqN}s>|7U}q#6)eBqKbTx;@$Fr&=-!-=e$! zJ9Y<-bc#K4nY-&1>qn*Hu^d#TWqC0FBT9a~)tC@;hd%mFHF2w}Psy>bl%9N_{w)T9T4xYx~ ziO9a6-e9F*cCEBf4?=g)^N>a<^TRoFVK7Q-uQm?%6|e%grcfblm)bUqmgx0({>i>k z@*?ZCh`D}J=PFHs4vug)mTukxA~U`kZ&qZUOc^;dGfYSTZB|GvB@jZJ3HR0RCLvS5)E_Z?7 z4P@DZn#qv#T-$AkDpII}2mh97EFWZC@Prf!b4bs1c3nxeG*v3G(55Q3@JMQD4+lN? zzy1j^fZDJtZthWv{fkDmY!^!nj1DTp9Q~%VJF3lY9uEfNaep{CsLk%^x18NkIlJL# z5RSv_1~NV;5XliTWve2|C2TK%8dUMN4KYzV?y9MZCsR96^({}lDtyMs?BSrM|Ch0@ zzjRoa4F|m;EqdkKs=i3}gUXje*M>RTX&4~>5EkHMuw|grqpA{WMQ3bQXp`g003(7k z2X+!*Yd-0gtHs66rW60RKTC{am_3f75OQ@dQZ_3fYjZ^PAzbqc-yaR|@$CSgO(#$Q z^Swm~asJO{l>bx+Ero%4qXvRPpGBR3R434YIw7saTqX$s>k#KDi!_yLLrLLo)T_vx zr+z@tXXk&=+d_bUp5B5xrk^*@WY?n8g}DV6Nmy0Q-ZYO|!lw5p%2$RXtC<^rGlZ^6 zyND-vn1cGsXYgD(n@(Chla`$VvL&X~f(hdN^g|a@n#-)a;^t3Xcs;aacFoP709ZrS z*}cGh+#;Q-)HW}K=CQDh#I*K;2hd(a17FzA4tgci`zsakBut4|`Cz$9P)JMC6?vti zd-CYFR0DMYL^nFwquuRVA*k(^gZu^`)V-_C>I#B30v$6e_t?u9SxM;Y0Cz%Dy)rcN zIkr&ia`cK4XJL(L%!m?I_eAUdq*C9BbZWu1=IiHKD%d>(m3K?HfyW_aQp8>;Il%4X zJS#(gZF{e~3!Q|F`ndI+pk<4r|4o=3)iDFSz84~+Gv}*iJ9~#SwLDfmKk1-daIsS9 zt;l<=dm*y2+P1SwzBCMY$h-)>1{{2EO5mWBC^GMn1B1C3qkO4_S*iro7}OF+MbU6L zIAl+3WUQQ>nuJmlVxBYbz+LhOsS0n*uRHg=9gYUZN(p?;{*mnu*c00~?^?_uaOcJ! z+o2qGE<(UZR)XuY=Y17CAzx`FslceU;Rm&S&Q*V?Er{)i*9NQ_V*7*Eri0XW)M~VB z#BF~^)HY#l;?~6cL8q2!^|THva#(4MHep!Rv9-q_zn{-N%(Wnuc89fLJAxX3EXi&5 zw%qFPI;@>>olBl<{BEDDQC|JoGUM01TS}cf{1YbCoogZbtv&k#q=~)>%Z?DrL>R1` z`Df?Z0m1TNw83_ASzPaY5P+fFmp+&38+uNRhlA1K(Hn1#u@_2fFN|YNV|;+V`Dr}< zxuN+{+1QTx-WPgv;buNExM{*zG5^o+Wg6S(B0krX(I+7n%L{2DE0=6kYrh(?)HawY zjp<(@;UH=4P)^tkD**tX$GkR{lSK*a!?x*qmt_p5U%^ZffxIfa#d$CD-qlV?GlOrM>s zEU8_pQ-s|Lcv61Dj34pRcAl(j1G;bkQm`+WgA5p2|BVll3CvwtztagNz9 zMx~Y68lt9*N>Fo#N_3Ve9=8&;xP?Ej6yGX^toR}k58yQc4o^8-a{V0R&Fv zE!E!xc5eUu)>lbk*YEs!@?q7mGHTAyFQN?X5@`;hp?f9Ru@@*T4!45*!pjuR-AB9Y zYHsgF1V&N3P{!AvA~`wRu2=4>V6%{)aYn;NjsfcL-qOLqvzBu1YM;xc2Z< zh3VR0$mZFKXY@>bLXLfjJ;qQH^Ptsw$VE1;?t~xy-4fyh*q$^ z_P2Mnv7W~M{}@}3ga5;Uqrj@+Z7XMZRfDcAOJpSTWL+uFQR<3;Z*Z_tkO9Iex53zBBsix^}=fQG?WfvuXSam(IeX>2^hKr@%W?s%X?wzyr%w0QCYlgW8> z?|4KDQobi^G?T1>7g=;MBY1+7x(o#1FN9+T z^G3XKMfXx>1%=$bO{E4XrM1z@X9`kHa4V!lTI9m5afiuyU>XXDhekLH3fXUHA*xqO zC+d?B7&{5LVd5Q9jX}P5@UD$2PcE1c6*=b764;1$D)T{WO0(IzwdeGLC`1>z>LRY- zn%N~#^Mo&Ypr85#=-ePSsOk81yZ7jCyZ3Lq_kZ#3J$i2UlE;cG-c6-3d#zQOS|ohr zm4V=!U0&3=y2pooAF&xvcpBN)g8lP7{3Xiq%CK**%p?_-`}ykN>OF~?v7*`#WyEdE zxeIoACMnz%UqEG&fSMkaCkv%zzFfI?PfGqon0+Y3IcRH-6Fs-+g_V%u9kS(Up2lir z7Y<)^(8l*3(|3=<%Dx9^cr?*kNbswg*O36Lg;=TFKlo7SgMm%T^A3xR?y%*0rX^yB zHfQ^-oOTom=c`=~?XofXujTStz5?E+50bNgxtIc=vkYQqf>P{gof($_5N~4zi&Pk4 zyS?kUbMp3IE~f1L^aB9Ov|4EHybb6O+O1#sN(})+h_!qW`i)_N>tyY6QXwFoFW+_G z$Ow~n#e8nxCU5QNG%k?Uj$I*vN}Yg-?_-Dsp_h%s(*I%ma)O?svC$XOWKbq4D(k2} zQeKP6)X^90ypySYO!LeXiCY5zf)J9B&pNxH{g# zBVenE^g*o|XbHXJ^U}x9!DbCJOF7f!k_RT9(hr$V36+y76`;win;drtzAIlcSNf4_ za_KLVkvTOBYA(^cJ3zc_UaDREBOeEtCiaQUeCe}TQBEo2Yxe}M(_xYSO*c$uk*=S_ zu$n&5a2(M41v;a95avv&BvCLTr~T7~_T=lxxM7bXNqWJU?tF~d1^%mH+}WBvV&xS_ zs&d*r@=(X50OfJ>yT=u@v;-qnp?CSmP=*TXPAxz&d^7dn&yTn2r)Bk} zg2r?Q3V)hzUI+ViU$l3Lr1c3$JEIH<9Z)C7o7mQ->diK#S)y4 zxZyL=#Rp^1XvUoxLXBdYW06w6MgqHu9OZ`Fa-6z;;CEX$DT7pwU$vUaw8QMN$iLFj z?zmeLd{%`xfxUgz0tBb{D{giTDOOhg$IBL57#54%?|geI zXm~rq4NkR!1koLBL|}UJVK~lU)JQx}eKfwD+n&v!$q9x;hsJ^E$q`;qlWC8A5`qM( z%lc4serN+UAyWkwNW`dOJ1b&INr6IH`OcYid{x{cXEM!Iz? zk?$~`Jn}WFR#O2Y@UGTB7o^t5ar)Boto|PU)eT04+>oY~k)F{1PA;^BihFKgGy4X) zw(`j}iM!mZn!vQw~tp=wti`q^Z)M|iy_Owmwb3=~GttlzXWR%rJlpFK)2`ERc=H!zh z8JiPN{@;~!vLP&`7GmF=8nU*I8_{M>v6wqTY6`IfaKES-pygoM{qzzr*T;v@k^A`z zX)2piRN_tco1~}QAvI-FT1v{dv1`7_4Pu1pC|l0s6s>%l6`s26>(`aY`Pb5K;wuDg zs91ch)Q`ftxI||A*?-ZYH^qPZw_&iDuTOuuF`dTLm*q;K%z^ce;pTuiC6IJM#RB&q zK13SMO#fksXPH7Hy!RE1kn6(a_VeWvcuiLYCpWh+{X!5WfSwYR_|H}6cG4|Q3iu`p zR(If7WOAx{>*f7fXyBNw?aSJ3mL7Zij|k)IT&XhmBx+5{4;|K}zh)d?Z1kone|`6w z=m0gdTN*2MtQ|Xs=m7jDr6t91JYukA2}!KxSV=})u1fjprO*mEK*bEb6ilx|qVfjb zfDQ)=TLVNA`AE&9=B&+=qzzl8ovKu{!GFm=V5z650S6et04|yJqTrgRd607sofw>{ zl*ID~p^Z!PUjl`oc-lsID7y??g1b>FyK_mIFuKgF)2M4N;I7O}_**jTu6Sm;R>tk# zpO~(fIPs$WsQH|yoWlcn$(6&XTClr2PvW+t9zDTBZ_-YDFq+Ke0dFh=q6> zvo?h=0*J2w0)*(2pmY*0L0#i1iW;7pWQ%RUb*q|FHFULYdeA0nF|LLnpi5Lx{l-0T z6AsoIs3HIwvIScSW9&426>>F4R48biysO$j{o#+@Kdicch}j?RdVdf<|MZ9a=b!$N z{QT1&(x2H+e^`wFu=g|j!$WYJPkFvXjh|!YG)FtEJ?CKG;pw_psrlQF#Y5lZu_`EV z6|;)A%O^LuWmpoSgD2zjTximYaPV>oLhE>*i&d6Gc(}1MM`mO{laqVkTdj!sY!IpxkW$~>-FZc{9jJu; z)t6%+DtIEY0eedq-a8zx9K@BT_ET;cv&c-Y1z+LdGRDiyM=DaZfg2V2UpU(i*dT+n zsvsVNDk2l80`Yk_Ji12%d%Em6v>qEs9(gRwQl=oKh?p%^2Gz5b}c`@{ME z@n|qV-5(y0hC=KQPKSr5;`n3~pPYBs!h2U~xTY+ws=3RT)cnbtTp*$eAMSqp%{Yncr;FxXyb_bE~7ogZz zzN`xKCH*S$h8t9?j4#McXtBRjekrz3Kgdkkl$G+iPS;g}H6@^|2P-^RqB1;R)ory) zeM-Df&!?pptJJ`lnYj2Ex0k+!&n{F}$W-P5n?%i66-+e`T>DE36i!9l z-hqB4_N{H%HfxH&=XzJOYzB9vkLf2j>A&~F&FeOQ#ZKHu58JL^-)>2$eyb^G>|P~b z?4C>Wdf;=#?gcqs4?apC92{nd-P_0x8}>RJjxY^#J>KrQ5U-7hJNA3V+-;A#W4|iy zt|9WSF$ei8qVKlG-|dEB1H8>Rx8^kEuZhFk7KyhTl1=a$qVcvQScDP27<9AH1(jD* z=*RE&ZJB~GpxsTaF%yY+pfFSUAQQ0=u9_G9xoH=;y+jAgZ5#|3!?%)gK~E!~)Bn%j z`?t4kBm3g}w_gPwrF&!dnzSU#KX$!4i}mHR*Zn z-v=`Q2$CQr*-qMYqw}0?OoD^Kufbq2^BEJ7gklRM_c3`Yx&^e?rNT=A_Cy-I&54MT zfRQ^!ru3G9o6s!_)vb$W(}IGKRAkH1ZwrBHNTj@K!U(e9B{@PUQbA&#OcZLs0&CFT z0pK$QrOY*svUI7`g(d0x6qv<^xH1-_wqm_lt>*|aW)Q1!q3RU1?V>4aE|@Tf>9dQA z%Qx3=-kx3ToL#)VzC1aD8MbFNdd3Z3wWl1PL60B1(5ZU_Py4 z{i4#C2h;kk5+OH-!lP+=TxyeP#+TRxc$e^yJr4I}{N!tv%_{eDz}KlU-&UB@8Hmgs zO~O45`dun$VJDGuo}VR^x3<%rP2OOXqV0x&DA;@bPE=AL9>bkjVfmW-Yg}$ z3k&)aAwkONFiq3;OKw*yXm;&gAEjmU=EXsCHf1cC$BYsj8xK$Sr@ZaJw;+Gwh z&N;R?91nU~!+$+d0&Uwz%?JS>J z&XC45tKt>xV;OPwm*cLdJ;!QI>ea7#+(}WFd#3!+d{fFJg&C!leq%)nj^v(SI+DMo zN%*$zE`WZ}`Bcpf)$buk8+FQ)`AMUnSqFJKO3bI8nvXMdeJ(|e7o>P_4qO=?9EQ>1O&cyBOFaHZZbow}8 zFW^L-(#%TwwRfX5qi;#SFn0^GIV_U5sgExkR1`FR(G ztfRNyH|iTS1Vf61lM3-Ym<8v`Hzwo(+|g(ssEsGJ-^LSx%C1qdOYX)mFd!?N8@C!Y z?*U8DwkZTLqlrRw{Si~$!cDB)xN;WDnK4h)Y%1bbO}<@`^He{^_>7!pCaO|FRmvx0 z8suW*i3y|x`-F%YOHal*%g&N0cL7l-uQV%sL?_px$%F~A8O*bcC3(EqMuc10y|#KlPGFIj-nM5==9mM8DK<(Lk^(Kw*r|V%eByGcr8CAm0uB4N-ji!=X3_ zKW&?Kp@&_+x~k)C8>An9LuDaVN~#^XD#(=I*%1s1HlNc_#ZE%f0=00iUtM_=LcJ8A zENYXWB1S6oZBvTqcv1Qov}gvgO`}C|4U|kIU3!U;9CND1K852`j4?NM=raCorqO@X@l_TL9Qo{QkmgNfjBC~Kd3l2cQ zRWcF>)Y6>MKuBZVa-W=y6>^n=`3sBG*Sf)@g_dOo)6>)_7coao-maItk(=y3=oXjEz1C6rEv=T z2tuGXO&A63kRi010ytGz2atV=d6=If=zXDWlVV8lGTI62K3Y8%-kj zwY$X2qZz}A{u~-xKPakbdR$;S+Gmj7KD;3kc$8+#3<&^D5$H1SAt=wN{i3NWS&U*2I-}@)s8DF3<83C6vt}c z&Jl3j08*6mNIbDuSD8*E6lR5X;nfyumEc;g7ypt$=&OIOPdDW#R-H|Pd^A=aPyU{t zes^|GhNqX;XD`o&C)cO&)Rggkzg+(G{6%x=$D^piw*Z7l4se?BoCiGCRwKEIo0kxG z4xKH9L+DL;pxy%fU~RL+%@JUFiC|U$0)${9iVBRFQ8M)jNTV-E<~kYs zHw*F(>X}lff&eVML=o!prz_P^67xHjnKyS#uGx5`5KcN_#?wwy-+KhBA)Bf%w(zO^ zXhX4g&Xo#vshV`GK5`+2uOxY`Icq9Q!yYk%YQ^^Zk&>iBLH%_=!1>X!x) zY$7j9NJ{sUb@AH0&Q9)U$8 zN-lq`{a$xMe+&=tt9drDGU_$Yg|jj0tC1ihHU$v<62J|@;}==hG;WOGHr65ngmI0g zr%{rW!z{$Kl|9hS9na76C_`As7w1>l_ZzAM6Tq(o{~mSIR@IwT!e$b3SguW58W?=A z=nE)$Y;JbSMqrKj1U*tfl5J{NJ8fG+Z7gQE&f{_78g9!EP_u9rQ*AhuvUr zZ@0Tk>4?#TaL_y49R>Tnqi_(gE(?$9)!vCxrFWuQ z2zL*H0Ud|q{?TaM5B3M$!MGpN-LS_-v^%1sgTuq4gZ?v@%l zZhx2V4timCba=44+wak%y?z+%?)CRa?BJ*uj{3bm?eF*4ZpaSVUa))E-5&(Ofc6eT z+CAF8*U?nI7W45qdS5zL7Lk$}1MDoJy}hCYu)i>hGRbvOdWlf{*an}7JkGWsa3AfFl z^VODfPg_pi=Jr_jKSSr<5z19U|FN;k)sDEIG4-By)O7<~*D)k_lQGPgPlaf#fN(JUV^ira;L{Jnl!BpKfAYGMZTwQOXm!M<8b zbF*Yw9sRXL&ItkAPO=IDTcT%>V0%?-RlDzbu_HEt!vZ-25L*x0sR!T~lvs@E{u+;? zox>j-BvTsl$B2`u5NrvRSsgvLLbC=T>yi#@$KI?R^HSj|tH*1Q2gZ?9YH-Gd=(X7n zA8Ao~5Qb!J`+j5;NkYmvlFk|4f+bl)lXT%pwy52(dr2VxevCSSf4vi7$GUtmRHdr> z)FnixXk!~}g>aI_5QVyrzf`>d>wqfUqJ@2{lJd~L48hQsa~`s5Wo0SZ<>8^W1wgo1 zk&Mddp8z7DA~b+5%0-n2Kxob62{Jrl0Qzb^E^G}I`jVoJojW`~(~j3FNs{ehC+G|y zAF2%kl_<9x_&=q@1z;Kpc!Ebhq5=$T#X0BXDU8U|!rL@sRPY4-ijm_p@nibb$`jf= z46;A`f-O}e>MKR}&l7bMbHT%(AknamVSdR#B+4IrucLHHPXt+%=JxGQur~+}Sjgz;U>veRNcRuf-VqJL-QE!$^#^p^ zAM}IWvD*pmFgzFydch!|gT2H3Zhvnu+6{U~tl#bTx`X{b8y_AG4vt2n{oUPOxPP$U z4F+t#dlc^P^+x@pPw51w`yKjEP0#k5fTFwaZ)11ZTMQHTwtN{1Yg&3L->q!oahUft zDcihnZwys?vfcAvq9NJv|J8!LrUiLb`=IIJ)eVFClQzR(OZ~?4BlnIZa(?Nw@fv#m~5o0Da6>UP(#IN5Sa_)Q3AZg{b;AYrl} zYe-*FLZURs%}p)EnUtVXRNrkMWn3PZ(jWP>ah}b${sm^24*~w?A7JTNNAJ!g0yj~%HHR2&;-xWmV;}7N{=diL3VFDDctrcX z;4olpJQ^MDvx9Mu_Vz}-L%K)z>E3Afpg-8{jrx22!`}Uu-`ji2lNvw zG+v;pV3_-Al| z&X^a_V9MU%sVe7?=>p$gWNawkl{CW@(`Xc2zc60qaKN}GUxfn0 zRPG_b<6!+q?^YYW710aSW)QxjVHvnQNjHm<|FoZObG~aD~S|=@MV;c zcaXtSZyN7NK;u|!Z#*GUF5AG85Fr{41&O0kMl;zsm0%Fp^Y@Sf6EULFNcJe5P}#kh zfVhad0&p9U<*aCqZiGhR%xfnUm3LjT1OQOuIbM%vOzwAZ6%7}&@NjZt0Plos9u044 z5-@)QyG`o}7xtm1=xGDJaLX5}$1tTeT7MMEk4f?gVnHEp#GtlE!sp0~dKTjjV*>)|(ADNuFGs0VHEq zN#cBsY1S;E4rwZN_vTzDvV@-2fo!hg-Hv@{RLRId87g7Z} z;|aOaxjQwq8qF9~AbH>G_54!qHx?@9#jz}O4<}sx&ndgHZ(yQ;xnC#LD4J4WgW& z8e>gSkR5k+fv+VMWHeOqmjDW8fI|z0bp)opQbuyuV_Crne2ivWGee9cym_Sl##5_> z%cTmKv<3%GK>F{G@I8@j!~f-;VL znjzoWRVrX7%PY(+rH{^Z5Qb_|k^{98A#D6?9_LXC)^+p)NenUBAF!pHGLp7jn?WG^ zvU%ziJVvRd<9Vca(D!?Xet&Pz?{`ZBG+GMUs9O`nk<^YX$hd7ADiHDNY;?zYK80=0 zH}zcuCOLr7;YG!!{0Q}=EjdZ~EXq|L*gy$N1qmpoR8C(SWLyX{Lw$0l^Z;3W+uUNv z$~uT>+i7Rg97WW^K(T96&|A^nd2DsJ1+Z8=(inzTAv&~h7>&5Jq?mh< z3p;Yjl3ceXFR>=OWVLqabD5Vd$x!~twl~);&H)Q{2ZQ~v+dHD;{-7Hk9Z}XBj1Twr zy5rHoc<(6a_PhK0d%JtR-R^iW+V38XX*k*&?eC5cjz+q2m(6*Z)tL|tE~Q1Xf|eRZ z3C$LAUPVa|&%+vCv9r&&F=StD^Oz?X{JZFg-@-jm#tJ!2ASDcHv4G2{hKEEVd72kl zxVOoO1#~W0gVGcUk~7F5sx({71uC1Nl{P@=G?k9#QH&^o{)TyIz|nOE+hP|7H+V_T zO8=>lZ8Z~SK2^`5L`5%^?_Sjemq*KfaRZA__3^U5xBoh_-q`2a5``k%z)hmm#AN{l z&K@o|UU#JH0BhG3M`IQ&0_*f(^0b!d>3d&Y+$N+Q!qyq3^3{FQP`L-oJzUl9o>iWg zug>jv8ee(!!EEIk>4at8hy>A)8P7gZ-oa7Q)MDUhup*rAep^@-SokUt^pRcp{XHWEdYBIh!Hpsl};%@4-%GW zdtSgZM2=OKRitS+)0p5H3n407nL!kL3O{yRrEZVaS@v$q0My*}nhKf7wV@g2%!tK2 znV3wcNKh?-a&z@0N)%JaWY-)o9VVN~$*jC#nU7xbjC}XQX(^cko;?d=3xR7kmn z*oI1SfW5~ruW_2-abPXM;G}R|t4OnW|B9Rxtcv~*TH}kE5B>koWFE)cMC4hNO#ana zxODA7A-#kinjzZeb;%QB3lB<2!?jOL3}Wj{d-dG(fPHYk(imn^PGhPoS*1N?42ew> zQD_DC^!)zde{X5FYs}eEVvpHz3RXQR)p&N%-hGXqed;cBbX=w5Y^& zg?KfRmTQ^YxokZ`wgTFsem>x!T{&Ynp0FQKg}Qxi^AK5n39_8D!(*h?XJk3WviHae z{v$0OB&+$5JgOH-TPXEoc#=H2FG-^}$>aHxGfaawWDZGg$Ue@f={Ja=1scU!!7ZU0^85Eb3B5^`SSFfT{Y!;8uah8|fT zMUSkEqemJ%m8<^AYl-3ecqqGklv}-&Nt>T?iG8BZx>XP52e+xoN4e~!T-Hh4La-hg z;EoMw#>8wu#cY@EOF&2)XL4h}gLTPRJF`?TuIN0UvLugGKvFqOtm;By(IhdO&|q;4 zf1lW%FzV{LY_@>9w7FT7O+VIRv43+R&%ZN9HY1<;+lt_p5B+6sZ0#FbwXHYMD{{9% z?ZfkDip6MbyeY_xCUY9c3*VLm(M0EULpXpL3nQpc&UzE;BgYIwebC~B9b{>A4Sd1H zHXPkc-GumGB6EfM{K=;A)6Y?0{eH%9x2s4n3SisjTGl3GqbXE`=O>IHNDcn zD&Egx;Gx3-pgvc8R zQiEQyL|y(CC9k-Y7CtkD9}$6>gPFr{6gL_SEZUy}GpgG88&#JWpDZx8*cPE-#*_cc zM@2HuDYJ4-CPmKZ5?R}|N*lDndzR+f6BjBitVb%vp=uScT+zZgM44jpY|geTJ|YE2 zvNJ0#Cqie*L~`nkHj{lZV)-3oNs}QW35}tW=;K&1-+hbd*;um&XR&RQbkaD3S-oLp zpwD=kfe=(KKqD6OJL}%FhAW2EK`LgP4f&L#{d}Ht&zY&!Z`^=zxldUEyHGh5?A#c4t7o z?byyK|2#!Z!+%-N_I zPbJ4s^;B2;n=WSiW0I;@C#m>!8um}$=NV?=0bSfO0b7jS8B1n419h?0N-8fBI04#5 zj8Y|M7q$=i{p&ePPc)gbz2yFbLBm;;JmOdYy_JXJL9EyPoFB+L68t>Omk;QV*a#%N zLL-z3#LeDC9r?BtiUA+1Hx5TSAr%0gDRos7)s&)yyh&N|x`5Q3(Y)pbXX&mED+>Dm zl&&w4ycREr{3}RtL^0U9eJ$4=-hhqGuzjIcYDkkB-rAaaEzRw*S~SotvfZc~n}SJu zAJPVcP>In;d`v=?(<%)}fja53+Vq~RW+}*G?lsfEksXwd0%eEH!$3k9ixG=2UqBejfw|G-H zP?+6UzV{*R#$zV7@3!)ovp{M!q^^R4vZNq;pF&WbJgP@YSW=|R2;5e@p*<-qxZX~u z;`Q8u=S((h^0OEiH^{TOkXspq*|`O@RqTX-*7vdNgFvrce-l77{+wccm9BQk_$`Y- zQ+GdBhcBtVq0=9`+m~?B-u31EXO%v;cZ<*2*-iC+re1EU-rc>GUCv=MP*F-7`qfYR zO%*y#$6~`yU87_C56ItN3$h6UpNtGz!j>zse}%aK&b-h=V4W(djrdaXBz0KuSgJ<@ z!x0EFDuKZSKIsFXgbi(|+)UmXLhnkh*7eW8)*%bBP#bujkat+lpK_63a-P2^pZz+!S6k9oMCFolkdr7@NiIn>&D zy%iLd8|B5wLPrzOO>A3ReiAF(5?>h`n(mL5Mwtrfj6vsA!mz7|-BC_V)+;i^$#62y zISyzmEK8FcNV6x0(~$#G)#&Hzx{&Cv8)h}2&)%BK5!Rxh7L-Mhc4r zb2sqdhUKrKWWq9&!>QM`^WSOBLOQ3YAY8b+qU6nDgCUWR2eG5+dvou5R75o@4NK9^Wlh`4q8Qh~y zX+rjTee!%EPj5>9I(>0|)m7YXM?iWMr=ZmOyF0>HD+vf@R+;FTG>3*R`i43Bl=4K?6l$%phG! znz7pm2bczVbZgHAUEoEO+~3(iKZv zpRrs6s<^5~JZxFFSLawk-MnC9mSrp~n0NvAO#)pqyCpaw<%pa|QjMwP{+J7#HzCa_ zYTaCphcKvGay0?9(pr_D7~^X`&*W6LU`(_dikR|DN}}uQSKD@O8o5~l<{4UauCHHd zKPru2d<|7?x$rCk6Q8)0AK(Tt2OE{X>e1pZ) zl{HiH=E_4?M3ov1Yvk`27(>@}AxX)huZV~#k3&JIf(SDpG)j~yY&N01grc0#m?smT zTpDRY@qI4OH-gK4z`>Lqru#|d>o1(-5_U(nmj;=hGrgAmBzA!ojR*M){ z8zRWlszL~*m5$_-^EcP0$7HBOQ@2a6NywG^gnp1Qxh7OO@^y#LmC3j2nFp0TpXVAULiUejsm!b)rR)g8@}!4)6)>7= zl+x2Pob*Upl~x7SBB^WJs?VO!$78U(|5Ct3O=*&_Soir{?Z6oen3U-ij#UONgLohp zIE~4Sf<*aw&gSeY`h}rq{rzjTuf{C561#TEmm7d8Po~CP6JRD2Vmi)W$|ZBn+5^yqtT+Vm#01G_H0lxPgMpootbq(i@E_S4`Ag zL6*s(9?#{`OqSP+Qi?9A?aCu3jy>G^Btf=Fb)y7FuP)9mF~Nn9s*wv= z1d4!7bE4g*P@pNZX=6y%$_Ri^^(xqZP*7Lgl(o{d4`XNZjK^r)RPC@lqQGskb~j9P zA@}WBolfln4Tcx}xabX2@Y%-isUl->?)cC)U-c#cGTFD5ZFiZ#PjUYPHR(M(0kAz56H0QOeCPLrRV*&t@b31mj9U}m9QOqo2UBQES>XEYa!5r1!6 z^by6Z=kM`-QMUh3|Ti| zdz9^tdwT~5qw(GW?d`E}(6wp1VaiB5R9VrJc6YgEKf!mkyAb<3=zIIhquResJzVMI z$uxi-7HKpdGhE=Pp?69j>25ys_sTD-{d{yZ=yV1kx)5hx!#Jw4?uG4m*{+=7&iP>@@?qfs(Cl`uOU}Ojo6Yr9FIY+o|j`?s%z%y zSc!Z+Z%3u#hWs7+*O14fVXo`I-NuPt%8%JX$PU_USv4?4KXLph{z_4#F6Ng2HlQkL zE&xS&sa3!llvPGDr4&(3o#yEH&V+sBK@dxEN~8-BW0-?!X(O#Z$Q~w-bLsEs+M;5Tv69@_Fj`H zufQDIb=xRV#vjN*yFBk8oLvWiT-3?7*Z@|lpt;9tPMmAgUZ%Y2p?vset0;YWyRFUr zY6|ve(`KtHu*7Rfb1IJ;Xc2YuD$8798OM4k8G9_MM3F74hHh_p(~XwJcr}6o&As16 zjunL{32+&+7EKIg#~YVRM#G=7C}(eyz{N0ruHgCBV%++&gm*;N(?Bfnsz0*w@#$2! z8XDRkxf|Ej;R<;%JZY2<0d;MY`l}nZfZeZB${8e980kDPtS7U7cn=fvIP5;}ihG?Aw;p^ z7-zV8LZ6KyBRRecSfzs$5E;!8=G^>_8b`{|A`{25&jr&4=A3k^_8Y6bqrjNcozDbd(??1Q5rra4#0a!X) z)u-BMYQEaqBERA;{|}wtO3Xp+9na%R}J=26J}4LL?E7OXx{ z)A?u=Gi#J8FESR>MKK(DH1wcXO3$N$(yxVf<{rBuo#}@UB#-i#5q~(PS?)vDxLF&e!3A+{#D-4|7w#TD77Wb?|5yBqd_y?b7BxAS+Zi}R_A3o^Mbf#GO<;F{%5t=N>c%DIE#FWM9 zHo1*Nl$!&LJhE6$`z+bH{qTWo3O19PZJFwp|C;`rXOehZnJ9rWI}D zZU28O+b++ic^W@b9ZUBAy@PIb|KIEG?|t3>KgaLGhn;UXhG4G8ti~9N08o}{UDD!| zsC+Cl<$B<6e7mEhYRJY>!bnFgODNPKo_d>RQId~I=g-3Xv*?uMMMVgF)Cq4$=YMxd z=WU04l*gVVAp5)U&uDfvACIH=q~mprNLR838gmI*n$edLU6L@mLY@F8(Jf2V>28}a ze{#I>?%lf$b01{i_Ed8x>T?#{?cDbKUf1t!xb9r^9S0jU%C|&?E5;az~Dzkxk$5PCgnp4Dj>Xl3mR* zNSL^(2k-yD(Bb!6q*Kt8o2dyQtPgp|@fNn3deP%Q3n*WV1Mzb@F*vKqSGOp^2AEMA%87B{~P(MlkDHNHsB0=yzwo0 zSNh`Jv9g&oam@S)c~?`iYpB8RBxPsa?~dD>yp2_KF!oe~%X+$O=iLUe(rX(F4@A9H z+9F@ROQQIl6d@a=;JYOq`ZKw3-~$Vxx-i3$C~p=tl`%2X9HMKFpOGjD*?Z!XZt<$z zxmKB8QO4O#cG4MSVkKiUgJh42@H_my^ zj>$=UM;D^V-mFyxxi2V(tz)BSfAbbQsl0Fk>I;_7(+=^m=Ibf55yf*(-hK1-9VxJG zLX#l%j=?1cD?xOIWO#;|+9w!U^bW0{!;^O#vRlocR{%{2tHaK~(!6gOPEE%~W3USQ znxyJ;-Ro2~SP4V(RG4bvtuhq`v{Ed{V2``nxe2xseB-lPvF#mA$p{9+v|`H_H2jvm#EwO##gx1eCt?e zH(4zsxVP%aHcM@xT2?i?TK0x5>Sb4Ko%_HE6b#&{+!|eDQUy9CA4!ztTNZ7bmkE<| zMH}RYCmpMX3O7k;AI$agEH5d+AoxEgKI!P0*pXFj6@M3#y;B;4LP#`=fgu=IqOk?` zDoDZ;3^@e3zpZr=U1XcES(+<^X<5!8rl(Zs{xa&?HwnL!ZIb^fglNL>#U}jkM*w?$ z%|p2zZsz<7@_7*-55V#hzoPxs6hUQQVMUW53%O~=t$H6?4eDZE_5uOb?ha_Jqm7zS zt)WseGL3$#oBR>rz+bqVS8tuxl)9p!wGYz&Yv(^DtI$NQGy!n?T+rD)4}{CkfBpUa z^7*gd-Q62}J^y`0a4(#`j}>REz>N^ z^$7%`?aO4OZO;EeXEE`~N0LMsGdmz9`3Zl-R^}U;rWL284PaT{=+g&JO_ zY3M-vs&p9oUzYlAu9MqX-H0gNHnTMrXc{TBO`eEG9s(HlvEVm2!lFr|43~CcTvb# za?2O3CF-J27h;o)2{EBLyQ2%&`;tJZJd2G0(l4BhL?+<7)R6&=b=GJ{#my{hsb>G} zXnhoB0M%8hv?MVcv{N1yquRXO)}J@dK5%ueLuM-x)LJP>SPerI{lXfe<^f00Ft`%4 z79&dVt>1dPp%y(QH1)zRzClYA^^j=&oOYx`LEaA>s8Kwz5RpTujjwd@w@l=ProXM0hxPw6#wKw z$Ghh^<-V9x9*>#I+M?xjON%!u>001CTWO#27>1)G7QQO=wkiMN!?N*!JJ65h=Q+=n z>C?iWke}DhxQ}RM`uh95{@Ue#NZE`hg5?je{wTyzx#o)^aV>Z7SNo97a1QhxN8Me z8_%k!TF;avq~{-!J1U5R34q*VDleN=RWv5(`ZbiyWQ=tF_N?b0cF3l_81KNJy1~i= zb7-@^W#6{8vHlF5C|ox!9{EOYA{Pkx2aYaq1A6?7YzCahOa#m@;Vjt!+8tzO!}Gk- z220aa6np6lSiV;*Z`(pw*#|!7Mq8v0FCrV3Z@%tQc2g}pmWoO4rZk8uf>qtXRlqKk zXM~R1wc#ZkD`|35_?2$5pIx2XkrjC8c+nwSN~SWvy4Po2jJ2+q6+^w2*cB7IGykuPmoLi)Kq!mEP1ERgIjK}etd5%-`f``H``-HFrA^{+#Ko(Wt-mv&) zG*x3$AORGP$lA?A7xf@wLCj|$S+!FP`qPnb{A)El{&OnW10-BB%ATnG)*bI z(<9=KSWc_ZO4h!0Tx!<6DaMQ@o{n710i(l7x3XlasV1t?68B&QGC+7LiOZujj;3k6 z@GxP7S72)DN)S&?#w$>1rwyLJhY7K42UD%~SIYz#H7hG>2 zT)8{z)!d^ewKm|a1iYdz=5dTZQ&wHoH^*!6P-~t#xBPG@j5mGE?{vNa6sRQR$!*!yDfH?#mbsR*cy(J*>ciY|XsM{l~NcBR`q zr5Ji)ls((YXQ_z_!N)V2Hp^8=`+Sm3+T?2GP|?0o5uNp+)>qR!PrqXoq>CvRd3j={ zw*#X{6!9jVUjG157doVKeBA4}CcONm7~Vf+G|s2LlwZNQ!m0}kUEqnLJja45lPmN4 z>+3RJmTbiLjE2@K*5iO{xiiWlHm<^PG~rIlv-~_`*VBxNDUZXl(cbDvzgn`)aSEAM zt}uy7Gm%oUG6faQX>roC-YPp9B%v_1>@8n$N|f-dL3L2Ju~x%biOrQ4Tt$JwhvpjD ze&~5HWEHNftMlvpbj%5}6a8-?QGn9D%*@*QXnmE_<2rS-GF#7^3dD?Vo)GaoelB zgpUx5N1n<2goo^$&RC6^;n}ORA78%(o=#q$UR|6FKLbzNF6tZ8cN1g7S3h1|pI*K` zz54#`@Xh(lv+t_h9h03~n(f5V$Y^Fo&fc9G-S6JKK7IY_?Dg68@@8uPu9pqX)$fm0 zic=r6kXA~C(QPDnc1)i3o;Du&4~23w_@0Z>;xx17$LFV)=cm`FS8uOQFaLfvJbnB9 zo2yzQN)x+6buANSokmq`v~YIu=5kfefa>a;)z$0M#7YdiF|!$VwH-cF$)$! z?Ao~!R{A=a(kO|N$!ji;nDkC~K@xQ;;*X2`N-C1&`BmS!8ul(Y0m)NgRS0{e;|2AH zA1hB(r=>C3^j}6X6aIHBCmk@kD%>u~BIvpvOClfY+A1B*``vPz+;To+S2<4~(9E46 ziM)BD#>BS{!mG@eBCC#7s^r93>X61+)a$M`jVvw3aidego>AGvU1nZL&Q{+@QdXG# zHtfz*mXM3u_K!8m@6_t^bDB+9Ue^eun(~nE(~Rdl;PElJ9$qvpFGr_1`GS8~wZn>+C-92=*#U=I`I6 zwQMU6XX$T*M>M#(=CAlfyh$JdwE6Cq$MYG3tH!!>g7H9FA#a;w zfmz9!JS#sR{@_*;&jlYokTA+ts;vT)TJAh~qI;9Xi)sqBj`Nc~J=0=Aw?L%1^dt28SrDKO<03w63pNj*C?zqc&l1DS<+Zil7K^F1O zmQ%$TrDJbK)28_?duA+lMrqXsc8@&<>1I5o6Xj(0Ji=JNh$l2&y5%&_m4Lz>FU!?z zNsCIrp{}8tCx{3dNdkz417WX71Jd=UV=~sMYm;h5MwKJnq9&|;ROMzwLUp!)rkq?l zac33L9hgHeD!;W8<$?d24;>`j)3aILY$`qk>DWRIy-05RovOo%WdD-$yy8EEU?*dq zk#L^Im=9Pj0hRh2{wk2LYZtgkzj5PeV8fv_IVQZSBAX{GiS+uekO%T$FQr(xoWmpn z_J%wezlgHex=`D;lm}t!u9OF%zNk!TK5c159F2CY;?Di3r0mrXv%rlIarprzJZ)yL zh_hbGTD@osZ_71fGs=Zwa7AwF7R8nOtFZ6Crs8MN5ZgvDO1&8sKi4u?+FY?b*jZ=P zmK(Q_+H0CRyDN#jrpyq{Gh2S~GK!gVTCc+{Q;bXd~J%mnQx}{fJHHy zq;0EoG)dPGvr2l5Ch0?1vnNaiIJ@#Vk1eCpl!_nNqEdu(Fy(_vli{99${do%D@w;a z7CZROy_%`3&1Vgp`;Pra63A#WVdYx@sqe_rcz$(}moq1+z~j}XmUCj;jgf`)Uv-PO zoJdt&Y?-=SRNM5;L#$uyeaf@k4;KlYMZpS2uyM@qVm@hIggcFe)|=e*%bdSkMu-P{ z^;UXhRt}LU%2h^n8Es*T0aElm9MRoBm0>tC}rC zbx!YXAF{ZRf06(7zsM+K^oD%=sGJ9T-M^G6g*;(@DN$El9gZfX+TFzh{{}Wa3xv~* z;4Mq?>qW{$>7XpXB^}Zszy9iZ^4n)0J}4sXOXqP-W)D46e1?_bs6_YeXSyR>wpgLf zMY7RyIbCZ**@B7vmI^D`Osle>EZKfnGN4#I)zdT_v-7tXC)eM%(@7$K6eTVbQfYeDX2EB3;d1C*HS_W*%}oq-B!ryWJb@ghG_vEY!${;A7giRr`kIg<>XRW)G2xJ67c=Ks zq#)BD-kb4kp)!o#F#{~2^7sWwMACRZi4r0>GRoewfTU6~2%%7FHBp*b@!YsipbnS7lJ)9SJ9DE>hhJ9T&XSyTzWJbM^{=IB zI^-K`<=B;GGdE)^ZaA8UhFz!j+BVs@7$y}*4?g&Qbu}tD`)sa8Fpxu>tiPSB(aI-y z1O*sJ@0$&74d?;CgH5tFGEoxcm!E6zace3rH;5b9Sjk=_`Q{(IiAR&=aeK2^5tv+3)qzgRRTQH~j<&s1 zeR<>r_p+2_W1h`ax;H9{{P^f+MqEB_CSjY~o6P0m_Jr^xUXVMU-6$6}r}Lcq3_D84 z-uUbGT)kI&ie3MaOfr^|j^!Rn;PPpdjyvSzM>RrT4l_djzg6;S+g`oSrQauzl?_7g zAzrPY(WkY22fL4_>#8oRD_?rq-H`B*$=O`<@h~q&oaZ9XX!Y6&Aq_61Pgg*K9SaD?GvnchJDnn>e8YHsBqJ)=)|S<~H4GjW zOgZWNM5A0O#AYY~Zfc(RzF$)1G;B|=5w+)&UU8cidUCycVU)R-Q&YD&K|4$J)G+jZ z2XopbsO1=W1%{qA%iLyHyp}q7_3G`_^~v=LS5n4d^9QzY+)R}nrmHs#@^`})t^J8V zo}w&kaD!BQKfieS8}cnh_dR(3>GJIQ^zHEc4DbZr{`CFn`P<9WlNbN^T#W#Y*wfCn z-rbZX9&Dbgwd3o};qU9_@ay693p$*F0Kxc~tDM%sbbgC-X2Ue|7J zDbx?brOfRqyY#AzXP4~w>?L#ZzFI*xp<^x%cz{d>IgLjT4`C< z@`|91#w1ErfKD>bJwb07@p3Yw$plVbV4v|$$WF%RfAzz2x&GBy~cBXQW2Uutu^urfq)O!tB#3Y7Zgo zeUy`KhV1blNt*>#nazj@gSF7qr8PCUR%6j8s)q3v% zDO)tgJR^lzh0PmN*Ok~1bt{L{&haRgjzZdzCoQty5kgtR2YaH^#DID-JU_#$NR?BW zCgjZaJST#aJEj21F|`4l5FywRbQ{!wBE0Xk!$VEzC{_nYN?q0OV6SpY{?3l(7B5$Q z8?~(N{*^?Z&*SUsfvl-#);^^zKL$OnJ+k#?xqi32!_#~xNTQwMlDV}09m`u4%NU^G zXX8%rN~H}eirG&ex4xXmEUPAru^u+o){G&#%4ycZj!>ae71K*o;kjTZ7iV&Uvdme4 zrjdZR{>`n(dB)(WCT`nk}U}-OY_BxC_5$}ezoep=8%yO9PpT_PwhK` z>=2bEQ&dPh3`|oM6JI)~SM6LX+E?yPHUu@@>d7zQ{(p@wg@ef` z9TUrn*=XEs?p?Rn!1B&?NfHZnr!gXC1&>?kgk>QzaN6J*y5*9pFJPn3UrR?+d;=?8 z(A4yGN37l=_u8mfepgx^6*KS}%L7``>{J>hRiL^?6}Fkt6%_iV zt#cJz&^>ux2@iL9Rsa+o1gX5%-9T97&TsZ*4f)=F)M2yO18PTO`|Mi1>gT9-7%leWdCuC%Dz0##e* z=O_1!Tkfs2QUew5K57)9ys)wM#Mnl)0=7e>8v^^)-jN{ zunAXzdQ3C^Zx-aNWSahMd&gnT_8fyvYD>`xOxf7q)yBfof#4;{G<%=MQ4r-2^CfZ? zk}1pBHW5)0;H+m!4)+*3tuu01BaUHTdhbjbpz*4~V&S$rBGa7Q@p&9#)_X#=elMv@ zRTMN0Q6o-s!fsi%AT$gk*~T5{Y#RII6{ELu++?i)!-0ovGNU(45JrQk;usoI^|yPX zVJ7D}pPA@4Gg(NKU@V!k7!WE5g);}an{vU>@)zOR_l7O})yLABtH!3|$yVje3s_d| z;MSq7NkdXd#5@-M4Xd_WOT>WBW;{XZ5yTNoaz|l{Qcz>8g;KfjhdP`&P^8c2NfNi&wHn+YHSG!`#GnhBh42jy43nrfG-#`m1}4n+Vqt2_H1VP1W46 zMZK1_J8X%9W+C0Wo;9B=+}O7*ibxY!ZezbS=?IPLfvbj9`ryHct}EfWwyObc0Ix;4 zc7#`}+)`_I^*CPv6kZM|+%nP*Ye9wlGOnD6uFLRTTR18{S;D}&$r`++c@dUa+8mh{ z+!i-?O_ekTt5jA?kFcVJ&#`bmA+l`?uI(Q|6;#bqYJ=1ls!G#?6o%%FR$V^T3#_DW z=&P$zflsR#d{qWkPzKgf0NUj4iU7o`!4I#tL%6Hd_m`T)d99aMKbx+BJl%#n-EL>n zZ|g2NvU~O0>k6)|7QQxxdYRRRvB~4S0f%)d;;Qw}KYjga*mO2aW12IeBO)=?W=O?! z#Ay}^wFqW3^%ry&uT@94+wJZT2Jqi*w_E*hzq`Npr{3UTf3H92AMAGj)b01Xd;5PP z-A~qP>o=F@{6BRcTvxSmU&yZ_D2oBNrK2qL4cusgUxv!d8uO7(%-S$%cE0uhEqKz= zG?aselrGyy5Oh!}s*a!O8gzYz49LjiXCz}2_FjcdoWwEdZ2mw0+nudV|J$wq{%@%0 zn+_5uoo_z$e}!s2&)cX#q~1hHcua;`1aD|$R4D+Bn5(&=@yLj~By+KZQB)HiB- zMPcA}k)cV#bLDJh-rEm>hV|fhqqO-fqq%G!8aR1&Rq$}C)*pIK*Jtyuzru1+Q?*F5 zjEm^t`LFYQPYb~2X%US$7hd%in8ckBQ4_w4FsbBF_-+#Wjq^|O0)m|#=UOJ&x z_41Nmu-+92~vjyo+2T?0S{X{`}kaqa8$X){;*$ zZU6Y^(8rtB)wBEWYi55TC*n^ig8u`~#g8|ctDRL$=EkXf|B3wj6wnVkn}5Q2{2y~d zf4tf3?6UvBDg7v>f=@WLe~$)($D4)Ct@|BK#ozky_6WcB^S|o12VMa$Isfl=2i5!E zgTY|;>-qn4{2nY!(~QqpK4tTo$C%|-W))IX>a|MoZ++u*(^6}@Sq%POtpuOgYfGdo zkQJ$Ty|asBa*oK8=y^NT&o>AWB?zlJV=2${$xCHeVtEz?=(#UnmSm|lmZ^oMpJqJg z0gsQ#_3#3oRg;HAv7Ml^@5kW@fG(*s8M}bEkqei9VOI~Kb$C>@r;qO1|x03!x zQE$Gl8n{IN>(%tXey`j6>i_>)exLX-y=1r2M>eMm{wF!Sxxi4G@MA62@E%^{kLT0v z#9Lm<@`L@yzsmprfZs~;f2Iy=pGN-g?(fy*|NdA1@6Yo46zBi7PW@H$sG4h0GVueR zF)sXo&vvRCha$B0X=f>Ct|+Zlg0xnTvnt8)W=4>>Eu+_x;pH8!xtibeCbt^b58mhW z8LxbPo6$U&zOqKxb)+-1<*NQIOcwmxYtu(q5~e&#^wAKHfcBv6N~^8T;RS^r{Gy)qkzHV$H|a_%1v^Rz?!b}^0eu0hfwD(CRzs)EG0ij39N z5XGxZ&mS_l&Km6xSP~#jY;2|64{Sk7<-QsxALrL7{~xUN6~9*b-y0nC_Xkz^zrX+0 z|L3#(Ed6h@Bt5rQi2(Y5X_v?gWhuLb!g$Ub(ctEeW}zSfpQSX9AU)$k=UzgLC;aYt zI(|*l7l@H}75&04X&!MY&bofrU0VD+XBiCv7^*X-v0xon!C}mUn~RK(mKD6l?A|Sf z&Mr<~!+W(oc`4^{z1W+SWi;p6NgBaccoK$?&8!pifW}iU^5dia?!e7$ejTK*>3dmT zE{4NHcd3(Pp-T1l`@O-SD20P_Cbh^YU%aN-4b%-@8bzsbMkh0tQA z!I7)n*-P}7dB%Pe>>`V1G+QX>(>BV7VhH$-|8;Q%4Q?*+e4YuYwO5}FFV3%kzf)-W zbw(4B(u^elXj7utPSrwYUR+>UE=%Lk;aEu%(iZt*``O1$6s+O<$O0Q0nGdL2DVb6oeV>oZvNc zSd`uls`!~pSHUyV>Gco%uHW^0)*`S#PrWrr{qy*^_pdq`+rr~oTF*$gp^x;ka+h!} zglD9a%3dvIx2%8pOgp;wOgk#iw4?QB+M_n4*S&X3dfjqULL2lp}_z9HJaJ)N_?5kk8>3^q zl+u~pFE;~DVDUi+35Qg!(kc|3CR(b5Lw;Md z&|(MQ$k!f7ugISlglGO&1COWIVV)E z|0qN$KsIHq+XN_Y7kj~G$kL1jG-u(~Hc?HiJmnd|W|f|A$vOg_hCP!Z<=(63?j>Q~8XFTAI%~S3f@Fdu8cu1HM9IXc#|cj?nII=Tpt9aX zNq8nmCrY4$3sDHEFDxQkB_*R;!?VWkNU=PW^ZXhWU&EE%vZ^~1mar@eyjzw}8|0pQ zFX$h5QXLu6l!D}qnDFHSRCY68{nsUX?PHBOu{4yygUvJ2@79>R7jSVkDciOaMK|PN?#XmlACG z8$?%vIqw{P0Oef$*f;7MkEi)DdHSJa<@4(tlg`d9%^-=Zl?8CeF0)PW6U}r-2Uh(0 zbfcOQ;-PgFBo)p94s9gt9EC(>wiZP?N8_dzI7>(5J@3|CZcmNJGX- zM`FWrt)={6O-scWx506L_u$A*Dp1PR@u8lU0T+_)JBtBXOZm&TTnKfau9Z?a=9wI$ z&?@T-7j`RbpU%G^E=RnZjiB*`-x+9HUqQ2uhM#7|M2Ebu$XH0#4Yg;`XPUMI-Hj$An%B|AGl zQ1jFqcqVKvqJ$K_oP-K8ynG?ZW-u2ypBdykd2)kuCQr7IB9V0q1{>Olk)P)*i_}JT ztO^jI9ON;Xuw1n*3N=5PK{ro&?^30hrg6zL+XB^j}p-}yRwv@v#!*jVnLJ%68w zd7APp2a8C&z&w#iR#6+#;D#lkPkuD%?K3`;1?AQrhb+_i*$sIy#T1%KUB=whikww} z9Ke9jq+&0}kk4~?tJ=EoimBqkRDs59;}!K2NH2$jAHe~jG^kB9$~a7_(1|Gn>LRQK zLtx5imX8?Cy*!#RKF?QOv3d_T)qJlhT_d5g7oVx{9x|&|m0Na^0@(w%p$FBai8R#( zYxF@iND6N1ZUJY$Bpq=RrNi@!tDNR@ah207pQpo^(L{+l`Q8hX)Duv`6IjFxWSMgE zbHh#}P4ern9hdmE?)r_$#6BBdz5qW>tx>1WAV^%8f)@kh>_33Fg=>ru^@((l9?2;IA zbn2?hp?T!i+x#((pkoek+*Hmp!rtRQ!C{?+1*j_udnMpaQ`s$z=Q>@cihi&qtBP4@ z(+BXVpwpCR^Tbq=kdTe$6I2hd<1oD;ns#!oU7VpGO%yD(Oz`%++w=5fR!L%AA zAA0IpSr#CD^{Q#vCyZ=Hw$~A(aX!7^S-wS%4iAp>2wX9oP1nN)ZZRDquUKCcx!4+bsDK<^1b=6vtE0O zhCmr@F>o@k?;ji4uQ9V~D^eau!QwO-^9(JEa_8^Z1`cFeDRAjLFEVZ8=@n&B#^fkS zT7(tEVCtd_Qih&wLgPES5FmUKcFQtU6!qaVqcEAZI-_bc*cd(Y7D9JOv z^929$tU5hiC(eEkOJH}!{Ek^MK(4m0*KxzM7niT4sIa+jTX=QidD*%C@SW`T=qqH*{2N_N*%^r?%QZV*2`(E)JrrM$;YMAxLv^> zkxyBa6;-`YxnQARm{IWPL5vX@_s~w!0?dx;(J;(9Y8Qg9mCK%v7f}X}j%DzZrOw}6 zpW;3V!V&o5DNBl`@PwSadZl)`yD7^|TT&jvkqB*YQsU2J5|s%CAaAB1nj8eACCSYMX%G3n4a?tolUHvn*i)~7-rjTfpa532H3>!!Artmm@zb-9zC zBKdGk_f-r~_D(8zEe zDYZO~8?+7ddn9KUw*x}a<0nccaGkCu%r+cfi`!$VqT2>d_!e!R_`Pq5;`DYP^F_@H|LbWM36 z7%`GDynRGlHn~aoUChD>!=o$(0%;a;V}!`&$u_T@U6J(x( zx%COuL9<+$(P%8c&Z5bLWr9FQc^W%A9ec)7RPwhzKc_ z>`vPzQ+~&886GicvLHN}a5*voPZAd3<*=FoVI;D7DrfO1i`W?5fS!<7MhM97LKZ}L z%c&2F851mrF|_Ezmv$Yu5yc4$-a@KIOTL8Y#27~7G0XH+)AEmwbC#KLuMVae=C%Q? zSiXZKA|1TPmI|*ief}uqEV`R=au*4(B%q7&LY=+%T|(6L_wf9z;{GKE4_!0`!v=Ex3;>6CV6 z*{^|o=RrtB8A87lOH-Be%8!x#+z@-CJldC4=Ki>S*InW}IZDTV4k~};cqQw{7u9yE z@~bsch0irK(5j&Rjk{e{d85RPLI;CIGnr)nKYQ=~+%~QxjGmwKSKul+HSts^%Z?v8 z-kYlHIG!1QlQ>@6v*-Kn?%pg!LK2QCf&+kdG|Bw$x9}nX5`4+B;!MP@;7OvTpGc6xk~;tc;go?0aYW}hL0b8AeR83{ z$los{%gxpD6HFNgWGra7gz^F?&mz-+J*vhdNg5tQtw??NF_hTXt*f78*W|5Ah0n`k z9=}6#x2OF~)cTS$0iKVBqhMz^`fqvjO*o8!;@n`4=7cT;=3;on!ip4JB|qUmq`A)| zzb!nuegP7a1m^Ga<7;{P)HiEJN)VkERAdg*Km!aI{sRRPpK7l?-Kn8r32O7d91Ta` z+E}#o=-zB(Mi_sGrZ8Mwl8obITH*kXKykk);(HLw0s&PeDsxnU(KJJ4yMBFNVVFb$ zgivD~1LPg7BH7B=Lw-OJNaJjZK zAP#h|-n@|W6(bHX2NNOY3Na4o+ha8#c~yaB%aFAT>0%@k5~24{`c>%x z5pU%Bn8Q!GqnKK>n$dwIn~xC{%MT@-im&F*UQ`wf&8#yUSY;Niw!4|iib^o!GZ$ge zj(FP}X>GwH>I$4S$~LG9%2w<3hjgVdcf8shw=@gykO$M4jA0!7*a>y(XrPu?ZiWd% zN*Hqw*)IS~W6Z&LVa4$0VJgs*t9uAOBnYGsEB;O<>R6RAg#bh5!cWYvR5Vx!s09lX zwNC^P13&^3bjt_hz=lzC`#jnI&dp-Ew4aHnrcHZxg`mV0^bQwibRaVag5naTiKKmPzFjx_kR+E zL>{7)-=>sD#pzaea%Z?J4X%{)=L!|9S_x} z;RNic&?7^$h@BNI7p=Fd;u?0@#;sA^3NBQbEJ{#y*;cKXkGw%%+HncYTe3G?)QMI| zD1wd)_9`YA<%G@w>z*(jL{gRGgoKB3v zk0O-=yD!?_X9-$u$eP-lO0Jp16g*E6%|CAGPPxrgyJX|nqg(OV)1`R4Om_slcx+TV zFI>fqRNhGMfQ2Ks6-`yUWN_=%om1@`mnWhb6nior2gY|U&?a*@t*}H7j3x`t3yLi3 zdd)HNagm2e22w-=MSVdGIZDEXJ{M6GWua6ibA5`IPQ55m{(@N=Oem4&1X6sq8Zr_} ziTTUH73V$M>`t4EO2+|pM$c)j2CS^DH4IY=Cyc{5mhJtU;elE$Caq&o!mvENagz5- zG6Nw_Mu_IF2}b>?WVnJ$*al)MOaZ4vj)=e|XBB(H;_sJoF`1wNc$yd&4Q{~_MaD>( zFpH&2vgu5Sq1IWlgvr^&Hm=32Kd&}=SR;9$PngNhTiRN|Y5Q_>WdK#-=*x2R+(y>o z=&jt`jE){&YZ!G11wA}457VHSy_l|o%L7g%w4LFecq>*6)x(RqYF8=}NP?u7i6say zj})Nn^pLr93G_LTd5B|C>b;N$1wqkD_-PhHDz==w;*r-NJe;XXQGq661kRW^DT*Z> zLJGvR5@j!7L?JHDR21O3*hTUd@d+^H1FL)>OZZHfbm24c{Sr;lrvgcN;i`|uxsR#T zo=_B{TbOXAJQ-&=mU0#ZbHrv+yjI1TZADzU?=>*Z&3B!uTssB~!~xi-$rJp0hQQg| z3V!c(gn~?!u%vmp)`Wy_kP}N*-$-dyLZUoBcI0B--QT}YcX8ykl^)`#q8e8he<4g+ z7F(`)(k7eC=UHM@g+`Z{vTacU2EZ12XTjF_2al$8HaA`p>p(XSNS9R5hTgbdt zitVkc<)X$$TXC4SZTltdu_FqilHr%ua^&$LZrFH1!UQin20Tj=6pJy?e=Rizs;(M? z3QC7TwNmVSkXs*{^Usc7p6L&#MVot7}Fz{+Y*sLi?xB)oRtWA1DJtr z(_d=+{q=9Ep*hc-euxj>fuj z<%qt+bIkt(!{duH)w(JWpl~;Usz?xvGQll|j-5~a2~xBJ@0gB0aQaD{`p+&*8kRd2 zQ1vB1@>76QuPLb?>Z!KkLlgYzEsCwXCI#ELkjMf-l$Y?e0D$afiu1uPnN;E-3U`k$ z&T>b_lD!}fc=^skO((vFQwF{OPA=iy|6(L5FNRXbST;@&KYPo5!m;4jYVpdK0ghNO zk*_Nam87`|kM!68wDqU@ur)uD0*Nh}z_j5>ZN*lfs^0?+QQO2V46gRQz8rsw6 zeh)$RPN^Qhd{%8o{|geGoV~r2f+#ZAgKC+7l-G-rVoqQvbva6jlMt>=0DsT0407_% z8McETwcdCF)!IQQj+2(@X)w=2rut^< z;&#u9sp@ql5J1Lw8t^c!f$-++=j)S;y4nZLwGY~Aw{_sa-+>-C=|u}3qpX_d-Fn@O zvn)hw475s(`Rmqk5#!mEmkc;jOC~V%x-xzRSaA+TS1O=RRnV)D32CZd)IKwN#Y8wz z1#D#U?NpKzY9aXrFUKt+74?ptBqP>4zo$cE<4%2U?sfsDocyro=-;5lrS{P>CWYnIL5=%YqziPlNbJ;D6Z$36 zUAwREFh$c0(#Rdt{B@80-8^PFM0ui+u0KD)IF<%Y-j;*BTrklE>yT-Jz3dzpFbk)R zUlB@SjBhox0@b2OaAV4}BkKd)I;XYCMDqrOe07@tbT?~K)_B&}Tf#rh0;U2V3(>Xa zq-IJ6s$^9c+YHR*D}Hb?f=CCLqF6V8@-8}{Xihj%a%>S!uR}w!`lYOWP9k$+z^Ea< zkshy4JWvst>-K^u^}nyLWEtPuFO1Nhdo>eV#d}KyYQ42?E_4WP|EY7!QW(WT*bj<` zl{l4FCM=*?ERW~5^j|R7E>#_8OIiM$oS4#Qy6~kaTN4hZMjABjT*4$G^PjRfHV6Fz z1NkYKh;Jnqv`XEcj*SMwI}+aL*nZ{KrDKO3K(O4pRkwawNqtC!DGIrD9&^W4lzA(Q zh{Y;}YStbJ3k=3EiSBU3XYXa;SjR&%KXa7Ic!@+_C{Q72-Mw53!gu@{k6S8_$Fzfw zWvLf4Je^&IP@LUD7|#5pP8155C>7vT7IQGEh0)g~HiYpdQd4>%HBT-_S7&dkOXNYF z9Ou&th>nUmaj-s@x0Lz{P--Ley1m^C)D5PwL#~qL+WO6H9@Mva(4|d({SJd&PIgt6 zM{oB6QzS6PDo*2-Y*|g}rAlG1a;c_KkAkHg@QP7gVU;S7w^ ziPW?W33!g@X)M*fOxcB!2wf24ck19Q$3XdEXq^F0rdu*_plrn-^30&n^+Hqh{xVAh z&v+}VzC~PFDmkM`Rgpk}4$Sk5z^f#uX`TY@$ zaS}QA47v3}F>1bcRJ6Z8-ryvXDZn&}{sAo#W>j=Z#V4(Fj$D&T@mYi4ryTO3poi~>}_5#O)v;_ z7|AB1S1>U4tY;T&4V}rYXK^X+j)lg(yOd~>vxfdi;juC~27zA7yj&R!q#2uD;dw)W zd@GxqB3@r>xh9qLqeTKmF7uf7QgAW?g(8#wUVDHniH;gSMW^fl`(|sXaHcY_zMP7- znXn-Fsom!m<2hf$PHYpLdhOA=_wbz+10}aT@6>FkZoA<9gJ7>V$y-uNa&G8rMHITo z%Xm*9^2DX#1ir;w0V--}pyKzXwCU`zjOyd$6cWD`ZiaD~L@*|a#yf%>rjVQ9tMOQJ zl=j7I0|kS55!Tf>E0h&I4iJWF*3Sb2LbC#0}fWH zd!P|9`WCYk@^I#25iuG>`b#|)R`q)zp-uR%NlIceEgeRUAt2XZ>yaseX%c!Hk(PW8eALT_UKL=c4hyQR_sQNn0>o$_knuceP&8|>cGSuygj{!p{@-=H}b|TnJ z+29`=Pa${I<+s4N5jl;OD|Rv&W4EEYXO+ZBu@$Gq+fZNFDnjQ;0pkPFQ}wZXj_`E$ z7PFAtBDxqfEYVWYvb=6|CA-=KE;0q(1zLjABIN)8X(~hpiY64XS18gQ8pA z+rbhTZ%<2LDqSwY=+XC>Os{142O+np#$qxJR7eOZ0+nc|UA)sCj>+_cb`Z+J%S>=C zSX1T;;H1I)(ZBC*UNJDxmPTh6w|m-P$(-(Au|R)RdaRU0RfXNnD>kfxxDOf!jc6Qn zLF1qk8ig)K*(a*RFJ-}6+b*g!4YEyRnFuu8sUW%Xq&wdGb%Mr z+rVT@97^YM={`P}=@0efW()xcbo|Eu(jL1S=zk3cbsf>@G>(C5@u8~gI|cviZWf-- zC78E*XRK~sv2(=l2))tD@463O%*5(RR4I>cy%t2LbtB`EnUMXeRf=F_MK3D{FC)q1 zCmf^qH5MoiK(Rw=+ALzA89k^9DRKmEcZNGJwUek!)1%W;yVZ_V5JYKl>a_U;02!Pu z??P}lLrEaB>{NO)Ba91ZZ!vW`G}_m;yb^AV8Ly~GO4cj_nrP!B!8)>A>rj@8`EQpSK-R_!qEIsW)_t1gA{W2&{(R|N@t%}D7B@Q!ATKieG5D{ z+R9op=&hRX$_Akv?_DzuTYXdsh!itaGOgBmhD`An%6-uZ3uPs6kh#mWk-H4xmY_~3 z)S7`;RPN4b)houzYr?J98l=cnyF+pOYeMdl!c3)_e4>&c)>v_&I1X+^ZO1~UtQhQ4 zr8+*vl~vUc$EAFLqk{;~UVZ%{hpsBqs!$_mT6 z+Y7GSL1&hY$1y7B1JS-KzsQeZdU!D%4Q7s6P7$V)Zzps(^tEoB8h<%525dQ}8C6Ob`VO z|53y)m`Ohy8(SG*TE%%bp&wNk&S4&MqMT~xMII+*Y&B6tq;S$CX_&tY^khM6EM!B0 z{8Uw%nJI8O*Xy|EjE@?P6PUsf^TPScyf7oAd&G&#cPqyx;but6sH@UJsDaet?>62| ziqZ8=-lWpPHlA-qh9Nzlr4&O)hoi&MU~8D?y3(t&cWxowZ<7g3fhpyiDWPnon#uD= zfolFJgK_TpGXz%%f&U!+w_K<27UCF|Q%E^E>!z5`vhgq^bDcYVTVb+2j>&j?4jD&u z`z;BxIZBib^tORDk1BmT@+6aLnhXFwe;(F&plHY>MTrf*8SaM^C1$$%EES3(7;@{o zWw`AW$?>-{zZ$z5qO4b8Wh`uZd8uYo9C2j73`7|+IwEqT%dk5-K(#u@c{-{qDof^u zozj9)k#()nFrfp`2qlY7z3kOJ0kvoE3Pieeu4k8T;N%9jF4 zu^ZI1cVw|4OtE0Bo$N^(yCZm&2^wNJseC1m{lOf5Qn7>QV=ph2MTes3d-zGn1MkP~ zey)KzhT%cvbODFr=sjdN zwMk`M&~kf zShTNe9y9NtnQZSow$9Y~S01wsCaSf&vO%OdxZx0->>b1^1R2v|tMY(5=i8na? zEs@%e6PeTW>@71|D|67i!D*1GGC`=)rQ*oDMtsngHXhWYXMAXWLMTF1deRhop!O_a zoKTdOj9E!?mGVsMX=Ny52^dI!o}sj_W7?8kd%CCs9*3WL410&%sR@sUQ#e1$Gn5A@ zA#ue^Nf!D189Wnzb#|11bDf;~oJ8n18QtkJ&xa&FJK;%m_$Mke@^nn{U)w}Wn11xT z?uy&(EzhVqdB1FL#1bWyxa28O4crSt;_4-^g0cN0a4HS`b#zWG8RGPIXSlQX!erS} z3J-{51}6wWO0p!9MGnD_+aR!-lCkRs64PF7zL2-1U1~@^C{{dz#=yimW7^}aF1|fp zDjl1t6A`Ly2;rlut!ZpX2qEp-8*6nO+c2F;2BF%D9ZSegc&C?y)jZT zg)T^Rc5xx$TGRXta!yMvmDj6kXdpJvz*F}%fN3f^sDJ0-F!LSFj{eRD;CU_&OMr@q z%Fl-J_W)Z=lLXDl6_>GnEzom4EiUxhxC=!u5Bm1~-+aqT>aJ4|JEuoYYsQuqv$lhSTO~+WL9WbCM zB#Bt6QeM1PSw2m;L^sk=-l9__(3wGN!Y?|#YHX~dt(*gJ4nHZwsZ$-R--mBG!9y*2 zK4=|oW^Xx4-D`L8#%V{qsI%>=PLU{G*!F4A;-x&Se%iVgR6~Lc+l^G@3E4n7 z8b(Lpf)LxlD>^O-e6G}4{eg!r?9c}~?C-%J(&(QMa}zH?2}Lvt?jYt)l!Xo_uM(_2 z?zz9dVkW5VRg6Q|FyiJFE9^&DT=qJvfzZe3KpDpBi*@PucMj*pMf8a_JwtiYSrz9} zM!PSfHV(DTka6x%{<@V|`8Tif_|AD#JzT}LWgU!T=6hFTK){7KAIggbLT;44PA8w0 zN!U_KrWDTY4AUk&XX74fVY_~GhF62w=SmEN0%A&{nF18vWo7%gRJ?Pol4qa5(kEdxqAwfJY`^PrbUv3(jhSYxJ`XN2)>@v}=tc$r6x62dKC zNAc4g&vY^rFZ&59>K;UepSUbq(AOiCM%4SEDczAx3)F67n7mn}kg@hl5A-RGafo@m z$TMDx>HtSS2<9#RmLYM+5?*B#tfpG8FZ|-8lE3ODNUfnL3z6MJp64>=waju2IZmdh z?yfs;uN1;)7+5Pp3PeNA0M3`i1~?*i-muB)!)u3C~LAsljJ zjI0$U`ekmzTo>c#Rem>H*5cf}Vkh!Tu2vcTF6}Oi;ixeQ{?!z;G&{L_fd*dHTY13C zLQM_4(FH2@-8Re06;BMwuy)r_rj>J~g1n5<=@-Se^c6ua8l1?H1TssWAM$(nNg06) zBt+R-;NUgTnc%D$4XTQfdH~v+NxXG#5xM+ar#agGQQG~=G|j4{Xw4lS%xofO_7I_< zf`hgq*0(92D_#ZuQIeWk^*mR;BDELjegIn*1ogBJmzMJw;yp&oHxU{W?ck?;A}g>l z3o^<3mF-BEaNT(WS)zR`E=Z(f7Y`|ti4#>jc>yU`=`hHCMO`CMQ{xH=M5`+6T%2iZ za4>4@nV@P?h0t(4n3vJy{hGprrI4b;_KSF$FU&ks<`i)TQi?(pp(I3#+A3v)W(~Xm zRrRGX?GC-odQM|jfc)k}9ENg9CLz1V39xX{9z}`F+V8j-v96skMDgwUmF&c6$37(P zO>K)2OFl$8vKUULh-$r;IX>m+YKMtf7V<_eZ35ShYXwS_uuJqW6mpX_>?CJ=oJ@Wf ztXKR;WtltySrFDP1V6x5#9G$#+k~Bvxk;Kia1qpVEdC6OLQU(`1-Hbo8Aeo+~l*g5z{i{9Db75U5KiHb7eJOJKX*#;wVNZd7jmugVcwu1N|gv5o@z@rX^6yoF;OvG_?pj& zj#>h7DWGZ#z&gY_9Au=Q`8na22u2Gj2mOkSGnM=nB!nv$awP}_W%F_`g!(|!bHru= z%=oNuHgQ8t&rrg3Qdqxsv4BwHXjTI9yU*9m1|yR)6nW1uVjUstiiJO{*R9QNs+_E@ z!!1&&O@B(e$|=vSG|zYj;~`6NIrUtDnkrLq0rC`47Rb^{sI+PP&6cCv*dQfJDq)Ql z3_1W*{g9QeyzZ4Aq%}G1dxz&oKm)54NjlDiGJiIv+}@E4Ub-pUhNFZw6g{Si~adT#HQwps?IfTWzj`~uk~GYqEh>!V=10Y zgh-w-9w^7+&P~WlZGNJrsMTq4?Y8S=nQJw$MC{AxCUG zzka$Ui)}R%bC^oUyk*-MjYgw`y*>HwXf!JSyE7W?{%vRP@L+#;Z};%!=x?K)!^6Xa zzk$(eyXt>3#v%RN=-zFWJNJz|9x~!GALp_qrDzi4>5LcIr37gb^r$x}u@{>51MNsq zl#nXp)VVn8tFj_IruE_|H!wHk*CMuimE*a=^(pujp;R)48>Fr1AOmAUIO7zi8uQ`5 z1To_Tz*~YNFs2B~_)XyqCR3>q_SQH8Uvjjb^E3v}hd<#MvEk2%gMsv~tl5y;6?kHY z4z|E>3w!|+O6IbYRshgsLrphN5kzy6FvK5_{_nmV?d-Yqe|I$6-PzFpbvzG3 z|6!W4?VJg`m5b;K@fIfVh^RjRF&v{ zOsy%GV^N+HZ(>)0AtiDAA>$V$%AEkk^Aw(g%p9o|GhJOIU0@zmh)UR{CgzdsG?waV#S;q{+f-E-o-=x z@7C}={1>6UD&U0BEqBtc5EuIZ9H;3qofG;46Q^b&;RvZ|kR@^y6BG@G;ChC@tr!D! zB*)BJH5SMaCSZ)foJ4qnQ6$8&m?TpfMv8%(gxrC<84hQrAtt1_g|h$2N!H^CEWbkj&>WiHeQ~U^`=UdyEs8&q~WTM2p=| zr_8SPLlCMELNqfqw-sac9+`h|ss>}oP=F&a`k~AVrDbA%Nsq?a)Jln6Hun*q!Qd9r zF_BsGN)`17RS^s%dIRtrur$V825oS2ep+?Fma~o}(lMC^F}g)D;1q_)MctUFJs|#* znB1)deOc6letiv`Ovn#Sj4v-gA#@J8{|ZBY`}5DrtT$bZlp2s9>TgKOz`HSMsmwp7 z(=$w_BT6WSz73dX8CRUHs?Ja(%W`GM%7l8&&9 zjkaGj-Ws(?r4=%1a*{t3{FD+-LJ}W=>ywK{`L6CLStHbT(+CO#%zKsDs|2?!s0&2i z;UxQ{lLxxAr)FiAadAPE`BKfsu0MbCpQ0cDpH~ zN_)G_O^&gnnuB8id52CEGQZq1W*p0X^tES?K+$YnoqMU%<>>r}wQ26HQTI}8 zW8tT@d~WHoHK4af(fmP!B2l}U8d`B2-@%1hf+@vB(!Lln83@CxL3BzDz;hWj;aP?4 zl|X@kPH`)Jazlza4q+Z76iC)0Wv=5e6vKR8_CIq!n|7*>XW|k2PcDfor_wdKK}iRo zdG!LKMQtr_vifsUWBCHC5HPfxu|rk7OC%2+P}I*~c~I~a@2b^?PPTD5q9(<7kEP$W zHL)VD^xbt)*nayEt#V>A^IP*_y{v7sUS6*>t$1xkW$$OQGfjbQSwNlm2evDjw3Haq zEHN2B9sWD8&%}!q9f7x)$}wDgpelT>7nv$Yy&pO`yk!w8aq2wLtc^~Sk)U~=f`o8% zBrn8bsZp4MKM*Bgj$p#n)p3%|$A|*V08Ri2Ig9~CDMid=r#dnzdGdQPs|79hznAXD zqrK6{`i420mjuxONDn;k#YIU9RKwj|@+r6`;0B>oC4vTH#P1MF44r3S8)WH}!YH?P z@EqV;WtJYT$WVOabOJ2drdH&sUu1syr+Ec=w|;Mlr%B$a$5)g#U;T28`Fg!bOEQ$| zl?|a<#@n|pbk34rYNU!*$zfv&`dvTG_Maww=Y6a{TkZdMM|-2P{r}#^{(n8sz3o3& zq9^n^4`}&*l~$uFywUTjm7Rr_Ui0r&F4byByBV9-+Vi!xWT>=N5R+{dQkdF75X%KB zmqhndWj4oC=wf}_%iMM)wzoAlGu_P1Fwu#4wqD_*xhA|`2fpH&ES6JA9j$WXD;2nl#YQIhZk|s3A1W2fvSL81|KGtu$^T<#bg;YO z|Lb^G#{d1=HkEayjm7Iq9!|f6j8 zWk>w-TDE9j!+z`|&WfD~>zo;C@s1r=n%>;O<)UqkeJlL5Pe1O>S?}%SnJ>MoDg?Rm z)bF54A~K;r_31SAoD1bS*_%+TWwfv-ES^%iCxq%Q7FVSDlE7}Y_xw;Kmo-ibOML59 z7kA+MuMyRO%PJ<74@=;qPVdF?2+ZQ>C#Ti%e>Xw<{UX4$@c+?%<@`5#x%+a%|JU(& z_Mj^5`!`bn46O%=}d`HCI?12SG zGAV}M#os^#tGfMf(lo*p2Vo2ud%*p__i|L;|GPUI`F|}>$rwpb`|-tDzTxex7LMoc z@x__m&20D!WMH5x4-;kdB&6}1S5j2H-PzXZJhfq2Ng>o!QJJ{ds|8+d2{jaC}B;Onk z8L+o(!&cQ1Yg@Kz#SpUEwmC)nkAu3rTA}XI7_&gvRSVdCv10NK;G+{? zd)8L>wC(>@Sb((b|CjqaFDvoCcJ?;=e;rTf{omh8!*-6XfHmtX=_RncinGdQb<87L zduC9m*MD`b+EGo`4zFJbvtHnu>@N%@tQaTEI8QB`gjb^)Q-Xv_Z?g=$zc zaZEzU(L1XxGi1yJ18FSOu``V@>O0nNluU+Q_uUf1Z2*0Cqi}mkqREcw6Mi0qC@w|n zY)ZNkqW5i)yll*5=&k_hd>TkoKAawb!OlN+ht_`smpCfA^pAi1;~$Sxsng@Blm7&1 z#R=xC2m#wH|LyGTj1J54-`?Ry|Ff2-v`@{nmoHn(kz9SzUTd#arB|SUx_pG!9Ipop z@@ikJ*$A)bezpMF`LeGLqx!*lbU|JHXs$SjrPtVc6E^i*3Y(fQ?|Z~dEypsg-_YGR zb+3)xY2k0;mTuyfme<5eLzgxIw%qX_GJ0uol&@}%9^p&#p43J#ZH{5;)~^X;3P6k} zC|raws)ms92}Ts%BFZn3y7SE_5(^PeJumHjv<@*D}~y-@sH#5R zR@*jgI54g$+)3WxJtCg;h(1!^{yJix$WR7nZ=J9VJS5_qVPYWl|>Dsl3%r;JbK zVfJ0Gphd+BSZ&;*VkuWc7xh>d{#X&EC^nnW5#QOgEo-gRk?(Ewa`nds5`2+8;J7KzM(5@ZNm+|@u!adlT^ry zgl>PFp*Tg94SBk(0a%;;*Qjd$wR1Sy(EoKjV%J7!f|EiNH&Z_hW0FOK+yMNU{0)v? zgTWLfh~jYI7>DOxQP?pW)jCjB`8628J66;)$!<(#_(w)jb9GUo0YHq$a13SfW;j+s z3tSSF`&<6>q-n^cs_`wJA|}rZ*5FxR#1Q5QdfckRjG01(6R8Zbyk#Yo$d%gv&)-?0 zFD*gRbEKecfkB_ivXNZ}uJDd|@a()e){d?~qzl3=i*fCETlo0Uw->*Sn`aAb|M=t* z@r)+=9t@bKluG`IyrF5`Dc&_%LYmwnN^ykpqWO(ZeMc*GBFCVfLtvwdqT zRtP{2qCh+NJNr936923107WTbm=n4%*wz$e*<^x0)qUk~+B}o?-(Q;*ZJw|2w90=v z^cf}b(oO&^_Fpd#56bpmJ3Bj@_>XIOJm>$MhP0jk3u@^-)zdKyhcM$aLh&E+lv(i< z((&^yN!$?T(KO>TeacaINV~8WLlK(ANEwo*_-9J8R9lG-22VC#GDulK5 z6j&fS)Ip%Z^8|SW6kiP!>%qjs{{nEr5B9de2y6kZmVXL3Q3`P=)%EcLL?}iaf&UB% zMPI~#|96xymE@Zdz-N#HNRdj0h?A)RFh=TT@+r_*fbjyH80(!tgM8xGQI!646+d|g zmCeaHiO@B~31`(33~}*U-+Wwg$TJ3>=kmb}vbUMKdGRoN6LaI|lQTVPa)3H5rG%)k z-_~04tf2?%Viz>)1iMO;dQn{GMn3YgoXd|^XR96RjH?FE5C{6(Z%J;=ro?GL5|eAQ zXmguM5KkQXU=4X%9_h-UM3{v_9$jeaSDnc`@=A=u@|4z)hYl-5?y(fn388cF?hQD* zFm4-|Br=fuo1ZUaT}{{XzB&jf`%$Z}U7MC!^+j0$9#IgIBtap^x0o+-Zy%|CWuE0( z?}*mms&4#2M_6p2x1QiwT`e@!Tvg9}|>O^381{DG^fU_Z3>I26MdYTH>!J5&*6*`w%+ zw6jH}#;u|1v)1|!U9nl)N6@pepMu|t?lcdksyr<9OAlE8ez>b&y%Q*NX;zg8x9>aa(%OJj~v|c$+CjN3le-R){3# z;>Vc|C1wd$TkgFS)P2I&lD%%?O7~cOaZYp1hg-5xMxFNS@ia{lqTUZMxTohObg-_Nrkr}dnt-(waaTlLDQj)C7cr_#e^HC(+Y7j@ zY>7X3iz6vXXmO#rq}fx=U*(RgDbj+vSY9-Nu|Qf(zrR#Lt-YUx>>E=Q$pk}<9qKYX zk~Y-r zbX{sZ-;wY}9^c*}7$FKyjhOm0&)hZ7Vvf@oS)dsgePP2VPoLy<73BV+~j+MsULF6g4gNae584<0j0GOj<( zI8UvgQ<{d>Pr0&D5~3-EMl8$U`OcKxCu8Xz*@rdLG5cN7Bd28EG5c*k7Twe}Ef!jR z?tjKOiA10KUIVq+e~u2y_CLFOFE{aD*YZ4?`=2*zaec$rKqN+&Xd;Rmve7UGPXMT1 zfz|bxWr~d+Jqdu$~<;s-TBviTIqiT*=$T8jo8w$z+345?rz2YfA?T# zXG8zj@zmX;71M6Zc+wO~1%)_z4UB(gOvc7Rvwrn#|0r`C=hxLLb?>tj^4SXo^F)Qu zmY{VbrpX+P^!Pe@4JewTPnRf-VTg|77!02Of5RWQx1JAw-1@Kok_|l@DBxi5?DOuQ zgT{beEg{HD&3^%$W=RMRUTUYgD`kT<2nIfS`mAYeeK|v(b%}jg$^_9R1dYW&yT-rJ ztD~_C605>Fpj%-%GRAhXy@vb_20H^C*AC57-nrh^R@oV^;=-UV(Hh~|l0~&(51)_D zt=!xHqXU}variL%q`E`?{8Mg0z5EL=ZnYu508aiFBT2nM#@8l_-_g^~|4|x~#T+f4 z{;`Gs?;h^#SMq;|e>VJo4No2aZ%DRf;=SpZaQXV^baY!X{@cSJ@JjiA9cFo7Hrkaj zo48I-!4*OvA|V4Lk!L+bo!~ zic_3Sw}cF~CCgWBL7hN@Dq8a3QcaAqH8DXMm=lbU+C|JNn!`_58J!~kk}NglKp2{; zg@EIZ`VkOIL4H!fpB)?xiHKP%De^^IWcQ4-_<03E7rhpjB#u91{DMSIAeMaFNjqCn z2;9w30`il-UfV3MQN&!=n>a5TNP|C?&7Jrj$}b5#8(!md$;EzdT`VniQFyq?1lsOJ6~M@<}JQTT23dAJl4YeIF=Kvj2u&`&Z|RgKUjq)CYITqZT3CWwhqW&D)?e;FTYaMzo2E#>*M~FeFBHR9nuu(ujKXSkMviGu@{t4 z4VrMXJp%K^NvBTG!$6a=Aj0(ZHlL@O!forgo`av)7JLo~rOy zwD90{gBC|Xu8YzKXLBhar4l3+q#njj^er!Aw7tF^MkqdDIKLz@0uHM)eLeo?)yLzL zlhdoKkH4P&pO0s6+dHg`@%_DZItA~TZ!nsgZ0NR)@zv?c<>|ErWj$CoHQN#Mx2HcH z|Mu?sj0_6@ff;P!Q@`^eDjLw%jsVTtm*=O~r(%a({>RzL>BnC_Tvdj`;qWD> zy^aJ`7)AP@R?IFwTz19mqDHpwus58*7H#ENHdawzF-gwediT)(``hK|$E)AosGzH7 zAI?3y$LU2IS<9Yv`WqPI^Oz%IWKjvt=E}&vIj^0w=&%7a#gEI(`rT`#m-% z#DBTIw)d^{DuMA^6vKsfK|cZqBYQ{?#qyM_zU|oGIG!Vt@%-b<{2N6u!hhKTY8apW zPQwGh1mZZO=z2yGn~^xOYx=`I2fjm1AUbM+l>u+nS*+zmt>)-M5-`H7Hi`^F{pg|pa#>2S-YSp3*xV0r&sR~uuSm*hdI$aOG zqC|CkEOc#Qsuo&btZjK9<1>_StUTX`^NeRO9O$tj^=1qR!O?}X;Sad`4_d=l| z=|MxvgL|U0T(X0@;PyV{noYl5?V9*foEI$x+f#6so`0dmFx}crMD@V7chT@*N{7N=BI=pOz&-4oX^PK%FP<4#zPFW5{L-83eqTuhhTCIWzEXA+Ygs&ec9c?1 zjXbgyjFc0swl30vm?)WBuR(F5>2N&CFO%$fufHkkbP#fpm25<##yFuW4^@FHDph9k5g6@_>|-$IXzuK%K&i0&=RI>kmt*Q1^kt5G?o`~oVuq_L+X8LPW}L*S zwm`ds2{#_x*YB=;6I4DEs^4~J)thsBerisWUw4+g_Y~a-REJY+(;0TAPg(Y;rF{;x z%KGdl9HS5P#JH^2V!Omgzk^-s4-_;jMw*6jeGR z&`X2RL4VLV_A5~)sCjrRL!A)1=(50C52fucBbzi)-`vyc|B;d?!i;8;&W^Kaih4VO zwE6$v@{YX3PHnwvpl6d`-MjYohEW8j^WR5~NQ{9Uy>G_y8HIV^KJo zTvV%UJWlTGcS#N4VJ&41}h9#M&mHyu+ z@V|cA{QvhxCIA1O!@a`|{a?$|hyJ(4f9W2CxLQJ=D|4zmWQ!#1Q{J3!%HE0{qR@0p zKM-gntRrxVwe&>R(F%VB{9~^Z|JU*?cmG%QjO!mtN32vw&?ys=&E?*T!pUj7=Cg|QG4G#P?u^EK zWsW$-p)&N+4rc#2Q2t(3pF$4l6loVl#{<&6z3V?H8eFR6ZWrE&z=Ld^w56}%8!a4kdB=C+6Lo$~t z>i|+Kvtg+{O;wsgf|m~8-nifobPV^I40jre`$VOFfKbsM=$*Q;Tx%PH!=;|C^nb2} zw+EpAFLw?{W%_?G+R*>CJWRc1R2*Fsu8jvsaCZ$3L4&)ydvFi#&JaAfyAvQta0%}2 z9^BpCr_Vg^_pP(epZV3ZdS>_Py{qoJ>MkQ~aIW6i< zI}3}W#AK7k9bXRrsv#kd!fy{Wg)F}QB*8`uQ$yI~Y78)f@}MShM!t;mVTr=F~V zHf!8?kcX>I@cEBnl zW9+j6(RVqao`ygSwq|F|P&Y*^!_EYa+n)jc1Cz49DvkKI=?L%4XHp!N5UekJn2Wv* ze=aGkd+Wa0n!Z2wU}joxzo=w$zk&VL=@F*7I-obKr4mRlmB%C3pKH;`6GE_S?qDb- zBlUpJ^{qkfmNs0clyC|^Qe*a~KK?GEOSZxf-TROV`3KSzf9Xa z2%d<)`^9CL)ma}v&%sCQnXiqdyPxx$zWd0}86I%Qan z5DcRaVoS~ztjswHj)s*lcL{b3VdRWvG!nVfg?;dlD>T(QZPo|ecS&G3w|uOdXhdUT zft4OHv?LK}jAEq(U^GZR0h)?+tMwcY0!hW_+olPeyxsbRweNb@{i z$V545k=nvhyg1%BdHq_9CIMkSo_~9Q!PuN6uoVBMT@_P7BYqhaPzIMUMr1F9_~X(~ zP+RWrIo+X)HDKQYx{fRGQ3zE%WO5)L38L>XWQq6oz zN(mT21CJ9cdeh58eaBSCEu-%Np*9WiEK6-ie*&*EY|&&U^Ys%%nz7f2gPC#uwPz01 zV|q1t2nT!=AF*VHB?!6r>yjACk@7u+5}z52QiETBeKvm9Q#Fwa3-zLb9?Gs&yabI% zQQgkC;vwXH+jPb+Nyy$jUs~;5iO)|UD{8AWyYfr(o>xS_F3@9GaYg2nKYh7~FHEq( zG%+*I{J~t=*|*HkN@M$OxSaA$@(S%{@z&GKMoM|nyp6J)7dT=qT#icBc`@|Fd7L*He{$LzVm^J>l~DkS`o1#S#bJ8Exy3M9p&DcT*C7tHg;d0L1wHbC}ejf5O*S_8r*>o zbXK_NCRV23qnnl@{-`R`odBB}oZw5TtUQ1|rS=tWi35(@Z24Ol9Nxqg!5+hM@_2M_ zcTU585E~UQ6H?ALwT7j!UJTvs@3L7eu}gAIUTwM;LGftV#82x&l3whh$-2kBOdLA# zn;Gp;p5>=L#4?y)A`NY3Y5r{5G2E;W_D#yJk_rT879Uh77SJdSR}ANA`6`z6Fz1L& z%#Lbt{MIGfu|gJi+m^!(D5xz{qAflaR5M*yH+a2IG+8H4hAB&j|6dt>{KevbWpptM zM)*w0s22r>do?Lnhh%|j1wN$8H?0`O+W#MrI9^kbR<%lsmKUs$8*E(3)|3AjzUj|i zuk%S?#D=z0MM)TIdnM-R6S<#-Ak{Zi%{@Jnq6sMycJN`>5xi z3Is82nsb?!)(#}DmOv**T{HObrp-OR#UR@>VC=DrKb!~vxCAvWL8U3T|$;WR07y9Ce+8};7Z=FjjC(Fuz=Jv(YZ8e&~Qup*2e51_a zeyOP_Cba$p@89cl-H7i5ejaB^319Y65dw8p4@5Dp2GT&Ijc&a1k)V#q$9FQ4ScYd| zqu8&{z~YUJrhf`T|#bih$Ad0_jy^a-%TEu zcGP&Gok&^T;F&TEX_RPT2y^DUQu|(<9HEW>%M~cO6altghF$aAP5eHGIPl-%e%Cn# z=77K9DkTCY`5Q}(~f*=4Lb;6oSK1}=A67EOH zXcWJ@6qLCN&Bz0ZH#+WD-njm6-?amY4z|qAEz_@;eKBzq8PEych)&qVwXXH_t{M;P zFXxkzB`fO+5xI1)Ro5ab(hvCJF z-O0+>?@ov8hG>Mp+9*`qKd0Z72Nk4+=Y#U^s*brx*|Ee+*WijdNg6W*N#%nQ9gm*g z7xKVwT_E(UukV)^lS)5YYG_5UbUF8JA zi2MQ`U101>0N=K@SBsafw)zpEIahiP(3}B_eTGie|DiuhL7>}gl)d9t;_a(51pzfY;jPLIvYyM|N6tal( zc>>pE$qz zCHp@g?LTmyuDPVS&~(J%Idr)je|Ogjdq>#)va7M$F=oVu--fj#fjyFSbhdeVsr9GY zF{#uM_BUbmW+asjR1=3(G2nO`>9+m@mKSCcDw&Cs5~AC6ZV5vp!aUaosF!=0M-&K9 zmHhH$(3`#_=nSn*7m_K>7+;{18}{PnNw7Fjfk#(M;&#i_s&#M32u>egns}c)wN6`Z zXQAb$^L{^jUVdJ_U%qaGNMQkOtwJ4dUhmsWbGLuK`-?&aBhD>(f1ovklBUf!DU2nT>( zgkdO4s4?o{94H1%x$H8=fAQTU!JIYduSQsb!L&vn^r6(?954)yL_7)?;;AX*T2Lzw zkE7>mqMO1sUd<^1n&yaJ9u`f_V@<^pZ35W%v7MVybn7-30y|kK^*`K?cpOkDt?UPz zXbg3v`E*jvtlP$7zYPzw3%u0e%b^HMcYgr zLStg_zG(VAo)s34+6Q`&;Db)#gjK|bN$n3LPr~un-k+qZzP=1QC%UptA6taU14!Ra zA>yUPfFmj_4Jm>5n4u0%dpmyzNqwuxyB2W(6*&rrpHZZW%S0!c=uxnk`1Dj*d|nPQ zAejCPi^OxbjQ*{H%cy;$__vdKa&Yn9^y>igd80!6#+{+j%RWshA;~~4ZrZ@>z!pph zCMa5F07`>GvIn0wo+S)KaXdzlRab-4Ad6NgBW*$x%_jG^K?KRzGE^Cxj2CC2h&-eR zZs@3yJK)v>F`QzbMFYC3069*#coIdgL9?Y6rc@YSo1hrd&Fef8J;~zm2o+5kiLdT! ztYG3rHAx3Cb7LK7U`^^lGdjBZTP&H*)-MwDbj1+E286N(h|+;N;hhSxrLM(leOS_4 z?!&F;LwoqzvJV>&Ba8=4IS{=B|Y+`s- zL#F|ZwPjTA7Nqu8Iw%DzR5Tz(32=RZ0Lpa}Fjgv*t?u;X{G{m_E9+R&9N=LqV240ki-){g-1x_x@8ct$ELO ztx7xZpZXWapX!Wedb!DrS;O;`WD@h^i)8ePYD6U!0`uVfG)M0WwZ32dIIHnq6VNxP z?3lPWn{YG#q>6j=)1J;`K-H0O#a_3SuR9mMVCB=ezis>~=8Cu$h)AC#J$8JdD@1_dnKcuHh6cH?YKkppycCZf7 zz>N(21q4C9*V|`rFS`qE^+}B;g6zYCV?3r4PvTO|VkSEH@*K2=LzpY=NmpS{Oa&6WVGA(9lc-GvAwm=ROzlo9ZSBGU1E|;5$KfjnO zu#&PorcWd-+Zr#${2pWoNWM@I{*x?PYa3^IYJxK)dY7huTS2T6M|T+QUf#!LD&8|v zLw1TG?y__cMZ-Z9D8q!dcI#jzLB(J$4KDkY4kwv8Ql}^-q~sF*(xs>AC?|WqUVfoH z=08|Tk3`lvl1L{7_B+z@{TzkW+%h^O(C`zMrKTlx&Sx;b&KR#9eFkn89{;y4exT^& zcqs8nUJ73~&7#~RZ&|QgFs`P^1ngEDc7+uJXnmrft!tk>1?IzwJM}XLH z5*jVXu&?_g^FW*ZQWF;?nCCRamWSU8!9vnQN`_*Gf^24x@*1Lei*49=MGRBY?tXd# z(zDjSR1DVYVEM^&3@S_Mx+UD$BlboAok09|Z(|PSawz2No3U1eDyFH2;8Mj3XZvr` z$c?eiEF#tIR$QOz^-&v&lbXESc1AWAou4ZixAi8B6O#MQTmQ<@2hAYK;+LZHM@Ftt zvINjn+Jg;BDC=~O52s47Jm}l_^=OY|G*h#`YZA$!oqS*6IoC6oAU=9au-8FIagFMdGN!pW$?&#aeGCtw|t z!btQHPRT;X)W9-A-Ll#>yPUj_r%^jnL{6%l2L?bw*l8=PilY|4u7WO#koht!3=jtb z4um_cczN8{KS-aB@qrlna(&~5h1f)XRyKFEM64o)E! z#Ojj_=56DFIi$w;AloUUN~)yH2ikA08q4DSs>@k2mc^>&Xv+Iby??>ov9We)f8Nkb zt+}71K@e|Tu(qmA)!{<4*udyR>b+sbz5eNPV`Pz@Yo6SMf7O)Ca$UF<4Ziyx@F)kf zh6}h~<`V;sjtyNufzH&!+r9zNpBTAD$Q0bktqyIe6aF6W)N($aa8yNTOdl1ECDQU#lKtoa^Ea_3$`PZ@!ohbQTm#1L? zH+iUw%j%!(CV)EqMM%;Azs#8z3lWAS1m%s0F- zk3!3PI$1E}YYn`!w^;BIAnE9Dmq&(t8$9uxrsh0YmcO(Wi(gzjIW4+oj!xUGK@eN6 z@|MCByLZAXc^GB)UzOSp_!v&v!sx6fAy;GQ-h!@wEvHrerNUZMi0Q{S5J&X9BWi7A zMzi;owjsO&d7yL?rQP0QCtqr_v#h@qZG{dupJcDL0V`kf6_q|c1L<^T>M*j>1j!Ug zz;YJL78Q#}(oNqRnX$BLan88neQI0u_^r9CnHTtTsN41)_kcpf+kJTSSfvR$A!XnY z7UeX+2K#dfb^M$&6P%?Jb}8g#7=M{aaz@48zW zYwBygzPF$1&|gZ%9p*KkKrl(M^Xri?!bF!q>>?r7R6*5Vmur3~HY5`PDg{n_3k`k> z5crc?B$g0#aF=xbj9)*(*0aDV7byU3Mr_jQRZ$SM-tng^yUIoOr8-BojVAvhn;dR&s9Hlm2v1B=>eMAU_Kt$u{A-1379G z1CXTrr7N~{pl3Pt3~<@ojA5;#0AltwNh*DRJ|HA3H*H?U+zUazB3R2<0JOD1`0G1h z9~=g*-d#Qd=`DvOfVk}nVY8e*j9FrB+=wSh{?NeYKi`LCeR8JbPwpc5y##J`zvVJ- z-ro9&#r#=Lr|q%;U*Ns;)9-t&UB@aTA&^#J_A7>TTHKsjO45>>PrOy#73VlHd zOv_#0S*7)m6ly8TUwfVF$E&+-AekDV+&zzje2AduQYiE5LN{Fj!SC;Nz~flo#IvJd z+&>UT=GL!@EASU~(#R3Az%~8Vl?^`gb%=#UIdXX|VI(xF)Ru8WN*0n(eK+0l?#a_P z%nL{=vB#HboFX51h`=RW`oy0k(9_4#v=Z}t@=xqC5_cg0^Iw3oPd#Y{|NggV=x(ku zTTc+9Vl@`ypFU&CTj6 zmiCNp%#GEC(IPn&_)>lbeXSX7p-*?~AfLu@sl9d2!RBx}3AjkFq<54~{oPA9fXXx- z^gK%7)uooqRz!YRXjzf15&xZcaNjdGexU>j{#$Vz`!2GtTY+Mr{h5jFzP@4?43$hr zD)il3f1Q9o$*M7H!bmn)(g_CP3xYd#?T4owgl5p=pH{R5-wGso(4CLw(pXH^Z*_(Z z+_yja$>3dE?an2rCA%nuU=C@FwT`yb#Tt<3`6yar?%CIi#{84yOcI37QCqHR8-)S& zw29xTK&!Wzz~6Di-NT4Y44>VQ#j1fzQSs_lC2NECw@=@}JQH@uh|?2J+8F7}4bz{Q zTv>kq4#8SHO6yv)MV6cC5T+IuCAMmCH+brcO1nW{*qXz(Zzr~B#r^R;@Fch6P+;+H z&#M>X9f>x5LS-)n(RMU|XeH+JB=E9{GYDG!lMP5-PdtNArkcxuQtD?TbAh8Z+>cgt zqZ-4kuervbym{&!K47m+a>J(7-|n^&A7PPkDMZ z+~j$GTzgNoejz-17FX9rL&G0e5g@Bueu$3G>cNmf+J#oa4|jeO0}s#AhhmgF+pxR1 zKx@Yj6~7gd{bOr+6kVga-Q=>lykimt=zqS#!`RmY1m_oN`#{cn%sZf$>zECGoUS+$ zQ7^`VBI#2(D5u5-C`RV54scxU!XWVQy3SD+A7(>6Y39HnC;b1Y;dwb~L451DRka(b zqzh%XzYuJAk(LYX5!OaX-vpo9+7)Tf)-$uz=95UO6k3@0hx*?oi7CYG?(h}Q7^iZr zxkM>HW`b)?k~eqjsR6QG>fVutEd1$mV3nWg5cF?z`3NG6**$@1!GP4C$4jkGr(yJ|S!jGcFw9SVHyimG;zAE>^#BBK zCdwsVG3@lhJ!EAp?3@TZm;|_?@|W9J9!WoFo^5^O-&g#+sp%=z{Y*5p59dY{Hz}hi zd%1dEzXNb9(T;T|-J&+Q#^Cymbpe!1lMthLq)#)fM|+^oBg8{TMGDmBYyzO z?(eiuuEh~(lRsbUMVMg|IJ>mF5&m;{><-*=(Z3pPqnf+_GKd*uZ&)vH!FFD{Ps_iM zoahN_6;8Dxc-eeJwI+D!Y5|O<<_^J!wuwMjZ_MDya2H{W#c-FVEB#IN=@(IOR3_0e z_*!6W8tC*C`E%EW30Qy*jgtT~<|!iu;8uJ3raD$JJ?anGt`~BXtZ5KS4^3ny(bw9r zQmXeU{6Z!~S@xbeUIlF;z}8daC-9%Ztvgk83<_#hJ%h-h6s!?5*kLF5M!v$nlz3yl z=}qX4%*$gq4@cG>M$$HOipxYT^0D1`y4s!edeunv2A`fLn0G@cpPR5P*Qmv(skA>2 zkG|F*?#l19yX9cz%CYQ;%i8$S4ze+m^ij#+8fA$N*w}etl{H(fBvLywjmi#gG3sj$M1k?d}2ogS3yt%UR&#z23 zb~2gGpW~FKu*~o6SpUIH%?Rv&yjqj;l^>mV-*lKZl`rP@2}-24s~ z*8K&>l%_g})kHR4ltU{9+9rcM8No?r`72)+t^Ugjr_F=JP$7k+y_SF2Xi@zMH#JO3 zUrphA(=ocywJRdjXZ)~3SksMrz)g-6>#^55hwrsz6~sIpI}n)m{EPq4XSAS6r$?B| zB|8!sE+1}dXEfpgIe*F|%QaXjq@rp?MO|CZu`jGO3_L}u3eCjQCD9kp~$E&cVk#LY(kf=OA`g<&WOIJZCk}2Wo zMHWr21saC@8q z;s`zN0Xa7!khUACSBqC5I41`MFdEyv1?nsUTfL_1rf2DKn0N~)>U*0hS(ws@ds0&f z+u9?u5nZCc>Y{&De%1{G;}woD+yG}0!02IDS~$@=u*EO5WLFT zUCqD~LF+MCHFxB$Z{q%0ANNviE-6vw&yPZ*jxfRc%1_2|TR&1i8s_Phzq;gQc=%Ykg-wb8$xi;xG`(G3ERhIYacAvpkQ@edYGaFHv~<6 zYT@UVX>em+fb`H8H$vmAGgjDj=6%YM_?qCU`3GXQW35vtr1f-*ZC@G^&gqG&F! zXI0#uCarQWd6rfObhZd~b@Kb(zU=MHQvCi98f~`6tN*D3c0Mbe-0mw@}vU` z7&x40Yb^jMCiq(gy!``Q-CgufwtOdPo^g17QGXg2n)FAeO?r+~IYu74c4ipiE5n@m z)L+4E1$B@~VTZalg=b|O!d%d!Fm0H(nS0cw6wvAK>iKeTzc6xqK9Dumw&XZjeiV`P zq5LIJk?nrszpISOyBJnBmM`ypbo;uWkfobwTuNVd_K^rT_8)hZI3huYwec^{jA^

$Mt{{5%AL7W$f>{{cO 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

  • A+;AW{1f(wR9pb?d)r5Jaa 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 literal 0 HcmV?d00001 diff --git a/vendor/github.com/cilium/charts/cilium-1.17.1.tgz b/vendor/github.com/cilium/charts/cilium-1.17.1.tgz new file mode 100644 index 0000000000000000000000000000000000000000..d95b80cb41d32c7668b56856a226b83ee58dc55e GIT binary patch literal 218517 zcmV)EK)}BriwG0|00000|0w_~VMtOiV@ORlOnEsqVl!4SWK%V1T2nbTPgYhoO;>Dc zVQyr3R8em|NM&qo0PMZ}a~nC5Ai6*EufU^Tob5f5BK2jP_h#c{Np|a6lDU%Hy&JO= z7FY$67!?cE07^1@KmPU?$V{M6#iB&J-Ltc|ASnac$*{Cw5Uz2U6`OIyGa~?X!ECr(<>k%yK2{ zqtf>imC0oFFF9imLK~^_F?-l^f98BC#ta!N$F_xQD}@>0uLG(6CGRlC*uSt;nj+(? zE9is+FdBhXL!`naS{4?D`8W3qYi%rr0l0-$4>A>ur>S(Ec*bUBp5T8k~`CT2~<&HlF%+s}glyTOnr3qEOl*o+_Wod8uVSXN00B8UjRwT{oU+Wsuk+w^9p6 z)9W#rCs~=wd=4;qQDiFss8YdZQj15PWoC`AXJ4$)InRDp`aV-U-GZQkx;#>sE$HY+ zVev3A=>UA4d~1C?%}kA85#O2wkU8VX zHCB0&v(gBba3c)MLm5M%&dwO-_cJq(t$CqpLbIqPRAyvBlxk%uiIt?A}S`=Cs zky}Hv0hmjk^EsYw*m%Ist)qhFGB=DP98-uy&SV02f}?_KpjE~mxR$CkNFCp*wuP|^ zci*f*Xgp6DcZaDh4KpI~Km&%re)L_<++P6F4YEef^VDs7``n!)vDx<5O$_%5()wn^ zSxh>*z+|OD@G^H?Ba%|PW)&XHhHHjkIS>hl4Y`oXg4)hj%m@pF$oN!b$i1#J<61Co zjGX5pWmeIhm8bGSrX|mq$kReex8l!i=`(7$%{~r5w>adQV4onQZyWPzoeiRi6)HtC zC=FcUCP;T|HA|wRBhng|N0$Q=`Aq3VEJco&502PFc;^!*E@)wlYsx`75OyInj6`>n z`y|~BmL$TMS(#-krV4?)&of7EfZRs%n@lFn} zS7WYKS-^j}#Rv=Gd9ZyU+K-$Ki?QWGbo{b!zZms}m^+{!xD}6lwf%J_pxYd~ecg42 zBQhFtyX&mAj~7mjKXKtqRu9CTt|6QgHPI;({6U!7`XDvk8W~mUM0iRQV5{J^bFxGX z?kSPpR&QUyWud3v?c%1zW1JP|Rq)=pR_^=u&6y0rBysX9%0>LeC$p60a* z6L&IkmPuoonzhJD_hQw5;8`hH!KF4JjJYmFk;&+e_jg|EL^3=|IZ6k3gNaNeR;}Oj z?>Lbz8#>tnI~-kHve0y}->i~Ze)38Ue*JLIAc0G3WUxAwYpGCH+$0;m?;Rw5^K zB`gtSjjpDZLEe3s&>_&uCL5_iTeXE{o*@Q}w%z#@bwJ)D25J%m?>0~!v{Hqz{Tiyc zHTXJ`IMRPTJ={235u=7vhcq*sC2F};xf73_9O7A^IZ1fX@Z23VTFitNc_LDV;HN9L zEHf*M4DS!u->{ih%MCozwxy2K2cfMrAhpC41CUQpjmI6gIHuoym;lJN0iUE?t+gXu zY2YYfshG(e=S}xMQL%;>cN zyAP9%XY@vzZ8)r-;k4+)ewJPpPM*`W;3-piwyM8#aeKwg=FjUy(Jqa}+^|yz(FJ&J z*`svC&5ZKTZX5;m6K)762MZ>hWW2;-o}}c*2cfl0MVl161z8n!-|{YOP?uGsZ%t<3 z1@A-Lsh&qYHqTOiT}#E&cRX`4gFc^3l*+cJN2Yi>rM|K#l*+awh8vXmQQeaT$8D#? zsoQ?=m}%|ap;wOs(Ru~q%b%T3Kz9|-m))SmwZ7#}&QGsDxR=}Z#7JbS@v*nv>&6Yi zXpE;=AfQr{iJ&{`MjR^u#QMR_U2FFU%uwTNSMhlr>%wIV0WS8A^IVlVJ~1~x_ck!b z9*=rI|LUvp#vY4@-bx~B!?wEq3KWm<<(3L7^it+Qd*r2V#~*OSxI>ztRg$E82Es{$ zI&v+ze@FM+$%UXm-0Xoo{~)>7PUTkU8E@mViAukA%M~hpcBjQ$Hc{zw`>TNqU3czT z;(EHV*4PYA9rw<4Uxgdt_>#Sp12F(XfvC`jp73Rks< zKbur!@<9)BVRz>XYE1w0zn5I*yQeB~Z$vcm?aC#e3$vT4x#}C0seyTzckadNQR&o- zJAL+U^1jmttULYK_|=cl@AL!yoqhxPKD*VboMyr-l(MezeK{3@L@)l~4{S8p8x4m2 z;fqep79Dy^IwbrQ=MpTDiCWfY)_G`<{6E`_`d{`Rkrf`I=67-cc+&oVaCkHv9yaa& zulM)j#FBS*m|YcO%>I~{S=M95x>x?6 zY7P4RN-5;UNpF&grAT**r@~m7qrRomB)LA;UzF+|xko{WanQRtI9mKt zN==u1E*?EolB%Gy!`_3%oG*(^jMCJ1PF$pX|Z7{^q(|+ z`U^nNe|xy}XW_+pTtG2q|IyJ?o-`Q72icNpmd z$IR+dbQ){}LLV#2EMrC_T3B3l_k*Ja=^}*2hNc7mGbyv|23wEWAKf9)Co#SY-}|n6 zAK`U7YeN>+&B*lon&+un_Q5(gV5ic+o^ba}DrUUQ?123w#pAOw->x*K6lQm{Kiq!} z_%D^N>qVV`mXyQy90NH?P8nm9mujc z$qBa$I>$Ev_OCK9ZS>(5>GIO5E}qS$RSxZhWlG(b1-@(}Q(fQ9j3|1^i_ZNULvQ^b zl(Czx5X7SgPqw+xYiIXUh=Na6HOxfmjl(>7KZG%UUWlCC)e--LoA_NXx?YpgShbYD zipKTem<_aSv?S2@E~d`iAVT*7$QbB_&&aH-$-OT;I~ zlXKt|;jYa@h}1+YR%(%UcA~+0)kt``J3-qbSgowhL^T#11{q(u3v9>rTe32@(!JV+ zM$xiN=fX0=pE@!tm%=brS|HI0O!}Hzap4dcKYihb9nx~+zLdFr#cmgZ8TUSrxk$s9 z8a1>P^6suOB2jsYS5kb0jD{V?eksg%!DU)%W4Fh{b*Yv^Xs>;CSFu?rZ4sbBxs|hp zoG;w9%dJ?S$ex=Bv>&gFC0lSDNqHYhFWzB6MUI-Yc-l{qXT9ZbTdws58^W)&X5S65 zuUfnBuzKt3hx4K(q*FAkr8`i_|1Nlrv5$GSs*BOys(C5Q0>rUpX-l5D%WNTX>%9g6 zrJKq5^%*kKxn;`|rVs9NFv?81sgU?#tmZPev`zj#bnsRZZrRe^qWgR9d7yb>g*N}I z(ReE6u4$e!k*A(4nR*l&yf+zWBlB`8v`i{6F%-TEBR<;?eI8P77Q(Qpu#ZCI)xPTz z>AP?vIH}YVOj=H!cltFEwK$cTn#g9zMqfJpkn>Hx+E?W>(`7dW*SPyZOj;%!YVh zfk{Mo+2c4#-~xjC69!4tvQRk~OB-U@iU0D#F!cHQrR15MNs*$=(5V9ju0k1-Jh@|0 z8GI~aH6$p<5u8 z{c7>qOpra(AA0UV38)P4r~toSPbB~7ssw?aV+2u6-95+L#jne7IDK?i`=c}h{@!HG0&IAF*S8xM7 z|F&STzJDif3%Gt!J`Gz{_v^Oc3oT}uoG&)9aSeWZ6hl@n3 zEc;m6U_~`GrAZSL(EbciN%YLSrd_G2f*Slk;B+XIkyh!|n00?C`3eFLNapk@FnFj&hr=oS4HC%PI3r-n%xR)Upz=qChww$O}|hn-2J;d;PU`~dp!F*Py02@kwo<+AsWq; zo-6XbZ=sNz!wmGXZBxYg{8)p__=A$EAxCNV{&Vb=$;9H$jsUr}U4Ox!M(QO9kbHLi z!coHdO-AT2nC7_No6ZXlt9fz`I=-P}_(G+}xs}J!Pb|DMP9eUr+Dqps8RE}pUqn(? z4(p8dnN@|#)O_`$SdCeI?H$3~u}N(i8pv+QZ+aYlW^BoAvLL%y^fP1kZ%m&TMg8Cf zPdM*>-7;6lM)4?htdtqEOy=d62$)rwhz|>-f6O6@+`-{-YvIY<*g#o^wsgiv<&MH;$h!3N?e-n|#AF8ve46 z>s~2ZdGT)(a0iQixKgDl!yT*Q$GtUT;lt^3Lm55lg#iZs1&@Z@WA2M<0I%J5idOaJ3Ou$5kMExQPwp zX^Ka7blgVbiTGkQKMBpf8K(x#ZN8IjAesR2yw;@^DsRXPT+d7Nd`1w*dcIvpkv~YS za&V)2;97!thk%^r4^O~w`+mahf`K6D`}{z^UZQ58ZrE~N`pew*Q*L?3u_W|GQx6;@ zyUX0}hOd5zF>C+*7G$AQh&on!1>dyb>Bl@<1)0GQqE*4?9G%7-mbWUggG0Cge9XF|z5Ro3{pabJ{aYW%$gD6HWN!c8d(nUIdm>iP zfZ)zfEDtieD*9ksugm=ClVRA)D&(omQ=vhrlkv*I1>3*sw7xdH(;QfF#TUE&w!j0X zfHwp*rItZadt4O9da3k8EAYBOE58YP_PZF|9jW$Yw4>a~&Fet$vO~eW`zZ&)<|6Gr zPq2V<2X6D0DU-j-lO1cWDB;23DUAwn!~RFmmVeEaS7Xjyj7RN8awXCW8+$k+0b{mZZu>f6A$1tQE2%fGckui$?!+bvBk0X z*@cs6zKti1fX*L_AapQvav8H1X83}HgzGW~KYBpV6v=1HjLZ|k9tB(Qhe}mwQ5YaZ zL0I)_!cX9%l%eNP3bImMX>frdFi|X%+739uqbh z3jRPE*!d+H2aTiT3WW0^MwWdef;$w{=g0g zqY>WQJj+%AjOVl$4(sc)n~$HaPtIOc!<^^X`3)N3Ul_(SC%mlKyUBal#FMk@%a2zz zwyu$)zRD+F*T8eeGssh8<%0-#gdX9S>x=Wt^V`o?lk1NsHNYO*0Q;S?3y02ohmS+e zp-68@!|vVfkh!V!BFR#hxs~BrxWf`H`^ruEVKms=8w{D!RqK>K91eyNg=W?M??w36 ztrnqu-(y()@NV)xKsyH0ZYoWp1xQwU#W*^B`EY-9TC3UO3ZC3^cZNmIjjsZ+PLEGM zfBfm}`uhCztT7NB6%Dtt3-?fCFON?y%qw@KaXpn5Y*tQCFkg?s`;R|gon5bwE$|WN z4w<>;OLCWi^VqNt=civk(#_fJhx5~~9O=_Hj`WE}dg1^FslJ~;Jk?#}!h^oh-~_%` zr#M(!!+G!m+Pq*0<%!Mp$@j>O$bA^GZ+h~>^~aC5wVx)p*T=Mf(7CvE3ZN$Ho0Umy z)>jWgKWb@(X`KzUy@945L(6Jj*PgsGXG*6inGk!s=h6$qc7;uLaj-$kQmKE(`EIy# zGr{vd-MkQr**aBdhI)Y;*QlE1rNYQIc;&=HfEyDhR~ZA&7P;_7kvvEhk>(mM{ud@X zu%;s6rMu)8GXA+9MH#@9IWcdm7dU$5!ofZRFG`4I&E&nvFp)@rgMQh9L@I;c4AsmA zzn?_#dtAqVyF34{x;t?%#~M1cX)$9pw_?8P;NLg&r`xG0m0v6Hef|WWJB;c0??ibAZ@&LK{N7AgD#>NjGyM3+8}{y>Kb~BjqtjXC z_`SoflnwC};HSZ4o>0s}Ch)D~`Ui&}2(thY8+IAN1%*BUbf?Vmui(aAjm2KxarfJR zd?)T+!SyIOtWv|FqQ&WUs+2o7H~QVuMB^*h9iT z+bkeqnvaRO*b8#E5%VS5B1)@JHFu>_aSh;Wc27dc+)K!1^Hs=r_nLqZoKku1k=ce6 zW+=SI^YZ3i7N2r0c(On~ijA_UY!U4K4W;BO3kbZDR`N{#8ad+O{&->7QdpjH%LiUN zC4PKkuE30gkYRn-IIqbF-^g?u7|t;p23)jqxZ^s%C${1|;@^puORiVPnB6M==S`IIM_D$_N_z;7q+8#}8nTic0H^_=oNeUzzPP=YuR2327t_|e~UL8Sa->W?bV%wPWW_*^J$W5eZn%IX^x2=-1d42K5B&)bn$|{!%g$u zyX#l&OzsYW#L5SE2)uiD4P;A{d)2_c@oz>4WP&*qr&C>9jmr52vmIwD7wqcz_TXsu z>iG8PfKkf3PR{MY(U+qGN=et8-SzvEH+!SQK4pJN-D>0}d!7+~kn_b&!ZUF`Nq7Qj zeL;*vQ4^&uCJX%L&FbLk%#7>Sshn5`VE*%MAy=4>I{{kV z-8Drl|ams+>M01g{RPeONID}hP z4GNjulg}{>b4mcmY;cjhf~4ROy*+D z>TsQ`s!AK~Yb~sncu@mDp0OF1nTf$!y+UuUG21gOYf;b<5U&RrL7rNY^==!N#V6gU z)B#515LNp@b6ZNQO2dw?C#^6;%8oJX3cDBv8@V)WCO8~AM%b|99;60O{&ycHDBb9g zCYkQ-2gyUUZZo+YJGnkJgU)nbkRoZ4@w~YWa9dM4A2^!|*J{HZj^;8gtO>zR;6z;u zvO*?-#7UH^nYhHQ?}MnK9kl|TylFs;weXZrje69h#bE^eGN^eZR?ew`+43lq0{g^K zVTy2$A1x>y&gBEJCQnH(5_vkJixHR>0&y@ss}^qBjwy_=?tcw?`R>EyVGms+IzGqk zHMEQ7i)*q=@t6$zmQJ=x#lYYXQv9WpFoB$|)f@uC82EHF-4O2%_YU^nyt%`@vAE>LCI5n+ z!13ldxpk+YqqJjAuakEtvs?Y?3hQws_Bb^9%TLlZAgMd|sqHmS?tK~t&`CZ?fyCY> z@%;Y+czce`Ro<^7kE7BAHX^@wv^P39K;)0}RekZi8A&|AwN}e26GeemSr@((ifqs) zb(^hQWsD&}93Vca?r8)U^W?oB%$SOiTkntuSW%vo6^YUw&? z0m2{L@d!0KwaI_Bx@Ucc6}M239i0#nUlLJRmQFEud6D2YU_OqoG8b%EJdB)Gvnnk{ z6&@P$n7L!_QA=yxIasOv>jc49QN;uSpfrp>2+im24p|DGjV= zD=-83At7hz@Gq1xpapW`j+Rznss_X>fTSICWEVd4)m<#whi^L^=OAW5F7liWd%(26 z3a#*ngcX2*R_w4WO=Y+6L0_S$PX+>?VY0-vW5 zs#dX~zYI;h1~rDPwBNNIHN2zW13cVOPx8q61`li7&LI(20XD>eJ$BFRnQO~Xj5>Y!bPi}gid8*+n|t_@dLJa7M?qrMH?LW@J#n_ zyjz}QI-feEekSX~w;rQXz;G1TS z;5=q9@u%S+cJ0eN%M`aq2k}z7w@}~6U%lY!WCRlJqZE0S=OD`T12RrTeyyoWq7mSVWpX2b71!?A-MAp=`mAP*&*xgq zD?!W4Wx2AUIur#N{p3#U;PlBg$d;nVGWF0Bw!w|WLpyrd`?=%G3y?h?)sRhpmDPCEaP*-td{e+Xby!zR}K~8 zegHI+n$psswXdG8j1YM5z>Zm$XIU8ja-2yi_CkJrO0gX7J;?Z-&6SpRv1IhZ;!c|P!9VUi0Ien(pXk?U(`VCu1tB&$ zJvEnO-=lKL|1NU@!^O)RcZn*^PH(_8wF9TwS50cB^dr}9@%tE1y5+nsRVv!2wDIgF zuY>l(yG8_ZsX3ppB{#p6Li1DvwX(a8&{=a6c2%cmZ{#g-icce~K}N+5y(<;ajpBDf zUmxsO4-$caLeE&9+_RTX2z=$%454Hzd<(;)%yp6i!fsU=w2&`hVy~E-1yv`x0);}9 z(&BE;xOIosg6g2kB_vKnZh{wORQCXYO;-gshULOi?NfRQ0>(jC{uC>?mKS2qlNGs2 zd|#SFh0!W+_wD5R<3If~dpQ-BzryN;pm5V7lkRwfjJM>01_88Hp39K0#jP3DcO?Pj zsrgx`g!JsBxYm5tKBY{Wn*1rgxK)v`^zM!kDf{W-_=?H&1cJxaT9EW`6vbvLw3>yLkOb9%^l{tiG#(g%^=TOs^yJAgZq9-B$Wi28#c+rQ8f{d|0Vb$<0> z3_&p9?vZdU{+|wb>b@&x=%(9_3l?7?_2X|sg`F?aKGYZ%(;hvG=Q@%wm7FHx$w@I2@(jM=ouDrgR^ZNV=n>3t^bCCo`L(=WoW#JXt`xiduy9 z4!cx3R9*A>q?szKWMPR~g4&1D^Qsw}3U^9Ot~)!=r*kdJEZf~1j`sFD4&nDo|5#4N z1P?wWLhJ__Cs8@MI&T)8%~Lj&c@?MtwdEksT(HVO0y2bB-+b5LY`QIE_ZtT0FG`!L z$9zzUQIz2rO`b2bsJxYlznV#2LqgnO&MY)2ay&Kwuc7yr8L{Y8pw=2g-cqay?fqaO zb#;$kGZ5{@y{jF%BwEGhij_IKB>7i+@EiYrS*w!;$LcpAUlxv06v#A=+C^5*Wo`y+ z;+4j6x@Fdf$E5#n{%HD1F8!agjORHN!h=7-+#r)a9B1^KJD4%@%VU8@-ZMAr6Z1L- zwBs-?*bgf5SfRznsYO{MfV+y;+RP)F%NXpKX@ z34b18O4@8y?Ers93iiS=Q^+v>qUww<*(zCe;FaoP9v)I;*Z>nR8S7>b^DYaoz2)gi zE|DuRUr25Fc87<31N9~XjDgIH(sn%c;nYS2OxlZbp~I1ljwcw|PDV8}!*NsPi4R&d z@Bl2dAfp?i6>_LRpVk8&OGLxeCFY!cBww3&sSSg8#GMMqu^XdbgV$;2*L$(@BBVp1 zLfx~Ltz38w7SG+;u!Fo~U%&zqKvjr5yM;auc${VLEl6gd)5&-K`rxejrg_sL;h~ z*)GuHSY^AwzkRlg(eP6GVM$O%h1KQlCn~^}I@!hR)PWpc8lc3H;o%HnI+wSf-Yb1_ zb$&w4+G{*ibYG}PHUoS?%{AwG>dv1;Wf}SHkc_61q||{3p@_#$n~RS(bTw;63{Cg0 zR4=0hip;;`V-55rgQvNCTr3GQUzn;VlV~H`DFD0T29*2oq7NxDsJMdES<95)z zzThC5U{)?%n-?wfdO-<(>vt|?`~_V>MS5`9cMtEfU}cW?a`2-Mp2l#q+^YBgczVS` z;l-Gwm;B3iR3*P}nb*B;;WQOrBzU`=T%UTYm94CDYs-4Lbjzw!_a?tZxYgLVTsl!U z%SbT$2P8 zB}oR|hA`Is4$naDXXERC1~;L`-R~=S(Q!jUh4B7Uj^DbJ{b|XIaV?!xFG}D{KXnIC z=_zRG03JRvd&~$5&k$L`LQW=l5tTAaeUd6uP7}3Ua$j4a5&eeOaP-gv-3VVRJ5*g& zH$H*Sw?9Nlk6SOSYw9IdD>AuTx9%H6bWW?h^&Dtx6*GL*!!X7$Io#x4Ch=>-UZS@f ziu!`=pDGuxsMh8V7E=yu3RuA95Ao(GknU7+qkEo#b8I3~0lIbfK$~VNxnG-V)%9H{ zCIqEfa1=^PA5D|ps^GF5Y9IITtRg9|o2y)O8QGC3K~UwjM9UV$D2GD(I{cQEZJ+KI zE9<6Ag$A0dd!Exn5-NSd>hgR=_lTi|sg#Jku_;LpTwCqP;td9W$dcrh3zW;fzPj%J zMc0OSb z5d^R1@YV>lz=q9$&+jr%FjpT0u7&7_BFh(hQE0|*+!YKgT| zVV!{FjWsaG1%GfLIcSH_!T>@q(WAO4N^}SE`MDirghw0(Xp}O@nluWC9-Uf1LCw|K z+pU8r8(^v#MCXD3q*okQaxQ@%_=hM?DLMx0mK%r)$`mYtgXrBns?_2TEYc-F-B1g{ zL5Eq}g)+hq)PsFPgF-f5teO5Ovozs4#i#iW)tVrmQM9bYB@dn8Iu(GwyGY>vg~%2I z3({A`phM3Il!#7Y$Wu?FnDyT)orp?$?$-zz&=wCOcl;HK*855Dy6M#&-XiVH9MH8v zW9!rcs)kub$0afcDdxcrCj=xzDKjixN{7ukS%u!i=WF*h;uP}g^|?~exQ)Th)n=kR z!(nrW6vu8DY<7@_^sA-5%@Cl9_2QH2{=%r<=F-L>aeR7>5i8;Yfhw8;`fnxf&W$7T`_R?od$&)w3x>l^d*CaNBb>TkJL|hfQxBqWYM??AjSfvm=5X1bgrjz?STNmn*e*Jg8^+|i zv|0uIV<9T9;oycniY$AImF(sr>Ef`lmgYIUvL^wej@X9-cvrK~5Yqb_0i|6itgepk zX#={q|-zkAc^p z<2rWf=h)X*@zi73$K*%>i7tDoG*qwABE>q@ar4W1>xxrA)I?_-OrE0fMur31OW3y; zH~2y3?3~adr2$1odgoHB02T_ed6kvJmO5wq!#DU`0+)Ly?{D+YrlGr?y0bR zP|KARe_i_kS)>MYrRHD&UA|ZsDYn&+Rl&H8D*>Etah}n!F>w4hu$lSep9a_ksu+@* zi4KsP@FCo5-wnC%gV*>wUh8hpf3K@OIx{xi<-WkIXVvDyJ$XIX&jRXdR(az051PmM zy?X{|0Y^hTKiFE+RT$iDH`kpKb2nx^z*1`=)djt+(_RfGm7HZuE^~T#cvlhhYhf@% z{WNnsvp;R@Oa?w;?pzOP>MM0AXGZ383e8#X+B^uOigL_1idK<|h_g@+sYY<(&#HM# z@dp1YB!n;CMdu5ZR`PWKy}#Us`Vmh}AMQ8>xpzgS~_QiTS$MK>bCKKA${I$9mhXP>PHc&)_6chk`}oq7*Zrtc{YKH*orz zI$|wVtwJ@c+Cc?&ScgcI>jw8XrKgE^r{}9^jiQ_)3-wq}^B6qyye(2OJq5eDQAD(^*ehBFPCqD*#1Qk#2MV>O>ip(fr zN+r!;3r`Y=C`msYu)9 zM1tMD9@^mYQ0lxE@8#W-GEYd}F3-y~?}9YyRy zNL?QwqD)paWBls5kD`io5wjpWEmUEu*c=TxqP+jH`vpjV>I$l3Z^$Dq6<9pUJHRL{ z=WZiFTEG{Eolm?4+M)VoHoGd9x30TLe^7;qs&DLk${s6PZFiQv9*B8)d|MRAoxKw$HDy@Ai~3Fw>Gwq!lOtsLg6D^+C@E> z3fA$3qxB0|#22tnUeun?LPAfTJ@S=dO0!%%2u;s<7%3jT7b*4pd)L4G-F*K_H5Ku> zw}rMMK9m0&YApWh<;cIua$CuAYe|@AJj9RT4}xtV)TA4d_#8`CCXU23Uvvx^bIpQCL1W_Je&-p5IL|BQdSy@9#ECyLST z5P70kg|9*0NXC|%fbLE-bAG#H$T0;@y|;tO;exYvz?!KeL`9wV$m(&Br?_D;#8?Mv0NV} z)?%4b_a#I&Yq4}f5l}d;5#R*`d{P8O403dt=OT;ZU8+oVvLdlPD0GI|O8C5C% zp&o?Je8N$1u)PL^u2kwBtYnUl3~E62^~Hl!r9q5qNO*5jKBI)8mZc)& ztAN5aL8{6>bPt#7^uVDjQX)r_?#uuB5r3@sIGjM^HO--kH@MIS?rQs^5CzZN;fALk zq#R{Kuu%~C5%Xfb%a{#K$9rx605w}r76M|g)|@mbJnO;TuT2xls}Pt{}2svvcrm3Wq^$3dXm1vUJbU-IH3efEfo^?GY( z88=oYLHez#alAacI)Bxyht+LObjdYyy!tD!RR&QA8+>!QQRW%}%7kD7u9 z;7^htPtKd^O3>N#e1h)bs1XeSA)cB)8ftd-#hnf3lV&zMKhLXk@_zlIuRV)*-lIunr>rzw?_FfHcx8th5pK~G)UG>A&g=b3>2`RuITQf zJPACX!|e`@s)QYu&x5k0F%xp(sUi2TU8x53+z1#*pLWMx#~{790cA>bPlWEc`~OAG z>mvP!?!V(K*N8i#oD=8AmrN^44B^#Clxv@qevb7ll+B>fWc2>^uAQWK0KW8@g&s}x zh@smSNpze-iLc-)1CLxBmC67xIitJ*K;IukM$~nWhL&VQ{L$?4CI40BeQ|I4g-ZJ# zxBjD;E|j`=;tyY5t4zeJ{2u*pK$a<|xv=c~)Wom$R3;EWi9cZ*aynF`(5dRNdi_Z* z9z9det<#^WdARvZ{Luy;r#ayjpW;y$-NVBS~3f`~EywTh>YvVg`ajX?`2x~k{*Yq1(1!0)Xd#WI(< z*uHGyQ?lfsv#86VklcPSj|3sE(vqZ`}$LC~i9_RZ_JIKWjf zvOw*m5C>s=6+_rfF~&ao+>(Qe8{)E32%8LoL|Iintxe7i@t(`9)Z%ubg;}U99kcyr ziMF6coJlPnd6o@2?$7u1S8HuDazlm1S2OJH6A<{tuZK6TFPQ5zp;BA%;`!gVV$gbV zMpV9CyzAh9UA$Wpq(h5MlBiGBL@l!^mA}sWS}DtNQ?Y>Gm}Jj8H&&|f44{BBM)_rf z2Kr;3t=a`lG>>uUU8pq9$&JZ|GXe4OA$2g=O=%3@Sxi)VQgv)|5jL=0eU3_;Cxy7x zvltU(Y#eWsC}Ew@luz7g(&uW@g}ItpO_W{*Y*u{~tB&QIvIL zj)A-;RVnf4TsXl#@d;Z?3+7(B8P^~?cXrrIEnfLbI}p z_VQ;;XKu;7Ninw(9e8OM3MyZEMNN=*9Ys9?C&yuN>bRGro-9cC^>pFJUX1douzLM#op{qx-SlAd!yMj9r68Sy7yWf&8Bbn_Xuif#3=!; z%6^nt=Ix8!^VyZM6EN@DZe$Zvxs!K6Q&Oc3Ow_W#-SB5Mh0WsEo_0xxF>5pGe(ZX` zm$@_xx7|l?hQXBF;k;0f~xEGsm+$Pd)#3>Ry~c zoIk5+aOb405s!NXS*r>~bi94DSGU)$HzoEr*68 zrS%Q{8KX4O64@R@PQEKn4w-`B_X{n=5>!#UJN`!)6Y9C)-(Y%0{bcoExk9CW=8YR6 zsS-{ZZ?oLnuHf~_WlnKVzX^YU)8~2GuZG`sOj;om3zpIQ*|QGT?L&Rn7pwVC`1e-5 zAk$T|EB;vPKbtuQT{V`D)6cNh$k5GemCky|p6-NjdW^qX*Umk^Z6I~6D*9Mp1IMF? zS>sM{@7nr)ic;VZmC+}T_3|LAhIq@u5dY|Yypg{m(*~{$GNZ%G z$%&bIH1Qn^HJ6ix82Eq+n8m^H?I8l6tot}n(pWa?vHc!<-D9J}9@`)F*wKEEjrLym z*xun`j~$LiJvJKd?f0nt-tgdc4?y?Wo6%eM^=SXievj?%y*_%~gIYz0)r^qlC%oVZ za8TFvtfofzFNM`IF^$ZWYdVX~=E|uPia6S4cM00=4jsPqHwMA_tlaJf(PANTNPAc} zrShpNZ38zbN`n8n#YG*`&9AqM#V1q6e?wU^qyoHjLAM1QhnYZ~3<{vD+4#4jH-UJ| z+1VFJoJeSPyiwfm!|+3&rMVgZQVPBLU+95!e0+Qyf7Ysq{c3=yB$UHqhA=B*B`q7+BTh@n?%nhMb9DJb7=M) za{u0qV22~>KbkfcNESlndKYzOl{x!Zi2Twcuk@1J*p?c7eZr?Vydf)bfrbbre!-?< z7;Xs$t;g)`+oMC2;9~Cx|AeZeN`P)JZU)q{#oFt-M~MKRgMh#Iky{LlH?7vhnxLGg zXgMXp{)e_P2hj-UvpCU=0~KJnjX-X6RS)q+8n<+aO0X#gbls!p=zxhlQSS1YoLvTG z>{BS!CJ`mKcopON$%w}Iu~rHHIXK@BD@TUkg3Y~Hts`^h1O|3`b%X9))ioW{0CTR` z7X}>Ks$!7|N;({$$>2NzgCr>xGU$hCE=+pa%cF+m}>JBv*Mqg4(FnRrP6R_GzS~bbY01 zTo+Fq2MnKWe)BXxt!?-!A#7vMpBy_XLAgq|ia6i;+ndk1*adD0bDrK~9X5<0>xV+09*}FS?NZN0S0)xz(#JNDza9otE%HqHW|Rvsomr z3o@Zh6nSF6AIB|Gfb{7SMb4Vz9=U$r)LJyM+fKAv3cC=caTlHDklColYmp%U!VP?L zNEAwQ@M%J;uC<3$WQVE%^c9WQ`r9;9)7>SP`K~}or>#{#(djM;+cKXI5~YPQZk*3V z7W!xgE56KLJ$*0MGzC0`MQP9u*NA#FqC4AHcndgfv7H3$&uR)Nc}S7@u@#hWA06@Q zR$`NAUesh>=Dl*z!aEw8TJLKCQ-z`^nARg4sZSxJL$~Ny?UUPn-Q&}L`}}z+EdN|R zygwHz{p_CVpF@l;{1Rfvs12x()G8GBeP!FgY9(AJO&+$_SMTR^_06&Lb z+c8fZ={}WNF!lxa@pdP)N}e2RuQUGUN{^ZtAXTa;&2R zfjl{%q(l;uJ5_rm5*P6E64LeCMZ-25^0T_@BePKL+9wn8vKu~-7CcWgp;1={sjGCE z=b^MuBxw74x#qD*6Y=+Il?cCv^R1KabbJk>u6u_2!{b zo1Vs}Jlvq4sbg5v(IC2&ipCAueWtNPdf>6Fcd2k+6VKKuT^gvDIa56*2J=CW72KFd zrOA_g%8g9GR_9H*f2jp9LZ9a|^;fFLqtT1Vaa252c7QV+P_OhzPf`00%BVzi8qFUDZ60YRv8ejT#6p$RnLVFg`piD zpVW;NZ(Hl9R=aJzs}*k{kpG9&X!!sNSIrZF0WwdufFcDO8l3QCA^POrUA2i?e-pob zC8s^ZYeb&`5X1Eh)9zIh?A4}HK_-4`>UlP?pW~i!#c}@2e?QIxLTuogWAtG7wMs&= z-UcH3`x{#Rb@G>+@*lc2lzT1HsGuUV5NqA@**$;ecK^|}d11iM`(CVmha07V1^(lO zglbNIr56g|JU}GtJv`zycc|k$&Oksa;fA?0$cNraXyf{M-SNTR!9mx{+mKq^A?%ij zV7RCDs>isA3o^Qw2Zr+oc@qbslAQ1|b)X@sBzz%N^Vp9Wc?rQon;_9^p@7JIxCZlF zl{u!~KXNbYI0e5j4s|?JbVG-RAkK@2qwiJK9qI%Uo@nXDS6P|g=jt(M2_y|>GBxX- z_P$uNxtGN!Y=82>H9tQk6-FV1zS)>wZ4x_MPDGWjEsD_&26l{^rB@rINI}01d>`WC z9PB&NwP}qNL4g_ zL<2_Y*y(XZfpb>T(;>20)M&=eEpZxwoBHCF3D4^359p6m7+@T}LCX+*g2sl}Z8~hi zPGoqDJwYNn1thSKsQ}?6Rjo_(pr{_l-ln~f^F_8|sWgcyHH0xx=~bFDWg~g;=^-eg zXQ7}7r(=w;sQ!WHW z3#Q%LIZXh)u5lVwW31|)21vXpv?{c8(;#~%6vUp`RdcWQ2Wk%_#a!{g{8cHJnSxv*MC-P3FTNix;dcN|EaF3Y2 zSttwrqDY+@2Q(Z!4C57vV@|AdTo3*^rIc-|rr< zzptd>Ka$j6sq`IjE^+Ie8&e8>EoQMW50cinw#5cgMGfv{o`z&h2qOAQDRy{Y*1z;? zdz#@_WE{|iZSu&k`ozq>Hi4SG=eD}1TfAi;1A~Ta{_cSU-Rho1-_MVz?!@u6-a{LG zhrZ57XG@QvZ9H@u-a_Am$0i@4ZN{(r2Yom1piRC(-_tW_gI|z#^3A#oTa$jOw7IlP z{L|NoA+PG4c4fOMasXvKeIZbePE^!pt)bMVpKUqt}B`#Yu6~KE|KWw`MTP{@d5$&1^ay zy?HY_d_8-8aIpV&$X`$QUhnZyDw6%t-u{6&8XiuQso2{~-@F~Y<)fpcbZ@xF_tPX& zWUqyzJQI+@;*?jL>p{nJ@sT5OMd;dL8M}5DnM}BW;()EN@Wx#R^`Y-EW}^yV&4oX7 zFML*z;L7hOtkUDgPb_5IIV~MC+=OU`b37pN=d&*&DScKa|F6%iDpaQCE70n+r{Rj) zlLMQWl|*)OyCWGXfRViVVgwd#%hS=s8hxh#&@`Z_9s>I za3G;y^OAqLxfhQW&Ew9fgQGv{MICm3Rk?_|xE=o~H{41ygYu|jcB-zF^>Sh-;t`dG ztF5`ElJy+Gi<3$WJa>bltC-9olN2W0 z-7SPpL~i*UlMFfOfQq?+%2*5ZkMJ?A4W$ssJfIz2@-LtApehbIEWNugo37Yhz0DXx zO78p%UFOI2FTcaoDT&rIQzz#iC?i%@_A4NS3F-khNcIs~m7F^Tsh0tu3 zv$^JpV1>|9r4d*ZCfRA@mX3OUa2+Smj{;L`xYza*%72ce+=@A3i@)8_Pkw{oS~yWt z=5tJ=aCW!xOh*hWxy?|?%ia0WUw z`LpMSem}vVJ*j5q13t%(4iESDHo&b!?pElg9rO;=+xwVjt81m~dzlGLvssfyb(tTV zE0wz@>mNQDp<~qO=QWfWOL&2yaZ*HbRH{``m)(vpE;dZkryN*|&udi9!M?Z+A@QY} zg}zO%dwdL`_P*!cQoUkh(qq2F1yj0~wR=$G`_N%+e2l5W%^wo9nz$rR6`<+I^04zz zwgui3(Q8W?TQ@(36u3O6NIl#;a9hf}y7l8sh{V4L#lftfI}<{^X5{qE5wGsS-oati z+lDeG2G|^1>JbkG2^{e#M-wHlb6di1=IRj=Z4IhoxGXXODc`wPWgfgA=s>L5 z*R)_sskO*$wqgseQ(VVjpm(7Eeh?pXRRV>m$%f33wG^7WMVV#0dv9J3-|nQ+B&9J@ z<)8Iv|Mlqg+r7hsqxfu4ntk~3jGc1Ji;U-j{j`T(A643aI+hSC_hj#*5Iiss$)M?w zg8!f`J=99MVP0M=`u3`Ir;=x2qg17BSvz#6S@`@`KH*=LH>42cdhTNiUW&ndz`FZG z)1}SglNvOS#HoN%Ieu$-d5E)I^%Vt|q>^X1h~4+7(f5ug_i8q)Ozkiet5+>|Q2W$n z&JvDEl4qE2u4Tpm@@vq3u59B=ncG+Fl7ERx7h!4?P$H1a?G!SQ(A&dMDhJA`Hx(2H zQi~Vf@j}PTLTI0&?-k8xG#t`=94t(4av}0d{^fft#n1ksQV89rk!enDIGT*lIy1xA zM09F$7pJCLB@aS-7bwTYltY*J8Ki4Wm-!OR`y8%dQzavmIhE;#(%OFJ-BDP|YU-zM z$hAd{-Y{PG0=e7Yn0od)I49Tii2vEHf7K+aLTWy;d0A!qXsBZ-&2ySnVgo-mT-c`}KQxEpEGA zo&FUN+atKy*0I-{qOWV#$W=2hVpYlsEAT|2{NhUiyB+GM=V=74$vF`-$4nFjqSi#X zjatQ)5bE~!-w;pUptf!cp#E;ieQS(&1IdlC-g8x$mH-3zy_#!?XCTMd(!sAppWC_U~_j$Gv|&NFEDi>=rPepXJf;Xs^Rq zDaWz(M#j#JXu6%kke?hKP$^KkMq=g9O z3NgGDLUQiV3@uOr)Pv;U-`R_&_~|?n`BRk(Xm$qyCuK3$JQWyT^|%hKDxu*-GV)@) zqhCk}rKRfkOL_Mc57sjHR11WYiULDEL3!3{O?zaT&MKKA(&lIkBat>%TE?f0@~*jz zlLdov(3QCjI*Rp#IQ4hCK_f@w0s%SV&rqhI8OTK;8c+<14G)IH#&_?G()hVg8Ef0( z!qB&nHdHD5h%{t2H7(7h70cGRvp&B5ZDvCHn|cEVLEvMe%7#V}lu9r61eEqh{k2Ry z?Lcb&NzJ@H4KJ^LDZihKSH!g6s*i40=zBF-VEVrXz1OGnzcLkch0yoZc#%@5c3D)s z<5fnH2VcX7Zwq1g<7w8!^ePe-%lNThEMrxoOO=e=j$_coHc zX}PUZ9M+Au&2&y{hUy6B6JA3tenZTKNhs)Z}OR-^U~f7TDIdOmdz2h~|5GZ^_tF4i{ zC^mQ^N4~_P;cyiHO0LAw&$G4P8btj%N8ye4<+>V%dQ<1kX_2?{J^pmQ(eaomz2*-5 zmvc2ftcZFlX6{7`$|_ds%-0@v({K_}MRd0lgYX!P=N6n`JzbLp&*vfyo*Ht-WIk^i zUP=kI$|@T+%2!4$1vBNe?+f{XEm?C33jVv@yPC%?I59#kP0*h9nD{bace_N7{*o8F zcm=wdebU*Ob$45Ls7J7cndsA#*7HqFQa`W=(g2Q?G0W#N|FWT}%}k#saH7OHtdg~p zhCvFMT4rlVHQXl~c+IJ4&EyQej?tSYXyqw)Z49YzGZViHYUA!aJ$UrYBo`Abbuxf5 zQ3Vxd^nC8waml|>@l-0Kq)JFSVQSRNP=<=F8P-m`K!2`x)Wpr@al0f z<;i_n>=G0JU}~`5K?5^phY_qB3wd}1rNeFGo}INu(y%GG+PUWkq&&{#OeCws=dCx; z;YogTNIjK|c9*J1WojOJh%CM2=Dz-h1y7ymPkih|KcvD~iYbbIeGod8iI>p9)OaC{ zZNPr=$pn*?;L=*3~rHvsjzw(pbCAo2mKV(pPUtU){FN@ZYy&u8!gQ z4c~os$9HQzcOq=NRv0@R?ya|2A0booT}n*0>sGz*ekk}ZfoLD7P5|1zgW$!}?J@rw z6jb0#_$k`C^|tYcxPw1c{?vj%gU&52{3+*735O*auycRkB7sMF7j+CEqfRZ&M^O?78$qRw=p&DF_sF85f$)s=a4Nu)vBQSzdpy|UQ8pI)Br?zm~;004`|@~qW|7`4w0?~Xp!yIy@xMJ}N}F?xtm3OfHfm8r#@802V<2Mcka<~m#T7_iAOE1=xf2#@Nswjo9mB$Bob;XgG)1#Hw@_O zL%$ky{BOqr2lNc-COYjR<}dL*`ik9x!?0qHTv{*CVI7fNJXX+EUezBDKjJH6&}k$I z?H-!$T|nXl8b!g-AUBggBwSDIys(s6x6H62r@5B}%Y~g`#r-P2s46|La(EXN%B_Xf zd^VHGpypri{wvQ`6mujW=(To&{zTG*dspTxm0BdWvlHrh^E?HIYxd*hnk`i-2JBL4 zp&s00-bSNTnW4SEP&7(sr!fv^m|-i8-8W;SjN>8KELos)_sNxe4coF9bm%s}QYR|U zfexN<*N$#}KgxnHL&4U{WA1#yD3*m`MlTo+X0~4McH@5X*HO^s6ulw6vcKUnlZ1w-e7WWps-PU2ZdH$1^rKzpHQ=ys&L_v0 zj;@zwW@Ue(R0aq;yxlMEhlv)|eu=9HdoHD?ypJFmk<%3L1o{^f+Q?-^}*3r>Ho2b;mqX$KIG#tSH z+kMmhQ`@jhZtn4c`I|;{A`e#6-8=X$R5|*tR5@x;WtI+y>|o?je@0+3TP!7q8+W48^NAZMHb(__#Bro%m&LdGcyfN>hJEJ{!WnN7(S-V%&GVgQiZ^EbODVJzh837KLwV=l4KpWT_4&8V zoaU^>OlBDX>(R|Z1zsvx)F$dx7Cyu+T8}5mcQDvMzki(EILg8Fsl&xP3JjooC-sS$ zqsriMl8cZZ9ojqMbaq&->TS>xf~w^KJ1)S=URC)Xvux@v;~pFe{=ah3sa7d0r9b+r zPi=^Mh#dzIepr_(;OchYzCng?VEbNvXgn%fkghuq#SKU=&kNaiY|sauisZ9Uw!

      {;QCp*GaP*&o)=>j3 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

      ;jRNQNl;8q>CMsjgXv9lhe_O=C-KY5ONee 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

      ud-tta`YeXag&u~(#-eEF&9_>1 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 literal 0 HcmV?d00001 diff --git a/vendor/github.com/cilium/charts/cilium-1.18.0-pre.0.tgz b/vendor/github.com/cilium/charts/cilium-1.18.0-pre.0.tgz new file mode 100644 index 0000000000000000000000000000000000000000..e5ebaddd820ea5f268a10844b4e63edeb9be6348 GIT binary patch literal 223919 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®Sx&kKt1bW2c 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%0D~mQOby3LxajZ1C~H>x*HmD5W(7@U40y3 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 literal 0 HcmV?d00001 diff --git a/vendor/github.com/cilium/charts/index.yaml b/vendor/github.com/cilium/charts/index.yaml index 86c23d64f8..2e59c5335a 100644 --- a/vendor/github.com/cilium/charts/index.yaml +++ b/vendor/github.com/cilium/charts/index.yaml @@ -1,6 +1,745 @@ apiVersion: v1 entries: cilium: + - annotations: + artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n + \ displayName: Cilium Network Policy\n description: |\n Cilium Network + Policies provide additional functionality beyond what\n is provided by + standard Kubernetes NetworkPolicy such as the ability\n to allow traffic + based on FQDNs, or to filter at Layer 7.\n- kind: CiliumClusterwideNetworkPolicy\n + \ version: v2\n name: ciliumclusterwidenetworkpolicies.cilium.io\n displayName: + Cilium Clusterwide Network Policy\n description: |\n Cilium Clusterwide + Network Policies support configuring network traffic\n policiies across + the entire cluster, including applying node firewalls.\n- kind: CiliumLocalRedirectPolicy\n + \ version: v2\n name: ciliumlocalredirectpolicies.cilium.io\n displayName: + Cilium Local Redirect Policy\n description: |\n Cilium Local Redirect + Policy allows local redirects to be configured\n within a node to support + use cases like Node-Local DNS or KIAM.\n- kind: CiliumNode\n version: v2\n + \ name: ciliumnodes.cilium.io\n displayName: Cilium Node\n description: + |\n Cilium Node represents a node managed by Cilium. It contains a\n specification + to control various node specific configuration aspects\n and a status section + to represent the status of the node.\n- kind: CiliumIdentity\n version: v2\n + \ name: ciliumidentities.cilium.io\n displayName: Cilium Identity\n description: + |\n Cilium Identity allows introspection into security identities that\n + \ Cilium allocates which identify sets of labels that are assigned to\n + \ individual endpoints in the cluster.\n- kind: CiliumEndpoint\n version: + v2\n name: ciliumendpoints.cilium.io\n displayName: Cilium Endpoint\n description: + |\n Cilium Endpoint represents the status of individual pods or nodes in\n + \ the cluster which are managed by Cilium, including enforcement status,\n + \ IP addressing and whether the networking is successfully operational.\n- + kind: CiliumEndpointSlice\n version: v2alpha1\n name: ciliumendpointslices.cilium.io\n + \ displayName: Cilium Endpoint Slice\n description: |\n Cilium Endpoint + Slice represents the status of groups of pods or nodes\n in the cluster + which are managed by Cilium, including enforcement status,\n IP addressing + and whether the networking is successfully operational.\n- kind: CiliumEgressGatewayPolicy\n + \ version: v2\n name: ciliumegressgatewaypolicies.cilium.io\n displayName: + Cilium Egress Gateway Policy\n description: |\n Cilium Egress Gateway + Policy provides control over the way that traffic\n leaves the cluster + and which source addresses to use for that traffic.\n- kind: CiliumClusterwideEnvoyConfig\n + \ version: v2\n name: ciliumclusterwideenvoyconfigs.cilium.io\n displayName: + Cilium Clusterwide Envoy Config\n description: |\n Cilium Clusterwide + Envoy Config specifies Envoy resources and K8s service mappings\n to be + provisioned into Cilium host proxy instances in cluster context.\n- kind: + CiliumEnvoyConfig\n version: v2\n name: ciliumenvoyconfigs.cilium.io\n displayName: + Cilium Envoy Config\n description: |\n Cilium Envoy Config specifies Envoy + resources and K8s service mappings\n to be provisioned into Cilium host + proxy instances in namespace context.\n- kind: CiliumNodeConfig\n version: + v2\n name: ciliumnodeconfigs.cilium.io\n displayName: Cilium Node Configuration\n + \ description: |\n CiliumNodeConfig is a list of configuration key-value + pairs. It is applied to\n nodes indicated by a label selector.\n- kind: + CiliumBGPPeeringPolicy\n version: v2alpha1\n name: ciliumbgppeeringpolicies.cilium.io\n + \ displayName: Cilium BGP Peering Policy\n description: |\n Cilium BGP + Peering Policy instructs Cilium to create specific BGP peering\n configurations.\n- + kind: CiliumBGPClusterConfig\n version: v2alpha1\n name: ciliumbgpclusterconfigs.cilium.io\n + \ displayName: Cilium BGP Cluster Config\n description: |\n Cilium BGP + Cluster Config instructs Cilium operator to create specific BGP cluster\n + \ configurations.\n- kind: CiliumBGPPeerConfig\n version: v2alpha1\n name: + ciliumbgppeerconfigs.cilium.io\n displayName: Cilium BGP Peer Config\n description: + |\n CiliumBGPPeerConfig is a common set of BGP peer configurations. It + can be referenced \n by multiple peers from CiliumBGPClusterConfig.\n- + kind: CiliumBGPAdvertisement\n version: v2alpha1\n name: ciliumbgpadvertisements.cilium.io\n + \ displayName: Cilium BGP Advertisement\n description: |\n CiliumBGPAdvertisement + is used to define source of BGP advertisement as well as BGP attributes \n + \ to be advertised with those prefixes.\n- kind: CiliumBGPNodeConfig\n version: + v2alpha1\n name: ciliumbgpnodeconfigs.cilium.io\n displayName: Cilium BGP + Node Config\n description: |\n CiliumBGPNodeConfig is read only node specific + BGP configuration. It is constructed by Cilium operator.\n It will also + contain node local BGP state information.\n- kind: CiliumBGPNodeConfigOverride\n + \ version: v2alpha1\n name: ciliumbgpnodeconfigoverrides.cilium.io\n displayName: + Cilium BGP Node Config Override\n description: |\n CiliumBGPNodeConfigOverride + can be used to override node specific BGP configuration.\n- kind: CiliumLoadBalancerIPPool\n + \ version: v2alpha1\n name: ciliumloadbalancerippools.cilium.io\n displayName: + Cilium Load Balancer IP Pool\n description: |\n Defining a Cilium Load + Balancer IP Pool instructs Cilium to assign IPs to LoadBalancer Services.\n- + kind: CiliumCIDRGroup\n version: v2alpha1\n name: ciliumcidrgroups.cilium.io\n + \ displayName: Cilium CIDR Group\n description: |\n CiliumCIDRGroup is + a list of CIDRs that can be referenced as a single entity from CiliumNetworkPolicies.\n- + kind: CiliumL2AnnouncementPolicy\n version: v2alpha1\n name: ciliuml2announcementpolicies.cilium.io\n + \ displayName: Cilium L2 Announcement Policy\n description: |\n CiliumL2AnnouncementPolicy + is a policy which determines which service IPs will be announced to\n the + local area network, by which nodes, and via which interfaces.\n- kind: CiliumPodIPPool\n + \ version: v2alpha1\n name: ciliumpodippools.cilium.io\n displayName: Cilium + Pod IP Pool\n description: |\n CiliumPodIPPool defines an IP pool that + can be used for pooled IPAM (i.e. the multi-pool IPAM mode).\n- kind: CiliumGatewayClassConfig\n + \ version: v2alpha1\n name: ciliumgatewayclassconfigs.cilium.io\n displayName: + Cilium Gateway Class Config\n description: |\n CiliumGatewayClassConfig + defines a configuration for Gateway API GatewayClass.\n" + apiVersion: v2 + appVersion: 1.18.0-pre.0 + created: "2025-03-03T21:20:37.844470429Z" + description: eBPF-based Networking, Security, and Observability + digest: 156903126f0df9f75ceea05c678a038966f32756542a8935f9275d179b31a330 + home: https://cilium.io/ + icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg + keywords: + - BPF + - eBPF + - Kubernetes + - Networking + - Security + - Observability + - Troubleshooting + kubeVersion: '>= 1.21.0-0' + name: cilium + sources: + - https://github.com/cilium/cilium + urls: + - cilium-1.18.0-pre.0.tgz + version: 1.18.0-pre.0 + - annotations: + artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n + \ displayName: Cilium Network Policy\n description: |\n Cilium Network + Policies provide additional functionality beyond what\n is provided by + standard Kubernetes NetworkPolicy such as the ability\n to allow traffic + based on FQDNs, or to filter at Layer 7.\n- kind: CiliumClusterwideNetworkPolicy\n + \ version: v2\n name: ciliumclusterwidenetworkpolicies.cilium.io\n displayName: + Cilium Clusterwide Network Policy\n description: |\n Cilium Clusterwide + Network Policies support configuring network traffic\n policiies across + the entire cluster, including applying node firewalls.\n- kind: CiliumExternalWorkload\n + \ version: v2\n name: ciliumexternalworkloads.cilium.io\n displayName: Cilium + External Workload\n description: |\n Cilium External Workload supports + configuring the ability for external\n non-Kubernetes workloads to join + the cluster.\n- kind: CiliumLocalRedirectPolicy\n version: v2\n name: ciliumlocalredirectpolicies.cilium.io\n + \ displayName: Cilium Local Redirect Policy\n description: |\n Cilium + Local Redirect Policy allows local redirects to be configured\n within + a node to support use cases like Node-Local DNS or KIAM.\n- kind: CiliumNode\n + \ version: v2\n name: ciliumnodes.cilium.io\n displayName: Cilium Node\n + \ description: |\n Cilium Node represents a node managed by Cilium. It + contains a\n specification to control various node specific configuration + aspects\n and a status section to represent the status of the node.\n- + kind: CiliumIdentity\n version: v2\n name: ciliumidentities.cilium.io\n + \ displayName: Cilium Identity\n description: |\n Cilium Identity allows + introspection into security identities that\n Cilium allocates which identify + sets of labels that are assigned to\n individual endpoints in the cluster.\n- + kind: CiliumEndpoint\n version: v2\n name: ciliumendpoints.cilium.io\n displayName: + Cilium Endpoint\n description: |\n Cilium Endpoint represents the status + of individual pods or nodes in\n the cluster which are managed by Cilium, + including enforcement status,\n IP addressing and whether the networking + is successfully operational.\n- kind: CiliumEndpointSlice\n version: v2alpha1\n + \ name: ciliumendpointslices.cilium.io\n displayName: Cilium Endpoint Slice\n + \ description: |\n Cilium Endpoint Slice represents the status of groups + of pods or nodes\n in the cluster which are managed by Cilium, including + enforcement status,\n IP addressing and whether the networking is successfully + operational.\n- kind: CiliumEgressGatewayPolicy\n version: v2\n name: ciliumegressgatewaypolicies.cilium.io\n + \ displayName: Cilium Egress Gateway Policy\n description: |\n Cilium + Egress Gateway Policy provides control over the way that traffic\n leaves + the cluster and which source addresses to use for that traffic.\n- kind: CiliumClusterwideEnvoyConfig\n + \ version: v2\n name: ciliumclusterwideenvoyconfigs.cilium.io\n displayName: + Cilium Clusterwide Envoy Config\n description: |\n Cilium Clusterwide + Envoy Config specifies Envoy resources and K8s service mappings\n to be + provisioned into Cilium host proxy instances in cluster context.\n- kind: + CiliumEnvoyConfig\n version: v2\n name: ciliumenvoyconfigs.cilium.io\n displayName: + Cilium Envoy Config\n description: |\n Cilium Envoy Config specifies Envoy + resources and K8s service mappings\n to be provisioned into Cilium host + proxy instances in namespace context.\n- kind: CiliumNodeConfig\n version: + v2\n name: ciliumnodeconfigs.cilium.io\n displayName: Cilium Node Configuration\n + \ description: |\n CiliumNodeConfig is a list of configuration key-value + pairs. It is applied to\n nodes indicated by a label selector.\n- kind: + CiliumBGPPeeringPolicy\n version: v2alpha1\n name: ciliumbgppeeringpolicies.cilium.io\n + \ displayName: Cilium BGP Peering Policy\n description: |\n Cilium BGP + Peering Policy instructs Cilium to create specific BGP peering\n configurations.\n- + kind: CiliumBGPClusterConfig\n version: v2alpha1\n name: ciliumbgpclusterconfigs.cilium.io\n + \ displayName: Cilium BGP Cluster Config\n description: |\n Cilium BGP + Cluster Config instructs Cilium operator to create specific BGP cluster\n + \ configurations.\n- kind: CiliumBGPPeerConfig\n version: v2alpha1\n name: + ciliumbgppeerconfigs.cilium.io\n displayName: Cilium BGP Peer Config\n description: + |\n CiliumBGPPeerConfig is a common set of BGP peer configurations. It + can be referenced \n by multiple peers from CiliumBGPClusterConfig.\n- + kind: CiliumBGPAdvertisement\n version: v2alpha1\n name: ciliumbgpadvertisements.cilium.io\n + \ displayName: Cilium BGP Advertisement\n description: |\n CiliumBGPAdvertisement + is used to define source of BGP advertisement as well as BGP attributes \n + \ to be advertised with those prefixes.\n- kind: CiliumBGPNodeConfig\n version: + v2alpha1\n name: ciliumbgpnodeconfigs.cilium.io\n displayName: Cilium BGP + Node Config\n description: |\n CiliumBGPNodeConfig is read only node specific + BGP configuration. It is constructed by Cilium operator.\n It will also + contain node local BGP state information.\n- kind: CiliumBGPNodeConfigOverride\n + \ version: v2alpha1\n name: ciliumbgpnodeconfigoverrides.cilium.io\n displayName: + Cilium BGP Node Config Override\n description: |\n CiliumBGPNodeConfigOverride + can be used to override node specific BGP configuration.\n- kind: CiliumLoadBalancerIPPool\n + \ version: v2alpha1\n name: ciliumloadbalancerippools.cilium.io\n displayName: + Cilium Load Balancer IP Pool\n description: |\n Defining a Cilium Load + Balancer IP Pool instructs Cilium to assign IPs to LoadBalancer Services.\n- + kind: CiliumCIDRGroup\n version: v2alpha1\n name: ciliumcidrgroups.cilium.io\n + \ displayName: Cilium CIDR Group\n description: |\n CiliumCIDRGroup is + a list of CIDRs that can be referenced as a single entity from CiliumNetworkPolicies.\n- + kind: CiliumL2AnnouncementPolicy\n version: v2alpha1\n name: ciliuml2announcementpolicies.cilium.io\n + \ displayName: Cilium L2 Announcement Policy\n description: |\n CiliumL2AnnouncementPolicy + is a policy which determines which service IPs will be announced to\n the + local area network, by which nodes, and via which interfaces.\n- kind: CiliumPodIPPool\n + \ version: v2alpha1\n name: ciliumpodippools.cilium.io\n displayName: Cilium + Pod IP Pool\n description: |\n CiliumPodIPPool defines an IP pool that + can be used for pooled IPAM (i.e. the multi-pool IPAM mode).\n" + apiVersion: v2 + appVersion: 1.17.2 + created: "2025-03-15T13:57:29.915973514Z" + description: eBPF-based Networking, Security, and Observability + digest: 1fa89da0c6e4f1a5ac54eb1c17ea61150e6cb59476cdf19cd30b8c5f93eae5cb + home: https://cilium.io/ + icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg + keywords: + - BPF + - eBPF + - Kubernetes + - Networking + - Security + - Observability + - Troubleshooting + kubeVersion: '>= 1.21.0-0' + name: cilium + sources: + - https://github.com/cilium/cilium + urls: + - cilium-1.17.2.tgz + version: 1.17.2 + - annotations: + artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n + \ displayName: Cilium Network Policy\n description: |\n Cilium Network + Policies provide additional functionality beyond what\n is provided by + standard Kubernetes NetworkPolicy such as the ability\n to allow traffic + based on FQDNs, or to filter at Layer 7.\n- kind: CiliumClusterwideNetworkPolicy\n + \ version: v2\n name: ciliumclusterwidenetworkpolicies.cilium.io\n displayName: + Cilium Clusterwide Network Policy\n description: |\n Cilium Clusterwide + Network Policies support configuring network traffic\n policiies across + the entire cluster, including applying node firewalls.\n- kind: CiliumExternalWorkload\n + \ version: v2\n name: ciliumexternalworkloads.cilium.io\n displayName: Cilium + External Workload\n description: |\n Cilium External Workload supports + configuring the ability for external\n non-Kubernetes workloads to join + the cluster.\n- kind: CiliumLocalRedirectPolicy\n version: v2\n name: ciliumlocalredirectpolicies.cilium.io\n + \ displayName: Cilium Local Redirect Policy\n description: |\n Cilium + Local Redirect Policy allows local redirects to be configured\n within + a node to support use cases like Node-Local DNS or KIAM.\n- kind: CiliumNode\n + \ version: v2\n name: ciliumnodes.cilium.io\n displayName: Cilium Node\n + \ description: |\n Cilium Node represents a node managed by Cilium. It + contains a\n specification to control various node specific configuration + aspects\n and a status section to represent the status of the node.\n- + kind: CiliumIdentity\n version: v2\n name: ciliumidentities.cilium.io\n + \ displayName: Cilium Identity\n description: |\n Cilium Identity allows + introspection into security identities that\n Cilium allocates which identify + sets of labels that are assigned to\n individual endpoints in the cluster.\n- + kind: CiliumEndpoint\n version: v2\n name: ciliumendpoints.cilium.io\n displayName: + Cilium Endpoint\n description: |\n Cilium Endpoint represents the status + of individual pods or nodes in\n the cluster which are managed by Cilium, + including enforcement status,\n IP addressing and whether the networking + is successfully operational.\n- kind: CiliumEndpointSlice\n version: v2alpha1\n + \ name: ciliumendpointslices.cilium.io\n displayName: Cilium Endpoint Slice\n + \ description: |\n Cilium Endpoint Slice represents the status of groups + of pods or nodes\n in the cluster which are managed by Cilium, including + enforcement status,\n IP addressing and whether the networking is successfully + operational.\n- kind: CiliumEgressGatewayPolicy\n version: v2\n name: ciliumegressgatewaypolicies.cilium.io\n + \ displayName: Cilium Egress Gateway Policy\n description: |\n Cilium + Egress Gateway Policy provides control over the way that traffic\n leaves + the cluster and which source addresses to use for that traffic.\n- kind: CiliumClusterwideEnvoyConfig\n + \ version: v2\n name: ciliumclusterwideenvoyconfigs.cilium.io\n displayName: + Cilium Clusterwide Envoy Config\n description: |\n Cilium Clusterwide + Envoy Config specifies Envoy resources and K8s service mappings\n to be + provisioned into Cilium host proxy instances in cluster context.\n- kind: + CiliumEnvoyConfig\n version: v2\n name: ciliumenvoyconfigs.cilium.io\n displayName: + Cilium Envoy Config\n description: |\n Cilium Envoy Config specifies Envoy + resources and K8s service mappings\n to be provisioned into Cilium host + proxy instances in namespace context.\n- kind: CiliumNodeConfig\n version: + v2\n name: ciliumnodeconfigs.cilium.io\n displayName: Cilium Node Configuration\n + \ description: |\n CiliumNodeConfig is a list of configuration key-value + pairs. It is applied to\n nodes indicated by a label selector.\n- kind: + CiliumBGPPeeringPolicy\n version: v2alpha1\n name: ciliumbgppeeringpolicies.cilium.io\n + \ displayName: Cilium BGP Peering Policy\n description: |\n Cilium BGP + Peering Policy instructs Cilium to create specific BGP peering\n configurations.\n- + kind: CiliumBGPClusterConfig\n version: v2alpha1\n name: ciliumbgpclusterconfigs.cilium.io\n + \ displayName: Cilium BGP Cluster Config\n description: |\n Cilium BGP + Cluster Config instructs Cilium operator to create specific BGP cluster\n + \ configurations.\n- kind: CiliumBGPPeerConfig\n version: v2alpha1\n name: + ciliumbgppeerconfigs.cilium.io\n displayName: Cilium BGP Peer Config\n description: + |\n CiliumBGPPeerConfig is a common set of BGP peer configurations. It + can be referenced \n by multiple peers from CiliumBGPClusterConfig.\n- + kind: CiliumBGPAdvertisement\n version: v2alpha1\n name: ciliumbgpadvertisements.cilium.io\n + \ displayName: Cilium BGP Advertisement\n description: |\n CiliumBGPAdvertisement + is used to define source of BGP advertisement as well as BGP attributes \n + \ to be advertised with those prefixes.\n- kind: CiliumBGPNodeConfig\n version: + v2alpha1\n name: ciliumbgpnodeconfigs.cilium.io\n displayName: Cilium BGP + Node Config\n description: |\n CiliumBGPNodeConfig is read only node specific + BGP configuration. It is constructed by Cilium operator.\n It will also + contain node local BGP state information.\n- kind: CiliumBGPNodeConfigOverride\n + \ version: v2alpha1\n name: ciliumbgpnodeconfigoverrides.cilium.io\n displayName: + Cilium BGP Node Config Override\n description: |\n CiliumBGPNodeConfigOverride + can be used to override node specific BGP configuration.\n- kind: CiliumLoadBalancerIPPool\n + \ version: v2alpha1\n name: ciliumloadbalancerippools.cilium.io\n displayName: + Cilium Load Balancer IP Pool\n description: |\n Defining a Cilium Load + Balancer IP Pool instructs Cilium to assign IPs to LoadBalancer Services.\n- + kind: CiliumCIDRGroup\n version: v2alpha1\n name: ciliumcidrgroups.cilium.io\n + \ displayName: Cilium CIDR Group\n description: |\n CiliumCIDRGroup is + a list of CIDRs that can be referenced as a single entity from CiliumNetworkPolicies.\n- + kind: CiliumL2AnnouncementPolicy\n version: v2alpha1\n name: ciliuml2announcementpolicies.cilium.io\n + \ displayName: Cilium L2 Announcement Policy\n description: |\n CiliumL2AnnouncementPolicy + is a policy which determines which service IPs will be announced to\n the + local area network, by which nodes, and via which interfaces.\n- kind: CiliumPodIPPool\n + \ version: v2alpha1\n name: ciliumpodippools.cilium.io\n displayName: Cilium + Pod IP Pool\n description: |\n CiliumPodIPPool defines an IP pool that + can be used for pooled IPAM (i.e. the multi-pool IPAM mode).\n" + apiVersion: v2 + appVersion: 1.17.1 + created: "2025-02-13T09:21:06.335209602Z" + description: eBPF-based Networking, Security, and Observability + digest: 381de4f8f4c5eace677d3426aa8d896ef8d2318c2bf4d1172c9953345b744471 + home: https://cilium.io/ + icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg + keywords: + - BPF + - eBPF + - Kubernetes + - Networking + - Security + - Observability + - Troubleshooting + kubeVersion: '>= 1.21.0-0' + name: cilium + sources: + - https://github.com/cilium/cilium + urls: + - cilium-1.17.1.tgz + version: 1.17.1 + - annotations: + artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n + \ displayName: Cilium Network Policy\n description: |\n Cilium Network + Policies provide additional functionality beyond what\n is provided by + standard Kubernetes NetworkPolicy such as the ability\n to allow traffic + based on FQDNs, or to filter at Layer 7.\n- kind: CiliumClusterwideNetworkPolicy\n + \ version: v2\n name: ciliumclusterwidenetworkpolicies.cilium.io\n displayName: + Cilium Clusterwide Network Policy\n description: |\n Cilium Clusterwide + Network Policies support configuring network traffic\n policiies across + the entire cluster, including applying node firewalls.\n- kind: CiliumExternalWorkload\n + \ version: v2\n name: ciliumexternalworkloads.cilium.io\n displayName: Cilium + External Workload\n description: |\n Cilium External Workload supports + configuring the ability for external\n non-Kubernetes workloads to join + the cluster.\n- kind: CiliumLocalRedirectPolicy\n version: v2\n name: ciliumlocalredirectpolicies.cilium.io\n + \ displayName: Cilium Local Redirect Policy\n description: |\n Cilium + Local Redirect Policy allows local redirects to be configured\n within + a node to support use cases like Node-Local DNS or KIAM.\n- kind: CiliumNode\n + \ version: v2\n name: ciliumnodes.cilium.io\n displayName: Cilium Node\n + \ description: |\n Cilium Node represents a node managed by Cilium. It + contains a\n specification to control various node specific configuration + aspects\n and a status section to represent the status of the node.\n- + kind: CiliumIdentity\n version: v2\n name: ciliumidentities.cilium.io\n + \ displayName: Cilium Identity\n description: |\n Cilium Identity allows + introspection into security identities that\n Cilium allocates which identify + sets of labels that are assigned to\n individual endpoints in the cluster.\n- + kind: CiliumEndpoint\n version: v2\n name: ciliumendpoints.cilium.io\n displayName: + Cilium Endpoint\n description: |\n Cilium Endpoint represents the status + of individual pods or nodes in\n the cluster which are managed by Cilium, + including enforcement status,\n IP addressing and whether the networking + is successfully operational.\n- kind: CiliumEndpointSlice\n version: v2alpha1\n + \ name: ciliumendpointslices.cilium.io\n displayName: Cilium Endpoint Slice\n + \ description: |\n Cilium Endpoint Slice represents the status of groups + of pods or nodes\n in the cluster which are managed by Cilium, including + enforcement status,\n IP addressing and whether the networking is successfully + operational.\n- kind: CiliumEgressGatewayPolicy\n version: v2\n name: ciliumegressgatewaypolicies.cilium.io\n + \ displayName: Cilium Egress Gateway Policy\n description: |\n Cilium + Egress Gateway Policy provides control over the way that traffic\n leaves + the cluster and which source addresses to use for that traffic.\n- kind: CiliumClusterwideEnvoyConfig\n + \ version: v2\n name: ciliumclusterwideenvoyconfigs.cilium.io\n displayName: + Cilium Clusterwide Envoy Config\n description: |\n Cilium Clusterwide + Envoy Config specifies Envoy resources and K8s service mappings\n to be + provisioned into Cilium host proxy instances in cluster context.\n- kind: + CiliumEnvoyConfig\n version: v2\n name: ciliumenvoyconfigs.cilium.io\n displayName: + Cilium Envoy Config\n description: |\n Cilium Envoy Config specifies Envoy + resources and K8s service mappings\n to be provisioned into Cilium host + proxy instances in namespace context.\n- kind: CiliumBGPPeeringPolicy\n version: + v2alpha1\n name: ciliumbgppeeringpolicies.cilium.io\n displayName: Cilium + BGP Peering Policy\n description: |\n Cilium BGP Peering Policy instructs + Cilium to create specific BGP peering\n configurations.\n- kind: CiliumBGPClusterConfig\n + \ version: v2alpha1\n name: ciliumbgpclusterconfigs.cilium.io\n displayName: + Cilium BGP Cluster Config\n description: |\n Cilium BGP Cluster Config + instructs Cilium operator to create specific BGP cluster\n configurations.\n- + kind: CiliumBGPPeerConfig\n version: v2alpha1\n name: ciliumbgppeerconfigs.cilium.io\n + \ displayName: Cilium BGP Peer Config\n description: |\n CiliumBGPPeerConfig + is a common set of BGP peer configurations. It can be referenced \n by + multiple peers from CiliumBGPClusterConfig.\n- kind: CiliumBGPAdvertisement\n + \ version: v2alpha1\n name: ciliumbgpadvertisements.cilium.io\n displayName: + Cilium BGP Advertisement\n description: |\n CiliumBGPAdvertisement is + used to define source of BGP advertisement as well as BGP attributes \n to + be advertised with those prefixes.\n- kind: CiliumBGPNodeConfig\n version: + v2alpha1\n name: ciliumbgpnodeconfigs.cilium.io\n displayName: Cilium BGP + Node Config\n description: |\n CiliumBGPNodeConfig is read only node specific + BGP configuration. It is constructed by Cilium operator.\n It will also + contain node local BGP state information.\n- kind: CiliumBGPNodeConfigOverride\n + \ version: v2alpha1\n name: ciliumbgpnodeconfigoverrides.cilium.io\n displayName: + Cilium BGP Node Config Override\n description: |\n CiliumBGPNodeConfigOverride + can be used to override node specific BGP configuration.\n- kind: CiliumLoadBalancerIPPool\n + \ version: v2alpha1\n name: ciliumloadbalancerippools.cilium.io\n displayName: + Cilium Load Balancer IP Pool\n description: |\n Defining a Cilium Load + Balancer IP Pool instructs Cilium to assign IPs to LoadBalancer Services.\n- + kind: CiliumNodeConfig\n version: v2alpha1\n name: ciliumnodeconfigs.cilium.io\n + \ displayName: Cilium Node Configuration\n description: |\n CiliumNodeConfig + is a list of configuration key-value pairs. It is applied to\n nodes indicated + by a label selector.\n- kind: CiliumCIDRGroup\n version: v2alpha1\n name: + ciliumcidrgroups.cilium.io\n displayName: Cilium CIDR Group\n description: + |\n CiliumCIDRGroup is a list of CIDRs that can be referenced as a single + entity from CiliumNetworkPolicies.\n- kind: CiliumL2AnnouncementPolicy\n version: + v2alpha1\n name: ciliuml2announcementpolicies.cilium.io\n displayName: Cilium + L2 Announcement Policy\n description: |\n CiliumL2AnnouncementPolicy is + a policy which determines which service IPs will be announced to\n the + local area network, by which nodes, and via which interfaces.\n- kind: CiliumPodIPPool\n + \ version: v2alpha1\n name: ciliumpodippools.cilium.io\n displayName: Cilium + Pod IP Pool\n description: |\n CiliumPodIPPool defines an IP pool that + can be used for pooled IPAM (i.e. the multi-pool IPAM mode).\n" + apiVersion: v2 + appVersion: 1.17.0 + created: "2025-02-04T15:44:02.158877648Z" + description: eBPF-based Networking, Security, and Observability + digest: 72a820bf01bb3e02c01856892a0508da92dfc94174f8705c7bcb5dbb15e228fe + home: https://cilium.io/ + icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg + keywords: + - BPF + - eBPF + - Kubernetes + - Networking + - Security + - Observability + - Troubleshooting + kubeVersion: '>= 1.21.0-0' + name: cilium + sources: + - https://github.com/cilium/cilium + urls: + - cilium-1.17.0.tgz + version: 1.17.0 + - annotations: + artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n + \ displayName: Cilium Network Policy\n description: |\n Cilium Network + Policies provide additional functionality beyond what\n is provided by + standard Kubernetes NetworkPolicy such as the ability\n to allow traffic + based on FQDNs, or to filter at Layer 7.\n- kind: CiliumClusterwideNetworkPolicy\n + \ version: v2\n name: ciliumclusterwidenetworkpolicies.cilium.io\n displayName: + Cilium Clusterwide Network Policy\n description: |\n Cilium Clusterwide + Network Policies support configuring network traffic\n policiies across + the entire cluster, including applying node firewalls.\n- kind: CiliumExternalWorkload\n + \ version: v2\n name: ciliumexternalworkloads.cilium.io\n displayName: Cilium + External Workload\n description: |\n Cilium External Workload supports + configuring the ability for external\n non-Kubernetes workloads to join + the cluster.\n- kind: CiliumLocalRedirectPolicy\n version: v2\n name: ciliumlocalredirectpolicies.cilium.io\n + \ displayName: Cilium Local Redirect Policy\n description: |\n Cilium + Local Redirect Policy allows local redirects to be configured\n within + a node to support use cases like Node-Local DNS or KIAM.\n- kind: CiliumNode\n + \ version: v2\n name: ciliumnodes.cilium.io\n displayName: Cilium Node\n + \ description: |\n Cilium Node represents a node managed by Cilium. It + contains a\n specification to control various node specific configuration + aspects\n and a status section to represent the status of the node.\n- + kind: CiliumIdentity\n version: v2\n name: ciliumidentities.cilium.io\n + \ displayName: Cilium Identity\n description: |\n Cilium Identity allows + introspection into security identities that\n Cilium allocates which identify + sets of labels that are assigned to\n individual endpoints in the cluster.\n- + kind: CiliumEndpoint\n version: v2\n name: ciliumendpoints.cilium.io\n displayName: + Cilium Endpoint\n description: |\n Cilium Endpoint represents the status + of individual pods or nodes in\n the cluster which are managed by Cilium, + including enforcement status,\n IP addressing and whether the networking + is successfully operational.\n- kind: CiliumEndpointSlice\n version: v2alpha1\n + \ name: ciliumendpointslices.cilium.io\n displayName: Cilium Endpoint Slice\n + \ description: |\n Cilium Endpoint Slice represents the status of groups + of pods or nodes\n in the cluster which are managed by Cilium, including + enforcement status,\n IP addressing and whether the networking is successfully + operational.\n- kind: CiliumEgressGatewayPolicy\n version: v2\n name: ciliumegressgatewaypolicies.cilium.io\n + \ displayName: Cilium Egress Gateway Policy\n description: |\n Cilium + Egress Gateway Policy provides control over the way that traffic\n leaves + the cluster and which source addresses to use for that traffic.\n- kind: CiliumClusterwideEnvoyConfig\n + \ version: v2\n name: ciliumclusterwideenvoyconfigs.cilium.io\n displayName: + Cilium Clusterwide Envoy Config\n description: |\n Cilium Clusterwide + Envoy Config specifies Envoy resources and K8s service mappings\n to be + provisioned into Cilium host proxy instances in cluster context.\n- kind: + CiliumEnvoyConfig\n version: v2\n name: ciliumenvoyconfigs.cilium.io\n displayName: + Cilium Envoy Config\n description: |\n Cilium Envoy Config specifies Envoy + resources and K8s service mappings\n to be provisioned into Cilium host + proxy instances in namespace context.\n- kind: CiliumBGPPeeringPolicy\n version: + v2alpha1\n name: ciliumbgppeeringpolicies.cilium.io\n displayName: Cilium + BGP Peering Policy\n description: |\n Cilium BGP Peering Policy instructs + Cilium to create specific BGP peering\n configurations.\n- kind: CiliumBGPClusterConfig\n + \ version: v2alpha1\n name: ciliumbgpclusterconfigs.cilium.io\n displayName: + Cilium BGP Cluster Config\n description: |\n Cilium BGP Cluster Config + instructs Cilium operator to create specific BGP cluster\n configurations.\n- + kind: CiliumBGPPeerConfig\n version: v2alpha1\n name: ciliumbgppeerconfigs.cilium.io\n + \ displayName: Cilium BGP Peer Config\n description: |\n CiliumBGPPeerConfig + is a common set of BGP peer configurations. It can be referenced \n by + multiple peers from CiliumBGPClusterConfig.\n- kind: CiliumBGPAdvertisement\n + \ version: v2alpha1\n name: ciliumbgpadvertisements.cilium.io\n displayName: + Cilium BGP Advertisement\n description: |\n CiliumBGPAdvertisement is + used to define source of BGP advertisement as well as BGP attributes \n to + be advertised with those prefixes.\n- kind: CiliumBGPNodeConfig\n version: + v2alpha1\n name: ciliumbgpnodeconfigs.cilium.io\n displayName: Cilium BGP + Node Config\n description: |\n CiliumBGPNodeConfig is read only node specific + BGP configuration. It is constructed by Cilium operator.\n It will also + contain node local BGP state information.\n- kind: CiliumBGPNodeConfigOverride\n + \ version: v2alpha1\n name: ciliumbgpnodeconfigoverrides.cilium.io\n displayName: + Cilium BGP Node Config Override\n description: |\n CiliumBGPNodeConfigOverride + can be used to override node specific BGP configuration.\n- kind: CiliumLoadBalancerIPPool\n + \ version: v2alpha1\n name: ciliumloadbalancerippools.cilium.io\n displayName: + Cilium Load Balancer IP Pool\n description: |\n Defining a Cilium Load + Balancer IP Pool instructs Cilium to assign IPs to LoadBalancer Services.\n- + kind: CiliumNodeConfig\n version: v2alpha1\n name: ciliumnodeconfigs.cilium.io\n + \ displayName: Cilium Node Configuration\n description: |\n CiliumNodeConfig + is a list of configuration key-value pairs. It is applied to\n nodes indicated + by a label selector.\n- kind: CiliumCIDRGroup\n version: v2alpha1\n name: + ciliumcidrgroups.cilium.io\n displayName: Cilium CIDR Group\n description: + |\n CiliumCIDRGroup is a list of CIDRs that can be referenced as a single + entity from CiliumNetworkPolicies.\n- kind: CiliumL2AnnouncementPolicy\n version: + v2alpha1\n name: ciliuml2announcementpolicies.cilium.io\n displayName: Cilium + L2 Announcement Policy\n description: |\n CiliumL2AnnouncementPolicy is + a policy which determines which service IPs will be announced to\n the + local area network, by which nodes, and via which interfaces.\n- kind: CiliumPodIPPool\n + \ version: v2alpha1\n name: ciliumpodippools.cilium.io\n displayName: Cilium + Pod IP Pool\n description: |\n CiliumPodIPPool defines an IP pool that + can be used for pooled IPAM (i.e. the multi-pool IPAM mode).\n" + apiVersion: v2 + appVersion: 1.17.0-rc.2 + created: "2025-01-24T16:21:25.188869058Z" + description: eBPF-based Networking, Security, and Observability + digest: 7fccfe9f3977241a5890f2a429b7ac8e4a778edf56d08fd833773c43b4496dbb + home: https://cilium.io/ + icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg + keywords: + - BPF + - eBPF + - Kubernetes + - Networking + - Security + - Observability + - Troubleshooting + kubeVersion: '>= 1.21.0-0' + name: cilium + sources: + - https://github.com/cilium/cilium + urls: + - cilium-1.17.0-rc.2.tgz + version: 1.17.0-rc.2 + - annotations: + artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n + \ displayName: Cilium Network Policy\n description: |\n Cilium Network + Policies provide additional functionality beyond what\n is provided by + standard Kubernetes NetworkPolicy such as the ability\n to allow traffic + based on FQDNs, or to filter at Layer 7.\n- kind: CiliumClusterwideNetworkPolicy\n + \ version: v2\n name: ciliumclusterwidenetworkpolicies.cilium.io\n displayName: + Cilium Clusterwide Network Policy\n description: |\n Cilium Clusterwide + Network Policies support configuring network traffic\n policiies across + the entire cluster, including applying node firewalls.\n- kind: CiliumExternalWorkload\n + \ version: v2\n name: ciliumexternalworkloads.cilium.io\n displayName: Cilium + External Workload\n description: |\n Cilium External Workload supports + configuring the ability for external\n non-Kubernetes workloads to join + the cluster.\n- kind: CiliumLocalRedirectPolicy\n version: v2\n name: ciliumlocalredirectpolicies.cilium.io\n + \ displayName: Cilium Local Redirect Policy\n description: |\n Cilium + Local Redirect Policy allows local redirects to be configured\n within + a node to support use cases like Node-Local DNS or KIAM.\n- kind: CiliumNode\n + \ version: v2\n name: ciliumnodes.cilium.io\n displayName: Cilium Node\n + \ description: |\n Cilium Node represents a node managed by Cilium. It + contains a\n specification to control various node specific configuration + aspects\n and a status section to represent the status of the node.\n- + kind: CiliumIdentity\n version: v2\n name: ciliumidentities.cilium.io\n + \ displayName: Cilium Identity\n description: |\n Cilium Identity allows + introspection into security identities that\n Cilium allocates which identify + sets of labels that are assigned to\n individual endpoints in the cluster.\n- + kind: CiliumEndpoint\n version: v2\n name: ciliumendpoints.cilium.io\n displayName: + Cilium Endpoint\n description: |\n Cilium Endpoint represents the status + of individual pods or nodes in\n the cluster which are managed by Cilium, + including enforcement status,\n IP addressing and whether the networking + is successfully operational.\n- kind: CiliumEndpointSlice\n version: v2alpha1\n + \ name: ciliumendpointslices.cilium.io\n displayName: Cilium Endpoint Slice\n + \ description: |\n Cilium Endpoint Slice represents the status of groups + of pods or nodes\n in the cluster which are managed by Cilium, including + enforcement status,\n IP addressing and whether the networking is successfully + operational.\n- kind: CiliumEgressGatewayPolicy\n version: v2\n name: ciliumegressgatewaypolicies.cilium.io\n + \ displayName: Cilium Egress Gateway Policy\n description: |\n Cilium + Egress Gateway Policy provides control over the way that traffic\n leaves + the cluster and which source addresses to use for that traffic.\n- kind: CiliumClusterwideEnvoyConfig\n + \ version: v2\n name: ciliumclusterwideenvoyconfigs.cilium.io\n displayName: + Cilium Clusterwide Envoy Config\n description: |\n Cilium Clusterwide + Envoy Config specifies Envoy resources and K8s service mappings\n to be + provisioned into Cilium host proxy instances in cluster context.\n- kind: + CiliumEnvoyConfig\n version: v2\n name: ciliumenvoyconfigs.cilium.io\n displayName: + Cilium Envoy Config\n description: |\n Cilium Envoy Config specifies Envoy + resources and K8s service mappings\n to be provisioned into Cilium host + proxy instances in namespace context.\n- kind: CiliumBGPPeeringPolicy\n version: + v2alpha1\n name: ciliumbgppeeringpolicies.cilium.io\n displayName: Cilium + BGP Peering Policy\n description: |\n Cilium BGP Peering Policy instructs + Cilium to create specific BGP peering\n configurations.\n- kind: CiliumBGPClusterConfig\n + \ version: v2alpha1\n name: ciliumbgpclusterconfigs.cilium.io\n displayName: + Cilium BGP Cluster Config\n description: |\n Cilium BGP Cluster Config + instructs Cilium operator to create specific BGP cluster\n configurations.\n- + kind: CiliumBGPPeerConfig\n version: v2alpha1\n name: ciliumbgppeerconfigs.cilium.io\n + \ displayName: Cilium BGP Peer Config\n description: |\n CiliumBGPPeerConfig + is a common set of BGP peer configurations. It can be referenced \n by + multiple peers from CiliumBGPClusterConfig.\n- kind: CiliumBGPAdvertisement\n + \ version: v2alpha1\n name: ciliumbgpadvertisements.cilium.io\n displayName: + Cilium BGP Advertisement\n description: |\n CiliumBGPAdvertisement is + used to define source of BGP advertisement as well as BGP attributes \n to + be advertised with those prefixes.\n- kind: CiliumBGPNodeConfig\n version: + v2alpha1\n name: ciliumbgpnodeconfigs.cilium.io\n displayName: Cilium BGP + Node Config\n description: |\n CiliumBGPNodeConfig is read only node specific + BGP configuration. It is constructed by Cilium operator.\n It will also + contain node local BGP state information.\n- kind: CiliumBGPNodeConfigOverride\n + \ version: v2alpha1\n name: ciliumbgpnodeconfigoverrides.cilium.io\n displayName: + Cilium BGP Node Config Override\n description: |\n CiliumBGPNodeConfigOverride + can be used to override node specific BGP configuration.\n- kind: CiliumLoadBalancerIPPool\n + \ version: v2alpha1\n name: ciliumloadbalancerippools.cilium.io\n displayName: + Cilium Load Balancer IP Pool\n description: |\n Defining a Cilium Load + Balancer IP Pool instructs Cilium to assign IPs to LoadBalancer Services.\n- + kind: CiliumNodeConfig\n version: v2alpha1\n name: ciliumnodeconfigs.cilium.io\n + \ displayName: Cilium Node Configuration\n description: |\n CiliumNodeConfig + is a list of configuration key-value pairs. It is applied to\n nodes indicated + by a label selector.\n- kind: CiliumCIDRGroup\n version: v2alpha1\n name: + ciliumcidrgroups.cilium.io\n displayName: Cilium CIDR Group\n description: + |\n CiliumCIDRGroup is a list of CIDRs that can be referenced as a single + entity from CiliumNetworkPolicies.\n- kind: CiliumL2AnnouncementPolicy\n version: + v2alpha1\n name: ciliuml2announcementpolicies.cilium.io\n displayName: Cilium + L2 Announcement Policy\n description: |\n CiliumL2AnnouncementPolicy is + a policy which determines which service IPs will be announced to\n the + local area network, by which nodes, and via which interfaces.\n- kind: CiliumPodIPPool\n + \ version: v2alpha1\n name: ciliumpodippools.cilium.io\n displayName: Cilium + Pod IP Pool\n description: |\n CiliumPodIPPool defines an IP pool that + can be used for pooled IPAM (i.e. the multi-pool IPAM mode).\n" + apiVersion: v2 + appVersion: 1.17.0-rc.1 + created: "2025-01-10T22:01:41.151165757Z" + description: eBPF-based Networking, Security, and Observability + digest: a36edb4958505bff7ecb2605050fa8773d194a1ea7846e3bcc7824a9a8f5e987 + home: https://cilium.io/ + icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg + keywords: + - BPF + - eBPF + - Kubernetes + - Networking + - Security + - Observability + - Troubleshooting + kubeVersion: '>= 1.21.0-0' + name: cilium + sources: + - https://github.com/cilium/cilium + urls: + - cilium-1.17.0-rc.1.tgz + version: 1.17.0-rc.1 + - annotations: + artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n + \ displayName: Cilium Network Policy\n description: |\n Cilium Network + Policies provide additional functionality beyond what\n is provided by + standard Kubernetes NetworkPolicy such as the ability\n to allow traffic + based on FQDNs, or to filter at Layer 7.\n- kind: CiliumClusterwideNetworkPolicy\n + \ version: v2\n name: ciliumclusterwidenetworkpolicies.cilium.io\n displayName: + Cilium Clusterwide Network Policy\n description: |\n Cilium Clusterwide + Network Policies support configuring network traffic\n policiies across + the entire cluster, including applying node firewalls.\n- kind: CiliumExternalWorkload\n + \ version: v2\n name: ciliumexternalworkloads.cilium.io\n displayName: Cilium + External Workload\n description: |\n Cilium External Workload supports + configuring the ability for external\n non-Kubernetes workloads to join + the cluster.\n- kind: CiliumLocalRedirectPolicy\n version: v2\n name: ciliumlocalredirectpolicies.cilium.io\n + \ displayName: Cilium Local Redirect Policy\n description: |\n Cilium + Local Redirect Policy allows local redirects to be configured\n within + a node to support use cases like Node-Local DNS or KIAM.\n- kind: CiliumNode\n + \ version: v2\n name: ciliumnodes.cilium.io\n displayName: Cilium Node\n + \ description: |\n Cilium Node represents a node managed by Cilium. It + contains a\n specification to control various node specific configuration + aspects\n and a status section to represent the status of the node.\n- + kind: CiliumIdentity\n version: v2\n name: ciliumidentities.cilium.io\n + \ displayName: Cilium Identity\n description: |\n Cilium Identity allows + introspection into security identities that\n Cilium allocates which identify + sets of labels that are assigned to\n individual endpoints in the cluster.\n- + kind: CiliumEndpoint\n version: v2\n name: ciliumendpoints.cilium.io\n displayName: + Cilium Endpoint\n description: |\n Cilium Endpoint represents the status + of individual pods or nodes in\n the cluster which are managed by Cilium, + including enforcement status,\n IP addressing and whether the networking + is successfully operational.\n- kind: CiliumEndpointSlice\n version: v2alpha1\n + \ name: ciliumendpointslices.cilium.io\n displayName: Cilium Endpoint Slice\n + \ description: |\n Cilium Endpoint Slice represents the status of groups + of pods or nodes\n in the cluster which are managed by Cilium, including + enforcement status,\n IP addressing and whether the networking is successfully + operational.\n- kind: CiliumEgressGatewayPolicy\n version: v2\n name: ciliumegressgatewaypolicies.cilium.io\n + \ displayName: Cilium Egress Gateway Policy\n description: |\n Cilium + Egress Gateway Policy provides control over the way that traffic\n leaves + the cluster and which source addresses to use for that traffic.\n- kind: CiliumClusterwideEnvoyConfig\n + \ version: v2\n name: ciliumclusterwideenvoyconfigs.cilium.io\n displayName: + Cilium Clusterwide Envoy Config\n description: |\n Cilium Clusterwide + Envoy Config specifies Envoy resources and K8s service mappings\n to be + provisioned into Cilium host proxy instances in cluster context.\n- kind: + CiliumEnvoyConfig\n version: v2\n name: ciliumenvoyconfigs.cilium.io\n displayName: + Cilium Envoy Config\n description: |\n Cilium Envoy Config specifies Envoy + resources and K8s service mappings\n to be provisioned into Cilium host + proxy instances in namespace context.\n- kind: CiliumBGPPeeringPolicy\n version: + v2alpha1\n name: ciliumbgppeeringpolicies.cilium.io\n displayName: Cilium + BGP Peering Policy\n description: |\n Cilium BGP Peering Policy instructs + Cilium to create specific BGP peering\n configurations.\n- kind: CiliumBGPClusterConfig\n + \ version: v2alpha1\n name: ciliumbgpclusterconfigs.cilium.io\n displayName: + Cilium BGP Cluster Config\n description: |\n Cilium BGP Cluster Config + instructs Cilium operator to create specific BGP cluster\n configurations.\n- + kind: CiliumBGPPeerConfig\n version: v2alpha1\n name: ciliumbgppeerconfigs.cilium.io\n + \ displayName: Cilium BGP Peer Config\n description: |\n CiliumBGPPeerConfig + is a common set of BGP peer configurations. It can be referenced \n by + multiple peers from CiliumBGPClusterConfig.\n- kind: CiliumBGPAdvertisement\n + \ version: v2alpha1\n name: ciliumbgpadvertisements.cilium.io\n displayName: + Cilium BGP Advertisement\n description: |\n CiliumBGPAdvertisement is + used to define source of BGP advertisement as well as BGP attributes \n to + be advertised with those prefixes.\n- kind: CiliumBGPNodeConfig\n version: + v2alpha1\n name: ciliumbgpnodeconfigs.cilium.io\n displayName: Cilium BGP + Node Config\n description: |\n CiliumBGPNodeConfig is read only node specific + BGP configuration. It is constructed by Cilium operator.\n It will also + contain node local BGP state information.\n- kind: CiliumBGPNodeConfigOverride\n + \ version: v2alpha1\n name: ciliumbgpnodeconfigoverrides.cilium.io\n displayName: + Cilium BGP Node Config Override\n description: |\n CiliumBGPNodeConfigOverride + can be used to override node specific BGP configuration.\n- kind: CiliumLoadBalancerIPPool\n + \ version: v2alpha1\n name: ciliumloadbalancerippools.cilium.io\n displayName: + Cilium Load Balancer IP Pool\n description: |\n Defining a Cilium Load + Balancer IP Pool instructs Cilium to assign IPs to LoadBalancer Services.\n- + kind: CiliumNodeConfig\n version: v2alpha1\n name: ciliumnodeconfigs.cilium.io\n + \ displayName: Cilium Node Configuration\n description: |\n CiliumNodeConfig + is a list of configuration key-value pairs. It is applied to\n nodes indicated + by a label selector.\n- kind: CiliumCIDRGroup\n version: v2alpha1\n name: + ciliumcidrgroups.cilium.io\n displayName: Cilium CIDR Group\n description: + |\n CiliumCIDRGroup is a list of CIDRs that can be referenced as a single + entity from CiliumNetworkPolicies.\n- kind: CiliumL2AnnouncementPolicy\n version: + v2alpha1\n name: ciliuml2announcementpolicies.cilium.io\n displayName: Cilium + L2 Announcement Policy\n description: |\n CiliumL2AnnouncementPolicy is + a policy which determines which service IPs will be announced to\n the + local area network, by which nodes, and via which interfaces.\n- kind: CiliumPodIPPool\n + \ version: v2alpha1\n name: ciliumpodippools.cilium.io\n displayName: Cilium + Pod IP Pool\n description: |\n CiliumPodIPPool defines an IP pool that + can be used for pooled IPAM (i.e. the multi-pool IPAM mode).\n" + apiVersion: v2 + appVersion: 1.17.0-rc.0 + created: "2024-12-18T15:38:28.700108392Z" + description: eBPF-based Networking, Security, and Observability + digest: f2930855d7602f1729f52c871d48879d47b86c928dab4d53029876fa96717995 + home: https://cilium.io/ + icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg + keywords: + - BPF + - eBPF + - Kubernetes + - Networking + - Security + - Observability + - Troubleshooting + kubeVersion: '>= 1.21.0-0' + name: cilium + sources: + - https://github.com/cilium/cilium + urls: + - cilium-1.17.0-rc.0.tgz + version: 1.17.0-rc.0 - annotations: artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n \ displayName: Cilium Network Policy\n description: |\n Cilium Network @@ -86,10 +825,10 @@ entries: Pod IP Pool\n description: |\n CiliumPodIPPool defines an IP pool that can be used for pooled IPAM (i.e. the multi-pool IPAM mode).\n" apiVersion: v2 - appVersion: 1.17.0 - created: "2025-02-04T15:44:02.158877648Z" + appVersion: 1.17.0-pre.3 + created: "2024-12-02T17:17:27.250000272Z" description: eBPF-based Networking, Security, and Observability - digest: 72a820bf01bb3e02c01856892a0508da92dfc94174f8705c7bcb5dbb15e228fe + digest: 20481d2f5d85ee1b68237aac4210e8e0e1615b037e6ecf3b845fb8bdad254110 home: https://cilium.io/ icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg keywords: @@ -105,8 +844,8 @@ entries: sources: - https://github.com/cilium/cilium urls: - - cilium-1.17.0.tgz - version: 1.17.0 + - cilium-1.17.0-pre.3.tgz + version: 1.17.0-pre.3 - annotations: artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n \ displayName: Cilium Network Policy\n description: |\n Cilium Network @@ -192,10 +931,10 @@ entries: Pod IP Pool\n description: |\n CiliumPodIPPool defines an IP pool that can be used for pooled IPAM (i.e. the multi-pool IPAM mode).\n" apiVersion: v2 - appVersion: 1.17.0-rc.2 - created: "2025-01-24T16:21:25.188869058Z" + appVersion: 1.17.0-pre.2 + created: "2024-11-01T22:55:20.834191869Z" description: eBPF-based Networking, Security, and Observability - digest: 7fccfe9f3977241a5890f2a429b7ac8e4a778edf56d08fd833773c43b4496dbb + digest: e51e734bb6e08f34629373dc1a1e6c1bec88a6166083c057b4ace69e4709c3ef home: https://cilium.io/ icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg keywords: @@ -211,8 +950,8 @@ entries: sources: - https://github.com/cilium/cilium urls: - - cilium-1.17.0-rc.2.tgz - version: 1.17.0-rc.2 + - cilium-1.17.0-pre.2.tgz + version: 1.17.0-pre.2 - annotations: artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n \ displayName: Cilium Network Policy\n description: |\n Cilium Network @@ -298,10 +1037,10 @@ entries: Pod IP Pool\n description: |\n CiliumPodIPPool defines an IP pool that can be used for pooled IPAM (i.e. the multi-pool IPAM mode).\n" apiVersion: v2 - appVersion: 1.17.0-rc.1 - created: "2025-01-10T22:01:41.151165757Z" + appVersion: 1.17.0-pre.1 + created: "2024-10-01T08:15:06.105239065Z" description: eBPF-based Networking, Security, and Observability - digest: a36edb4958505bff7ecb2605050fa8773d194a1ea7846e3bcc7824a9a8f5e987 + digest: 414555ed00b250bbaa9edff538d1da68504a557f1c0fd165edca313bfade2d3a home: https://cilium.io/ icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg keywords: @@ -317,8 +1056,8 @@ entries: sources: - https://github.com/cilium/cilium urls: - - cilium-1.17.0-rc.1.tgz - version: 1.17.0-rc.1 + - cilium-1.17.0-pre.1.tgz + version: 1.17.0-pre.1 - annotations: artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n \ displayName: Cilium Network Policy\n description: |\n Cilium Network @@ -404,10 +1143,10 @@ entries: Pod IP Pool\n description: |\n CiliumPodIPPool defines an IP pool that can be used for pooled IPAM (i.e. the multi-pool IPAM mode).\n" apiVersion: v2 - appVersion: 1.17.0-rc.0 - created: "2024-12-18T15:38:28.700108392Z" + appVersion: 1.17.0-pre.0 + created: "2024-09-05T09:21:38.425895279Z" description: eBPF-based Networking, Security, and Observability - digest: f2930855d7602f1729f52c871d48879d47b86c928dab4d53029876fa96717995 + digest: b09c94dfee569ff7426af691ff7e5c5620df6187993f9f29155106160fa8be85 home: https://cilium.io/ icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg keywords: @@ -423,8 +1162,8 @@ entries: sources: - https://github.com/cilium/cilium urls: - - cilium-1.17.0-rc.0.tgz - version: 1.17.0-rc.0 + - cilium-1.17.0-pre.0.tgz + version: 1.17.0-pre.0 - annotations: artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n \ displayName: Cilium Network Policy\n description: |\n Cilium Network @@ -510,10 +1249,10 @@ entries: Pod IP Pool\n description: |\n CiliumPodIPPool defines an IP pool that can be used for pooled IPAM (i.e. the multi-pool IPAM mode).\n" apiVersion: v2 - appVersion: 1.17.0-pre.3 - created: "2024-12-02T17:17:27.250000272Z" + appVersion: 1.16.8 + created: "2025-03-15T13:59:35.84623681Z" description: eBPF-based Networking, Security, and Observability - digest: 20481d2f5d85ee1b68237aac4210e8e0e1615b037e6ecf3b845fb8bdad254110 + digest: 4075f908730db69b53c7aec23b5d2f53582e8991b5ebecbd895190391f3baa40 home: https://cilium.io/ icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg keywords: @@ -529,8 +1268,8 @@ entries: sources: - https://github.com/cilium/cilium urls: - - cilium-1.17.0-pre.3.tgz - version: 1.17.0-pre.3 + - cilium-1.16.8.tgz + version: 1.16.8 - annotations: artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n \ displayName: Cilium Network Policy\n description: |\n Cilium Network @@ -616,10 +1355,10 @@ entries: Pod IP Pool\n description: |\n CiliumPodIPPool defines an IP pool that can be used for pooled IPAM (i.e. the multi-pool IPAM mode).\n" apiVersion: v2 - appVersion: 1.17.0-pre.2 - created: "2024-11-01T22:55:20.834191869Z" + appVersion: 1.16.7 + created: "2025-02-18T16:41:40.827787438Z" description: eBPF-based Networking, Security, and Observability - digest: e51e734bb6e08f34629373dc1a1e6c1bec88a6166083c057b4ace69e4709c3ef + digest: 9abc7d0b713e496d3b76c7191e06cd13bf425a2a7517c4fb4c353cff2a169336 home: https://cilium.io/ icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg keywords: @@ -635,8 +1374,8 @@ entries: sources: - https://github.com/cilium/cilium urls: - - cilium-1.17.0-pre.2.tgz - version: 1.17.0-pre.2 + - cilium-1.16.7.tgz + version: 1.16.7 - annotations: artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n \ displayName: Cilium Network Policy\n description: |\n Cilium Network @@ -722,10 +1461,10 @@ entries: Pod IP Pool\n description: |\n CiliumPodIPPool defines an IP pool that can be used for pooled IPAM (i.e. the multi-pool IPAM mode).\n" apiVersion: v2 - appVersion: 1.17.0-pre.1 - created: "2024-10-01T08:15:06.105239065Z" + appVersion: 1.16.6 + created: "2025-01-22T00:49:12.609546824Z" description: eBPF-based Networking, Security, and Observability - digest: 414555ed00b250bbaa9edff538d1da68504a557f1c0fd165edca313bfade2d3a + digest: 99990b032e98124740138de055a473e5edaa9c5622e6be59b2f6d1733eb943d8 home: https://cilium.io/ icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg keywords: @@ -741,8 +1480,8 @@ entries: sources: - https://github.com/cilium/cilium urls: - - cilium-1.17.0-pre.1.tgz - version: 1.17.0-pre.1 + - cilium-1.16.6.tgz + version: 1.16.6 - annotations: artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n \ displayName: Cilium Network Policy\n description: |\n Cilium Network @@ -828,10 +1567,10 @@ entries: Pod IP Pool\n description: |\n CiliumPodIPPool defines an IP pool that can be used for pooled IPAM (i.e. the multi-pool IPAM mode).\n" apiVersion: v2 - appVersion: 1.17.0-pre.0 - created: "2024-09-05T09:21:38.425895279Z" + appVersion: 1.16.5 + created: "2024-12-17T23:57:07.182234567Z" description: eBPF-based Networking, Security, and Observability - digest: b09c94dfee569ff7426af691ff7e5c5620df6187993f9f29155106160fa8be85 + digest: 182035ae7c2cf8d19490ececf618f6a11adc460ed1e7dbd38b23065f02f18b4d home: https://cilium.io/ icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg keywords: @@ -847,8 +1586,8 @@ entries: sources: - https://github.com/cilium/cilium urls: - - cilium-1.17.0-pre.0.tgz - version: 1.17.0-pre.0 + - cilium-1.16.5.tgz + version: 1.16.5 - annotations: artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n \ displayName: Cilium Network Policy\n description: |\n Cilium Network @@ -934,10 +1673,10 @@ entries: Pod IP Pool\n description: |\n CiliumPodIPPool defines an IP pool that can be used for pooled IPAM (i.e. the multi-pool IPAM mode).\n" apiVersion: v2 - appVersion: 1.16.6 - created: "2025-01-22T00:49:12.609546824Z" + appVersion: 1.16.4 + created: "2024-11-20T09:05:38.752908124Z" description: eBPF-based Networking, Security, and Observability - digest: 99990b032e98124740138de055a473e5edaa9c5622e6be59b2f6d1733eb943d8 + digest: e96a1fba0f361926bb29e6bc2fe565d5fe36be9c5194fad85e8ed2e828d09864 home: https://cilium.io/ icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg keywords: @@ -953,8 +1692,8 @@ entries: sources: - https://github.com/cilium/cilium urls: - - cilium-1.16.6.tgz - version: 1.16.6 + - cilium-1.16.4.tgz + version: 1.16.4 - annotations: artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n \ displayName: Cilium Network Policy\n description: |\n Cilium Network @@ -1040,10 +1779,10 @@ entries: Pod IP Pool\n description: |\n CiliumPodIPPool defines an IP pool that can be used for pooled IPAM (i.e. the multi-pool IPAM mode).\n" apiVersion: v2 - appVersion: 1.16.5 - created: "2024-12-17T23:57:07.182234567Z" + appVersion: 1.16.3 + created: "2024-10-11T23:02:59.494005644Z" description: eBPF-based Networking, Security, and Observability - digest: 182035ae7c2cf8d19490ececf618f6a11adc460ed1e7dbd38b23065f02f18b4d + digest: e1be328218c74bd2bed91f996d8c1b10e785715ce53299a392f79b0cef796805 home: https://cilium.io/ icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg keywords: @@ -1059,8 +1798,8 @@ entries: sources: - https://github.com/cilium/cilium urls: - - cilium-1.16.5.tgz - version: 1.16.5 + - cilium-1.16.3.tgz + version: 1.16.3 - annotations: artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n \ displayName: Cilium Network Policy\n description: |\n Cilium Network @@ -1146,10 +1885,10 @@ entries: Pod IP Pool\n description: |\n CiliumPodIPPool defines an IP pool that can be used for pooled IPAM (i.e. the multi-pool IPAM mode).\n" apiVersion: v2 - appVersion: 1.16.4 - created: "2024-11-20T09:05:38.752908124Z" + appVersion: 1.16.2 + created: "2024-09-26T11:51:56.311361Z" description: eBPF-based Networking, Security, and Observability - digest: e96a1fba0f361926bb29e6bc2fe565d5fe36be9c5194fad85e8ed2e828d09864 + digest: 5379083a8c3fe16c5fafe7c02205e9a0a0f51943afa64e172cb719fb17919077 home: https://cilium.io/ icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg keywords: @@ -1165,8 +1904,8 @@ entries: sources: - https://github.com/cilium/cilium urls: - - cilium-1.16.4.tgz - version: 1.16.4 + - cilium-1.16.2.tgz + version: 1.16.2 - annotations: artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n \ displayName: Cilium Network Policy\n description: |\n Cilium Network @@ -1252,10 +1991,10 @@ entries: Pod IP Pool\n description: |\n CiliumPodIPPool defines an IP pool that can be used for pooled IPAM (i.e. the multi-pool IPAM mode).\n" apiVersion: v2 - appVersion: 1.16.3 - created: "2024-10-11T23:02:59.494005644Z" + appVersion: 1.16.1 + created: "2024-08-14T12:57:36.976823113Z" description: eBPF-based Networking, Security, and Observability - digest: e1be328218c74bd2bed91f996d8c1b10e785715ce53299a392f79b0cef796805 + digest: 406c5bba515262c52e53b859af31af412cd8d2c332e277e98ec3e5f14382ecbf home: https://cilium.io/ icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg keywords: @@ -1271,8 +2010,8 @@ entries: sources: - https://github.com/cilium/cilium urls: - - cilium-1.16.3.tgz - version: 1.16.3 + - cilium-1.16.1.tgz + version: 1.16.1 - annotations: artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n \ displayName: Cilium Network Policy\n description: |\n Cilium Network @@ -1358,10 +2097,10 @@ entries: Pod IP Pool\n description: |\n CiliumPodIPPool defines an IP pool that can be used for pooled IPAM (i.e. the multi-pool IPAM mode).\n" apiVersion: v2 - appVersion: 1.16.2 - created: "2024-09-26T11:51:56.311361Z" + appVersion: 1.16.0 + created: "2024-07-24T15:00:46.978038359Z" description: eBPF-based Networking, Security, and Observability - digest: 5379083a8c3fe16c5fafe7c02205e9a0a0f51943afa64e172cb719fb17919077 + digest: 2d653f4826722da976791047f7f3d9e999526590ea4df2f36a3846aedbffae2d home: https://cilium.io/ icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg keywords: @@ -1377,8 +2116,8 @@ entries: sources: - https://github.com/cilium/cilium urls: - - cilium-1.16.2.tgz - version: 1.16.2 + - cilium-1.16.0.tgz + version: 1.16.0 - annotations: artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n \ displayName: Cilium Network Policy\n description: |\n Cilium Network @@ -1464,10 +2203,10 @@ entries: Pod IP Pool\n description: |\n CiliumPodIPPool defines an IP pool that can be used for pooled IPAM (i.e. the multi-pool IPAM mode).\n" apiVersion: v2 - appVersion: 1.16.1 - created: "2024-08-14T12:57:36.976823113Z" + appVersion: 1.16.0-rc.2 + created: "2024-07-15T22:46:03.276445381Z" description: eBPF-based Networking, Security, and Observability - digest: 406c5bba515262c52e53b859af31af412cd8d2c332e277e98ec3e5f14382ecbf + digest: 2ef4401a1ced8966aa1388cabacf9e127538d113399b14d66f1fb726ac6210a9 home: https://cilium.io/ icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg keywords: @@ -1483,8 +2222,8 @@ entries: sources: - https://github.com/cilium/cilium urls: - - cilium-1.16.1.tgz - version: 1.16.1 + - cilium-1.16.0-rc.2.tgz + version: 1.16.0-rc.2 - annotations: artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n \ displayName: Cilium Network Policy\n description: |\n Cilium Network @@ -1570,10 +2309,10 @@ entries: Pod IP Pool\n description: |\n CiliumPodIPPool defines an IP pool that can be used for pooled IPAM (i.e. the multi-pool IPAM mode).\n" apiVersion: v2 - appVersion: 1.16.0 - created: "2024-07-24T15:00:46.978038359Z" + appVersion: 1.16.0-rc.1 + created: "2024-06-28T22:27:40.613256035Z" description: eBPF-based Networking, Security, and Observability - digest: 2d653f4826722da976791047f7f3d9e999526590ea4df2f36a3846aedbffae2d + digest: 81759b91f03f0cbda6b7d81077f69ecaf9c92408c5aa6ef94291287c8995aac5 home: https://cilium.io/ icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg keywords: @@ -1589,8 +2328,8 @@ entries: sources: - https://github.com/cilium/cilium urls: - - cilium-1.16.0.tgz - version: 1.16.0 + - cilium-1.16.0-rc.1.tgz + version: 1.16.0-rc.1 - annotations: artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n \ displayName: Cilium Network Policy\n description: |\n Cilium Network @@ -1676,10 +2415,10 @@ entries: Pod IP Pool\n description: |\n CiliumPodIPPool defines an IP pool that can be used for pooled IPAM (i.e. the multi-pool IPAM mode).\n" apiVersion: v2 - appVersion: 1.16.0-rc.2 - created: "2024-07-15T22:46:03.276445381Z" + appVersion: 1.16.0-rc.0 + created: "2024-06-17T19:48:16.546067407Z" description: eBPF-based Networking, Security, and Observability - digest: 2ef4401a1ced8966aa1388cabacf9e127538d113399b14d66f1fb726ac6210a9 + digest: 779b4714b0184b9f4936f3eed323446e5c2b100f6f69e30a90a147760238f635 home: https://cilium.io/ icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg keywords: @@ -1695,8 +2434,8 @@ entries: sources: - https://github.com/cilium/cilium urls: - - cilium-1.16.0-rc.2.tgz - version: 1.16.0-rc.2 + - cilium-1.16.0-rc.0.tgz + version: 1.16.0-rc.0 - annotations: artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n \ displayName: Cilium Network Policy\n description: |\n Cilium Network @@ -1782,10 +2521,10 @@ entries: Pod IP Pool\n description: |\n CiliumPodIPPool defines an IP pool that can be used for pooled IPAM (i.e. the multi-pool IPAM mode).\n" apiVersion: v2 - appVersion: 1.16.0-rc.1 - created: "2024-06-28T22:27:40.613256035Z" + appVersion: 1.16.0-pre.3 + created: "2024-06-03T23:06:37.960751658Z" description: eBPF-based Networking, Security, and Observability - digest: 81759b91f03f0cbda6b7d81077f69ecaf9c92408c5aa6ef94291287c8995aac5 + digest: 0ac735de66666556bf59c3bb42efee57b6b29a8256cdd11ce85fd77ec85e9beb home: https://cilium.io/ icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg keywords: @@ -1801,8 +2540,8 @@ entries: sources: - https://github.com/cilium/cilium urls: - - cilium-1.16.0-rc.1.tgz - version: 1.16.0-rc.1 + - cilium-1.16.0-pre.3.tgz + version: 1.16.0-pre.3 - annotations: artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n \ displayName: Cilium Network Policy\n description: |\n Cilium Network @@ -1888,10 +2627,10 @@ entries: Pod IP Pool\n description: |\n CiliumPodIPPool defines an IP pool that can be used for pooled IPAM (i.e. the multi-pool IPAM mode).\n" apiVersion: v2 - appVersion: 1.16.0-rc.0 - created: "2024-06-17T19:48:16.546067407Z" + appVersion: 1.16.0-pre.2 + created: "2024-05-03T00:03:03.380234662Z" description: eBPF-based Networking, Security, and Observability - digest: 779b4714b0184b9f4936f3eed323446e5c2b100f6f69e30a90a147760238f635 + digest: 9b78652906c1984675d981fa11bc640eb8e888155331720d062acff86425ad39 home: https://cilium.io/ icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg keywords: @@ -1907,8 +2646,8 @@ entries: sources: - https://github.com/cilium/cilium urls: - - cilium-1.16.0-rc.0.tgz - version: 1.16.0-rc.0 + - cilium-1.16.0-pre.2.tgz + version: 1.16.0-pre.2 - annotations: artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n \ displayName: Cilium Network Policy\n description: |\n Cilium Network @@ -1994,10 +2733,10 @@ entries: Pod IP Pool\n description: |\n CiliumPodIPPool defines an IP pool that can be used for pooled IPAM (i.e. the multi-pool IPAM mode).\n" apiVersion: v2 - appVersion: 1.16.0-pre.3 - created: "2024-06-03T23:06:37.960751658Z" + appVersion: 1.16.0-pre.1 + created: "2024-04-02T20:02:25-07:00" description: eBPF-based Networking, Security, and Observability - digest: 0ac735de66666556bf59c3bb42efee57b6b29a8256cdd11ce85fd77ec85e9beb + digest: 646df1ba01ddd9262dab5f38b7ed2bccd509af6418889245bc101e4d481db352 home: https://cilium.io/ icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg keywords: @@ -2013,8 +2752,8 @@ entries: sources: - https://github.com/cilium/cilium urls: - - cilium-1.16.0-pre.3.tgz - version: 1.16.0-pre.3 + - cilium-1.16.0-pre.1.tgz + version: 1.16.0-pre.1 - annotations: artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n \ displayName: Cilium Network Policy\n description: |\n Cilium Network @@ -2100,10 +2839,10 @@ entries: Pod IP Pool\n description: |\n CiliumPodIPPool defines an IP pool that can be used for pooled IPAM (i.e. the multi-pool IPAM mode).\n" apiVersion: v2 - appVersion: 1.16.0-pre.2 - created: "2024-05-03T00:03:03.380234662Z" + appVersion: 1.16.0-pre.0 + created: "2024-03-04T13:27:22+01:00" description: eBPF-based Networking, Security, and Observability - digest: 9b78652906c1984675d981fa11bc640eb8e888155331720d062acff86425ad39 + digest: 4a02367277a1d32d68f45f6fc2c29be6fa22397f1b99cd72a6be5aacf2c78a04 home: https://cilium.io/ icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg keywords: @@ -2114,13 +2853,13 @@ entries: - Security - Observability - Troubleshooting - kubeVersion: '>= 1.21.0-0' + kubeVersion: '>= 1.16.0-0' name: cilium sources: - https://github.com/cilium/cilium urls: - - cilium-1.16.0-pre.2.tgz - version: 1.16.0-pre.2 + - cilium-1.16.0-pre.0.tgz + version: 1.16.0-pre.0 - annotations: artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n \ displayName: Cilium Network Policy\n description: |\n Cilium Network @@ -2206,10 +2945,10 @@ entries: Pod IP Pool\n description: |\n CiliumPodIPPool defines an IP pool that can be used for pooled IPAM (i.e. the multi-pool IPAM mode).\n" apiVersion: v2 - appVersion: 1.16.0-pre.1 - created: "2024-04-02T20:02:25-07:00" + appVersion: 1.16.0-dev + created: "2024-04-11T17:15:14-07:00" description: eBPF-based Networking, Security, and Observability - digest: 646df1ba01ddd9262dab5f38b7ed2bccd509af6418889245bc101e4d481db352 + digest: ecb2db5c7e22529676bc8a204118f971a413853b22b31d91bf32f7946d75799e home: https://cilium.io/ icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg keywords: @@ -2220,13 +2959,13 @@ entries: - Security - Observability - Troubleshooting - kubeVersion: '>= 1.21.0-0' + kubeVersion: '>= 1.16.0-0' name: cilium sources: - https://github.com/cilium/cilium urls: - - cilium-1.16.0-pre.1.tgz - version: 1.16.0-pre.1 + - cilium-1.16.0-dev.tgz + version: 1.16.0-dev - annotations: artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n \ displayName: Cilium Network Policy\n description: |\n Cilium Network @@ -2312,10 +3051,10 @@ entries: Pod IP Pool\n description: |\n CiliumPodIPPool defines an IP pool that can be used for pooled IPAM (i.e. the multi-pool IPAM mode).\n" apiVersion: v2 - appVersion: 1.16.0-pre.0 - created: "2024-03-04T13:27:22+01:00" + appVersion: 1.15.15 + created: "2025-03-15T09:18:16.180522717Z" description: eBPF-based Networking, Security, and Observability - digest: 4a02367277a1d32d68f45f6fc2c29be6fa22397f1b99cd72a6be5aacf2c78a04 + digest: c19c53cef793fad0850a59482246f3c52feda89c9f21663848bfb5a0be5d80d6 home: https://cilium.io/ icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg keywords: @@ -2331,8 +3070,8 @@ entries: sources: - https://github.com/cilium/cilium urls: - - cilium-1.16.0-pre.0.tgz - version: 1.16.0-pre.0 + - cilium-1.15.15.tgz + version: 1.15.15 - annotations: artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n \ displayName: Cilium Network Policy\n description: |\n Cilium Network @@ -2418,10 +3157,10 @@ entries: Pod IP Pool\n description: |\n CiliumPodIPPool defines an IP pool that can be used for pooled IPAM (i.e. the multi-pool IPAM mode).\n" apiVersion: v2 - appVersion: 1.16.0-dev - created: "2024-04-11T17:15:14-07:00" + appVersion: 1.15.14 + created: "2025-02-18T15:59:45.53415629Z" description: eBPF-based Networking, Security, and Observability - digest: ecb2db5c7e22529676bc8a204118f971a413853b22b31d91bf32f7946d75799e + digest: 7bce296b74e5508022cf37d9f823a00df2fd18951fddff41972efea00d6693d2 home: https://cilium.io/ icon: https://cdn.jsdelivr.net/gh/cilium/cilium@main/Documentation/images/logo-solo.svg keywords: @@ -2437,8 +3176,8 @@ entries: sources: - https://github.com/cilium/cilium urls: - - cilium-1.16.0-dev.tgz - version: 1.16.0-dev + - cilium-1.15.14.tgz + version: 1.15.14 - annotations: artifacthub.io/crds: "- kind: CiliumNetworkPolicy\n version: v2\n name: ciliumnetworkpolicies.cilium.io\n \ displayName: Cilium Network Policy\n description: |\n Cilium Network @@ -22281,4 +23020,4 @@ entries: urls: - tetragon-0.8.0.tgz version: 0.8.0 -generated: "2025-02-04T15:44:02.149628206Z" +generated: "2025-03-15T13:59:35.838764761Z" diff --git a/vendor/github.com/cilium/cilium/.authors.aux b/vendor/github.com/cilium/cilium/.authors.aux deleted file mode 100644 index ece4ffc5ab..0000000000 --- a/vendor/github.com/cilium/cilium/.authors.aux +++ /dev/null @@ -1,9 +0,0 @@ - -The following additional people are mentioned in commit logs as having provided -helpful bug reports, suggestions or have otherwise provided value to the -project: - -Brenden Blanco bblanco@plumgrid.com -Jakub Kicinski jakub.kicinski@netronome.com -Salvatore Orlando salv.orlando@gmail.com -Tomás Senart tsenart@gmail.com diff --git a/vendor/github.com/cilium/cilium/.clang-format b/vendor/github.com/cilium/cilium/.clang-format deleted file mode 100644 index c41e4c1d98..0000000000 --- a/vendor/github.com/cilium/cilium/.clang-format +++ /dev/null @@ -1,180 +0,0 @@ -# Configuration file for clang-format. -# Intended for clang-format >= 15. -# -# The list and meaning of the options is available at: -# -# https://clang.llvm.org/docs/ClangFormatStyleOptions.html ---- -# BasedOnStyle # No base style in use -# AccessModifierOffset # We don't use access modifiers -AlignAfterOpenBracket: Align -AlignArrayOfStructures: Left -AlignConsecutiveAssignments: false -AlignConsecutiveBitFields: - Enabled: true - AcrossEmptyLines: true - AcrossComments: true -AlignConsecutiveDeclarations: false -AlignConsecutiveMacros: - Enabled: true - AcrossEmptyLines: true - AcrossComments: true -AlignEscapedNewlines: Left -AlignOperands: true -AlignTrailingComments: true -AllowAllArgumentsOnNextLine: false -# AllowAllConstructorInitializersOnNextLine # Deprecated -AllowAllParametersOfDeclarationOnNextLine: false -AllowShortBlocksOnASingleLine: Never -AllowShortCaseLabelsOnASingleLine: false -AllowShortEnumsOnASingleLine: false -AllowShortFunctionsOnASingleLine: None -AllowShortIfStatementsOnASingleLine: Never -# AllowShortLambdasOnASingleLine # We don't use lambdas -AllowShortLoopsOnASingleLine: false -# AlwaysBreakAfterDefinitionReturnType # Deprecated -AlwaysBreakAfterReturnType: None -AlwaysBreakBeforeMultilineStrings: false -# AlwaysBreakTemplateDeclarations # We don't use templates -# AttributeMacros # Unused at this time -BinPackArguments: true -BinPackParameters: true -BitFieldColonSpacing: None -BraceWrapping: - AfterCaseLabel: true - # AfterClass # We don't use classes - AfterControlStatement: Never - AfterEnum: false - AfterFunction: true - # AfterNamespace # We don't use namespaces - # AfterObjCDeclaration # We don't use ObjC - AfterStruct: false - AfterUnion: false - AfterExternBlock: false - # BeforeCatch # We don't use try/catch - BeforeElse: false - # BeforeLambdaBody # We don't use lambdas - BeforeWhile: false - IndentBraces: false - SplitEmptyFunction: true - SplitEmptyRecord: true - # SplitEmptyNamespace # We don't use namespaces -# BreakAfterJavaFieldAnnotations # We don't use Java -BreakBeforeBinaryOperators: None -BreakBeforeBraces: Custom -# BreakBeforeConceptDeclarations # We don't use concepts -BreakBeforeTernaryOperators: false -# BreakConstructorInitializers # We don't use constructors -# BreakInheritanceList # We don't use inheritance -BreakStringLiterals: false -ColumnLimit: 80 -# CommentPragmas # Unused at this time -# CompactNamespaces # We don't use namespaces -# ConstructorInitializerAllOnOneLineOrOnePerLine # Deprecated -# ConstructorInitializerIndentWidth # We don't use constructors -ContinuationIndentWidth: 8 -Cpp11BracedListStyle: false -# DeriveLineEnding # Deprecated -DerivePointerAlignment: false -DisableFormat: false -# EmptyLineAfterAccessModifier # We don't use access modifiers -# EmptyLineBeforeAccessModifier # We don't use access modifiers -# ExperimentalAutoDetectBinPacking # Experimental, "Use at your own risk" -# FixNamespaceComments # We don't use namespaces -# ForEachMacros # Unused at this time -# IfMacros # Unused at this time -IncludeBlocks: Preserve -# IncludeCategories # Unused at this time -# IncludeIsMainRegex # Unused at this time -# IncludeIsMainSourceRegex # Unused at this time -# IndentAccessModifiers # We don't use access modifiers -IndentCaseBlocks: false -IndentCaseLabels: false -# IndentExternBlock # We don't use extern blocks -IndentGotoLabels: false -IndentPPDirectives: AfterHash -# IndentRequiresClause # We don't use equire clauses -IndentWidth: 8 -IndentWrappedFunctionNames: false -InsertBraces: false -# InsertTrailingCommas # We don't use JavaScript -# JavaImportGroups # We don't use Java -# JavaScriptQuotes # We don't use JavaScript -# JavaScriptWrapImports # We don't use JavaScript -KeepEmptyLinesAtTheStartOfBlocks: false -# LambdaBodyIndentation # We don't use lambdas -Language: Cpp -# MacroBlockBegin # Unused at this time -# MacroBlockEnd # Unused at this time -MaxEmptyLinesToKeep: 1 -# NamespaceIndentation # We don't use namespaces -# NamespaceMacros # We don't use namespaces -# ObjCBinPackProtocolList # We don't use ObjC -# ObjCBlockIndentWidth # We don't use ObjC -# ObjCBreakBeforeNestedBlockParam # We don't use ObjC -# ObjCSpaceAfterProperty # We don't use ObjC -# ObjCSpaceBeforeProtocolList # We don't use ObjC -PPIndentWidth: 1 -# PackConstructorInitializers # We don't use constructors - -# Penalties decide in what order (weighting) things should be done if a line is -# too long: 100 = try everything else before this. -# See https://stackoverflow.com/a/46749925 -PenaltyBreakAssignment: 10 -PenaltyBreakBeforeFirstCallParameter: 0 -PenaltyBreakComment: 0 -PenaltyBreakFirstLessLess: 0 -PenaltyBreakOpenParenthesis: 100 -PenaltyBreakString: 10 -# PenaltyBreakTemplateDeclaration # We don't use templates -PenaltyExcessCharacter: 100 -PenaltyIndentedWhitespace: 100 -PenaltyReturnTypeOnItsOwnLine: 100 - -PointerAlignment: Right -QualifierAlignment: Leave -# QualifierOrder # Unused at this time -# RawStringFormats # Unused at this time -# ReferenceAlignment # We don't use references -ReflowComments: false -RemoveBracesLLVM: false -# RequiresClausePosition # We don't use require clauses -SeparateDefinitionBlocks: Leave -# ShortNamespaceLines # We don't use namespaces -SortIncludes: Never -# SortJavaStaticImport # We don't use Java -# SortUsingDeclarations # We don't use using declarations -SpaceAfterCStyleCast: false -SpaceAfterLogicalNot: false -# SpaceAfterTemplateKeyword # We don't use templates -SpaceAroundPointerQualifiers: Default -SpaceBeforeAssignmentOperators: true -SpaceBeforeCaseColon: false -# SpaceBeforeCpp11BracedList # We don't use C++11 braced lists to initialize objects -# SpaceBeforeCtorInitializerColon # We don't use constructors -# SpaceBeforeInheritanceColon # We don't use inheritance -SpaceBeforeParens: ControlStatements -# SpaceBeforeParensOptions # No need for custom SpaceBeforeParens options -# SpaceBeforeRangeBasedForLoopColon # We don't use range-based for loops -SpaceBeforeSquareBrackets: false -SpaceInEmptyBlock: false -SpaceInEmptyParentheses: false -SpacesBeforeTrailingComments: 1 -# SpacesInAngles # We don't use templates -SpacesInCStyleCastParentheses: false -SpacesInConditionalStatement: false -SpacesInContainerLiterals: false -SpacesInLineCommentPrefix: - Minimum: 1 - Maximum: 1 -SpacesInParentheses: false -SpacesInSquareBrackets: false -Standard: C++03 -# StatementAttributeLikeMacros # Unused at this time -# StatementMacros # Unused at this time -TabWidth: 8 -# TypenameMacros # Unused at this time -# UseCRLF # Deprecated -UseTab: Always -# WhitespaceSensitiveMacros # Unused at this time -... diff --git a/vendor/github.com/cilium/cilium/.clomonitor.yml b/vendor/github.com/cilium/cilium/.clomonitor.yml deleted file mode 100644 index a5b76a6b13..0000000000 --- a/vendor/github.com/cilium/cilium/.clomonitor.yml +++ /dev/null @@ -1,22 +0,0 @@ -# CLOMonitor metadata file - -exemptions: - - check: slack_presence - reason: "The Cilium slack community can be found at https://slack.cilium.io" # Justification of this exemption - - - check: dangerous_workflow - reason: > - "It is safe to run code checkout '${{ github.event.pull_request.head.sha }}' - and 'github.event.pull_request.head.ref' in .github/workflows/build-images-base.yaml - as this workflow is only permitted to be executed after an explicit approval of a - subset of committers." - - - check: signed_releases - reason: > - "All Cilium release images are cryptographically signed during build by cosign. - Images are hosted in Quay. OpenSSF Scorecard check is currently limited to repositories - hosted on GitHub, and does not support other source hosting repositories." - - - check: token_permissions - reason: > - "Reason to use every non-read-only token in GitHub workflows is commented in the respective workflow files." diff --git a/vendor/github.com/cilium/cilium/.gitattributes b/vendor/github.com/cilium/cilium/.gitattributes deleted file mode 100644 index f2a9481120..0000000000 --- a/vendor/github.com/cilium/cilium/.gitattributes +++ /dev/null @@ -1,18 +0,0 @@ -/install/kubernetes/cilium/values.yaml.tmpl linguist-language=yml -/install/kubernetes/cilium/values.yaml linguist-generated -/install/kubernetes/cilium/README.md linguist-generated -go.sum linguist-generated -examples/kubernetes/connectivity-check/connectivity-*.yaml linguist-generated -pkg/k8s/apis/cilium.io/v2/client/crds/*.yaml linguist-generated -test/controlplane/**/v1.[0-9][0-9]/*.yaml linguist-generated -test/controlplane/services/graceful-termination/*.yaml linguist-generated -Documentation/cmdref/** linguist-generated -Documentation/crdlist.rst linguist-generated -Documentation/helm-values.rst linguist-generated -Documentation/codeowners.rst linguist-generated -Documentation/_static/* -diff -*svg -diff -pkg/k8s/client/clientset/** linguist-generated -pkg/k8s/client/informers/** linguist-generated -pkg/k8s/client/listers/** linguist-generated -*.bt linguist-language=D diff --git a/vendor/github.com/cilium/cilium/.gitignore b/vendor/github.com/cilium/cilium/.gitignore deleted file mode 100644 index f12f401e63..0000000000 --- a/vendor/github.com/cilium/cilium/.gitignore +++ /dev/null @@ -1,118 +0,0 @@ -# Compiled Object files, Static and Dynamic libs (Shared Objects) -*.o -*.a -*.so -*.so.* -*.d - -# LLVM IR files -*.ll -*.ll-* - -# Folders -_obj -_test -_build/ - -# Architecture specific extensions/prefixes -*.cgo1.go -*.cgo2.c -_cgo_defun.c -_cgo_gotypes.go -_cgo_export.* - -_testmain.go - -*.exe -*.test -*.prof - -*.swn -*.swp -.vagrant -vagrant.kubeconfig -coverage.out -coverage-all.out -coverage-all.html -coverage-all-tmp.out -bpf-coverage.html -bpf-coverage.cover - -.DS_Store -.idea/ -.vscode/* -!.vscode/launch.json -!.vscode/extensions.json -*.plist - -*_bash_completion -*.swo -outgoing - -*cscope.files -*cscope.out -*cscope.in.out -*cscope.po.out -*tags -.gdb_history -compile_commands.json - -man/ - -test/envoy/cilium-files -test/test_results* -test/.vagrant -test/tmp.yaml -test/*_manifest.yaml -test/*.xml -test/*.json -test/*.log -test/cilium-[0-9a-f]*.yaml -test/*tmp -test/cilium-istioctl - -# Testdata, includes generated .o -!**/testdata/* - -# Updates E2E Test -old-charts/ - -# generated test files -test/k8s/manifests/cnp-second-namespaces.yaml -test/cilium.conf.ginkgo - -# Emacs backup files -*~ - -# generated from make targets -*.ok -*.build_all -LICENSE.all - -# Temporary files that allow build containers/VMs work without git -# Not to be ignored by docker. -GIT_VERSION - -# The following files get created during image builds -.buildx -.buildx_builder - -# Local developer config to be executed in the dev VM and CI VMs started locally -.devvmrc - -# Generated dockerignore files -images/*/Dockerfile.dockerignore - -# Local Emacs files -.dir-locals.el - -# Clangd cache for indexed bpf code -bpf/.cache -.cache - -# Include dummy bpf object necessary for XDP_TX -!test/bpf/xdp.o - -# Files used for direnv -.direnv -.envrc diff --git a/vendor/github.com/cilium/cilium/.golangci.yaml b/vendor/github.com/cilium/cilium/.golangci.yaml deleted file mode 100644 index d674a83757..0000000000 --- a/vendor/github.com/cilium/cilium/.golangci.yaml +++ /dev/null @@ -1,228 +0,0 @@ -# options for analysis running -run: - # timeout for analysis, e.g. 30s, 5m, default is 1m - timeout: 20m - - # exit code when at least one issue was found, default is 1 - issues-exit-code: 1 - - # include test files or not, default is true - tests: true - - # by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules": - # If invoked with -mod=readonly, the go command is disallowed from the implicit - # automatic updating of go.mod described above. Instead, it fails when any changes - # to go.mod are needed. This setting is most useful to check that go.mod does - # not need updates, such as in a continuous integration and testing system. - # If invoked with -mod=vendor, the go command assumes that the vendor - # directory holds the correct copies of dependencies and ignores - # the dependency descriptions in go.mod. - modules-download-mode: readonly - -# all available settings of specific linters -linters-settings: - depguard: - rules: - main: - deny: - - pkg: "math/rand$" - desc: "Use math/rand/v2 instead" - exhaustruct: - # Ensure that command-line flags are explicitly default-initialized. - include: - - '.+\.[Cc]onfig' - - '.+[Cc]fg' - exclude: - - '.+cache\.Config' # k8s - - '.+fqdn\.Config' # internal API - - '.+tls\.Config' # Go TLS - - '.+v3\.Config' # etcd - - '.+translation\.Config' # internal gateway-api config - govet: - enable: - - nilness - goimports: - local-prefixes: github.com/cilium/cilium/ - goheader: - values: - regexp: - PROJECT: 'Cilium|Hubble' - template: |- - SPDX-License-Identifier: Apache-2.0 - Copyright Authors of {{ PROJECT }} - gosec: - includes: - - G402 - gomodguard: - blocked: - modules: - - github.com/miekg/dns: - recommendations: - - github.com/cilium/dns - reason: "use the cilium fork directly to avoid replace directives in go.mod, see https://github.com/cilium/cilium/pull/27582" - - gopkg.in/check.v1: - recommendations: - - testing - - github.com/stretchr/testify/assert - reason: "gocheck has been deprecated, see https://github.com/cilium/cilium/issues/28596" - - github.com/cilium/checkmate: - recommendations: - - github.com/stretchr/testify/assert - - github.com/stretchr/testify/require - reason: "cilium/checkmate has been deprecated, see https://github.com/cilium/cilium/issues/28596" - - go.uber.org/multierr: - recommendations: - - errors - reason: "Go 1.20+ has support for combining multiple errors, see https://go.dev/doc/go1.20#errors" - - golang.org/x/exp/maps: - recommendations: - - maps - - slices - reason: "Go 1.23+ has support for maps and slices, see https://go.dev/doc/go1.23#iterators" - - golang.org/x/exp/constraints: - recommendations: - - cmp - reason: "Go 1.21+ has support for Ordered constraint, see https://go.dev/doc/go1.21#cmp" - - golang.org/x/exp/slices: - recommendations: - - slices - reason: "Go 1.21+ provides many common operations for slices using generic functions, see https://go.dev/doc/go1.21#slices" - - k8s.io/utils/pointer: - recommendations: - - k8s.io/utils/ptr - reason: "k8s.io/utils/pointer is deprecated, see https://pkg.go.dev/k8s.io/utils/pointer" - - stylecheck: - checks: ["ST1019"] - - sloglint: - # Enforce not mixing key-value pairs and attributes. - # https://github.com/go-simpler/sloglint?tab=readme-ov-file#no-mixed-arguments - # Default: true - no-mixed-args: true - # Enforce using key-value pairs only (overrides no-mixed-args, incompatible with attr-only). - # https://github.com/go-simpler/sloglint?tab=readme-ov-file#key-value-pairs-only - # Default: false - kv-only: true - # Enforce using attributes only (overrides no-mixed-args, incompatible with kv-only). - # https://github.com/go-simpler/sloglint?tab=readme-ov-file#attributes-only - # Default: false - attr-only: false - # Enforce not using global loggers. - # Values: - # - "": disabled - # - "all": report all global loggers - # - "default": report only the default slog logger - # https://github.com/go-simpler/sloglint?tab=readme-ov-file#no-global - # Default: "" - no-global: "default" - # Enforce using methods that accept a context. - # Values: - # - "": disabled - # - "all": report all contextless calls - # - "scope": report only if a context exists in the scope of the outermost function - # https://github.com/go-simpler/sloglint?tab=readme-ov-file#context-only - # Default: "" - context: "" - # Enforce using static values for log messages. - # https://github.com/go-simpler/sloglint?tab=readme-ov-file#static-messages - # Default: false - static-msg: false - # Enforce using constants instead of raw keys. - # https://github.com/go-simpler/sloglint?tab=readme-ov-file#no-raw-keys - # Default: false - no-raw-keys: true - # Enforce a single key naming convention. - # Values: snake, kebab, camel, pascal - # https://github.com/go-simpler/sloglint?tab=readme-ov-file#key-naming-convention - # Default: "" - key-naming-case: camel - # Enforce not using specific keys. - # https://github.com/go-simpler/sloglint?tab=readme-ov-file#forbidden-keys - # Default: [] - forbidden-keys: - - time - - level - - msg - - source - # Enforce putting arguments on separate lines. - # https://github.com/go-simpler/sloglint?tab=readme-ov-file#arguments-on-separate-lines - # Default: false - args-on-sep-lines: true - - testifylint: - enable-all: true - disable: # TODO: remove each disabled rule and fix it - - float-compare - - go-require - - require-error - -issues: - exclude-dirs-use-default: true - - # Excluding configuration per-path, per-linter, per-text and per-source - exclude-rules: - - linters: [staticcheck] - text: "SA1019" # this is rule for deprecated method - - linters: [staticcheck] - text: "SA9003: empty branch" - - linters: [staticcheck] - text: "SA2001: empty critical section" - - linters: [err113] - text: "do not define dynamic errors, use wrapped static errors instead" # This rule to avoid opinionated check fmt.Errorf("text") - # Skip goimports check on generated files - - path: \\.(generated\\.deepcopy|pb)\\.go$ - linters: - - goimports - # Skip goheader check in the example files as these are included in the - # documentation. - - path: "contrib/examples/.+\\.go" - linters: - - goheader - # Skip goheader check on files imported and modified from upstream k8s - - path: "pkg/ipam/(cidrset|service)/.+\\.go" - linters: - - goheader - - path: "pkg/hubble/dropeventemitter/fake_recorder.go" - linters: - - goheader - - - path: "tools/.*.go" - linters: - - sloglint - -linters: - disable-all: true - enable: - - depguard - - errorlint - - err113 - - exhaustruct - - gofmt - - goimports - - govet - - ineffassign - - misspell - - sloglint - - staticcheck - - stylecheck - - testifylint - - unused - - goheader - - gosec - - gomodguard - - gosimple - -# To enable later if makes sense -# - deadcode -# - errcheck -# - gocyclo -# - golint -# - gosec -# - gosimple -# - lll -# - maligned -# - misspell -# - prealloc -# - structcheck -# - typecheck diff --git a/vendor/github.com/cilium/cilium/.mailmap b/vendor/github.com/cilium/cilium/.mailmap deleted file mode 100644 index 86e2de81b3..0000000000 --- a/vendor/github.com/cilium/cilium/.mailmap +++ /dev/null @@ -1,170 +0,0 @@ -Àbéjídé Àyodélé -Adam Korcz -Adam Bocim -Alexei Starovoitov -Alex Waring -Alkama Hasan -André Martins -Andrew Sy Kim -Andrew Li -Anthony Rabbito -Arika Chen -Arthur Chiao -Arthur Evstifeev -Arthur Evstifeev -Arvind Soni -Ashwin Paranjpe -Ashwin Paranjpe -Augustas Berneckas -Barun Acharya -Barun Acharya -Bingshen Wang -Bingwu Yang -Bob Bouteillier -Bruno Miguel Custódio -Carlos Andrés Rocha -Changyu Wang -Charles-Henri Guérin -chenyahui -Chen Kang -Chen Yaqi -Chen Yaqi -Christine Chen -Christopher Biscardi -Claudia J. Kang -Craig Box -Dan Wendlandt -Daniel Qian -Dario Mader <9934402+darox@users.noreply.github.com> -Dario Mader -Darren Mackintosh -Darshan Chaudhary -David Chen -David Cheng -David Chosrova David CHOSROVA -Dawn -Devarshi Sathiya -Divine Odazie -Divya Mohan -Dmitriy Zinin -El-Fadel Bonfoh -Emin Aktas -Fankaixi Li -Felix Färjsjö -fengshunli <1171313930@qq.com> -Fernand Galiana -Florian Koch -François Joulaud <48206448+joulaud@users.noreply.github.com> -Gaurav Genani -Gaurav Yadav -George Kontridze -Gray Liang -Gowtham Sundara -huangxuesen -HaoTian Qi -Hart Hoover -Hui Kong -Ian Vernon -Ifeanyi Ubah -Ivan Makarychev -Jarno Rajahalme -Jarno Rajahalme -James Bodkin -James McShane -Jed Salazar -Jerry J. Muzsik -Jim Ntosas -Jomen Xiao -Jonathan Davies -Jones Shi -Joshua Roppo -Jun Chen -Jussi Maki -Junli Ou -Kamil Lach -Kaito Ii -Karl Heins -Kevin Holditch <82885135+kevholditch-f3@users.noreply.github.com> -Bokang Li -Li Cheng -Lior Rozen -Liu Qun -Livingstone S E -LongHui Li -LongHui Li -Louis DeLosSantos -Madhu Challa -Mahadev Panchal -Mandar U Jog -Marc Stulz -Marcel Zięba Marcel Zieba -Marcel Zięba Marcel Zieba -Matthew Gumport -Maxime Visonneau -Michael Kashin -Michael Vorburger -Neela Jacques <68304471+Neelajacques@users.noreply.github.com> -Oksana Baranova -Oliver Hofmann <91730056+olinux-dev@users.noreply.github.com> -Ondrej Blazek -Parth Patel -Peiqi Shi -Pengfei Song -Philippe Lafoucrière -Pierre-Yves Aillet -Pratyush Singhal -Qifeng Guo -Quentin Monnet -Raam -Rachid Zarouali -Raphael Campos -Rei Shimizu -renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> -Roman Ptitcyn -Sachin Maurya -Sadik Kuzu -Salvatore Mazzarino -Sami Yessou -Sander Timmerman -Sarah Corleissen -Sean Winn -Sebastien Thomas -Sergey Generalov -Simon Felding <45149055+simonfelding@users.noreply.github.com> -Steven Shuang -Tam Mach -Thomas Graf -Tobias Mose -Tobias Mose -Tom Hadlaw -Tomoki Sugiura -Tomoki Sugiura -Tony Lu -Trevor Tao -Vance Li -Vance Li vanceli -Viktor Kurchenko viktor-kurchenko -Viktor Kurchenko viktor-kurchenko <69600804+viktor-kurchenko@users.noreply.github.com> -Ville Ojamo <14869000+bluikko@users.noreply.github.com> -Vlad Ungureanu -Vipul Singh -Vipul Singh -Wang Dong > -Wayne Haber <41373231+whaber@users.noreply.github.com> -Wei Yang <31728060+yulng@users.noreply.github.com> -Weilong Cui -Weizhou Lan -Wenhu Wang -Wongyu Lee -Will Stewart -Yiannis Yiakoumis -Youssef Azrak -Yoyo Wu -Yugo Kobayashi -Yurii Dzobak -Yurii Komar -Yves Blusseau -Xiaoqing -Xiaoyang Zhu -Xin Li -Zhu Yan diff --git a/vendor/github.com/cilium/cilium/CODEOWNERS b/vendor/github.com/cilium/cilium/CODEOWNERS deleted file mode 100644 index 58f1eff22c..0000000000 --- a/vendor/github.com/cilium/cilium/CODEOWNERS +++ /dev/null @@ -1,677 +0,0 @@ -# Code owners are used by the Cilium community to consolidate common knowledge -# into teams that can provide consistent and actionable feedback to -# contributors. This section will describe groups of teams and suggestions -# about the focus areas for review. -# -# The primary motivation for these teams is to provide structure around review -# processes to ensure that contributors know how to reach out to community -# members to conduct discussions, ensure contributions meet the expectations of -# the community, and align on the direction of proposed changes. Furthermore, -# while these teams are primarily drawn upon to provide review on specific pull -# requests, they are also encouraged to self-organize around how to make -# improvements to their areas of the Cilium project over time. -# -# Any committer may self-nominate to code owner teams. Reach out to the core -# team on the #committers channel in Slack to coordinate. Committers do not -# require expert knowledge in an area in order to join a code owner team, -# only a willingness to engage in discussions and learn about the area. -# -# Project-wide -# ++++++++++++ -# -# These code owners may provide feedback for Pull Requests submitted to any -# repository in the Cilium project: -# -# - @cilium/api: -# Ensure the backwards-compatibility of Cilium REST and gRPC APIs, excluding -# Hubble which is owned by @cilium/sig-hubble-api. -# - @cilium/build: -# Provide feedback on languages and scripting used for build and packaging -# system: Make, Shell, Docker. -# - @cilium/cli: -# Provide user experience feedback on changes to Command-Line Interfaces. -# These owners are a stand-in for the user community to bring a user -# perspective to the review process. Consider how information is presented, -# consistency of flags and options. -# - @cilium/ci-structure: -# Provide guidance around the best use of Cilium project continuous -# integration and testing infrastructure, including GitHub actions, VM -# helpers, testing frameworks, etc. -# - @cilium/community: -# Maintain files that refer to Cilium community users such as USERS.md. -# - @cilium/contributing: -# Encourage practices that ensure an inclusive contributor community. Review -# tooling and scripts used by contributors. -# - @cilium/docs-structure: -# Ensure the consistency and layout of documentation. General feedback on the -# use of Sphinx, how to communicate content clearly to the community. This -# code owner is not expected to validate the technical correctness of -# submissions. Correctness is typically handled by another code owner group -# which is also assigned to any given piece of documentation. -# - @cilium/sig-foundations: -# Review changes to the core libraries and provide guidance to overall -# software architecture. -# - @cilium/github-sec: -# Responsible for maintaining the security of repositories in the Cilium -# project by maintaining best practices for workflow usage, for instance -# preventing malicious use of GitHub actions. -# - @cilium/helm: -# Provide input on the way that Helm can be used to configure features. These -# owners are a stand-in for the user community to bring a user perspective to -# the review process. Ensure that Helm changes are defined in manners that -# will be forward-compatible for upgrade and follow best practices for -# deployment (for example, being GitOps-friendly). -# - @cilium/sig-hubble-api: -# Review Hubble API changes related to gRPC endpoints. -# The team ensures that API changes are backward -# compatible or that a new API version is created for backward incompatible -# changes. -# - @cilium/metrics: -# Provide recommendations about the types, names and labels for metrics to -# follow best practices. This includes considering the cardinality impact of -# metrics being added or extended. -# - @cilium/release-managers: -# Review files related to releases like AUTHORS and VERSION. -# - @cilium/security: -# Provide feedback on changes that could have security implications for Cilium, -# and maintain security-related documentation. -# - @cilium/vendor: -# Review vendor updates for software dependencies to check for any potential -# upstream breakages / incompatibilities. Discourage the use of unofficial -# forks of upstream libraries if they are actively maintained. -# -# Repository Owners -# +++++++++++++++++ -# -# The following code owners are responsible for a range of general feedback for -# contributions to specific repositories: -# -# - @cilium/sig-hubble: -# Review all Cilium and Hubble code related to observing system events, -# exporting those via gRPC protocols outside the node and outside the -# cluster. those event channels, for example via TLS. -# - @cilium/hubble-metrics: -# Review code related to Hubble metrics, ensure changes in exposed metrics are -# consistent and not breaking without careful consideration. -# - @cilium/hubble-ui: -# Maintain the Hubble UI graphical interface. -# - @cilium/tetragon: -# Review of all Tetragon code, both for Go and C (for eBPF). -# -# The teams above are responsible for reviewing the majority of contributions -# to the corresponding repositories. Additionally, there are "maintainer" teams -# listed below which may not be responsible for overall code review for a -# repository, but they have administrator access to the repositories and so -# they can assist with configuring GitHub repository settings, secrets, and -# related processes. For the full codeowners for individual repositories, see -# the CODEOWNERS file in the corresponding repository. -# -# - @cilium/cilium-cli-maintainers -# - @cilium/cilium-maintainers -# - @cilium/cilium-packer-ci-build-maintainers -# - @cilium/ebpf-lib-maintainers -# - @cilium/hubble-maintainers -# - @cilium/image-tools-maintainers -# - @cilium/metallb-maintainers -# - @cilium/openshift-terraform-maintainers -# - @cilium/proxy-maintainers -# - @cilium/tetragon-maintainers -# -# Cloud Integrations -# ++++++++++++++++++ -# -# The following codeowner groups provide insight into the integrations with -# specific cloud providers: -# -# - @cilium/alibabacloud -# - @cilium/aws -# - @cilium/azure -# -# Cilium Internals -# ++++++++++++++++ -# -# The following codeowner groups cover more specific knowledge about Cilium -# Agent internals or the way that particular Cilium features interact with -# external software and protocols: -# -# - @cilium/docker: -# Maintain the deprecated docker-plugin. -# - @cilium/endpoint: -# Provide background on how the Cilium Endpoint package fits into the overall -# agent architecture, relationship with generation of policy / datapath -# constructs, serialization and restore from disk. -# - @cilium/envoy: -# Maintain the L7 proxy integration with Envoy. This includes the -# configurations for Envoy via xDS protocols as well as the extensible -# proxylib framework for Go-based layer 7 filters. -# - @cilium/egress-gateway: -# Maintain the egress gateway control plane and datapath logic. -# - @cilium/fqdn: -# Maintain the L7 DNS proxy integration. -# - @cilium/ipcache: -# Provide background on how the userspace IPCache structure fits into the -# overall agent architecture, ordering constraints with respect to network -# policies and encryption. Handle the relationship between Kubernetes state -# and datapath state as it pertains to remote peers. -# - @cilium/ipsec: -# Maintain the kernel IPsec configuration and related eBPF logic to ensure -# traffic is correctly encrypted. -# - @cilium/kvstore: -# Review Cilium interactions with key-value stores, particularly etcd. -# Understand the client libraries used by Cilium for sharing state between -# nodes and clusters. -# - @cilium/loader: -# Maintain the tooling that allows eBPF programs to be loaded into the -# kernel: LLVM, bpftool, use of cilium/ebpf for loading programs in the -# agent, ELF templating, etc. -# - @cilium/operator: -# Review operations that occur once per cluster via the Cilium Operator -# component. Take care of the corresponding garbage collection and leader -# election logic. -# - @cilium/proxy: -# Review low-level implementations used to redirect L7 traffic to the actual -# proxy implementations (FQDN, Envoy, ...). -# - @cilium/sig-agent: -# Provide Cilium (agent) general Go review. Internal architecture, core data -# structures and daemon startup. -# - @cilium/sig-bgp: -# Review changes to our BGP integration. -# - @cilium/sig-clustermesh: -# Ensure the reliability of state sharing between clusters to ensure that -# each cluster maintains a separate fault domain. -# - @cilium/sig-datapath: -# Provide feedback on all eBPF code changes, use of the kernel APIs for -# configuring the networking and socket layers. Coordination of kernel -# subsystems such as xfrm (IPsec), iptables / nftables, tc. Maintain the -# control plane layers that populate most eBPF maps; account for endianness -# and system architecture impacts on the datapath code. -# - @cilium/sig-encryption -# Review control and data plane logic related with encryption (IPSec and -# WireGuard). -# - @cilium/sig-hubble: -# Review all Cilium and Hubble code related to observing system events, -# exporting those via gRPC protocols outside the node and outside the -# cluster. Ensure the security of those event channels, for example via TLS. -# - @cilium/sig-ipam: -# Coordinate the implementation between all of the IP Address Management -# modes, provide awareness/insight into IP resource exhaustion and garbage -# collection concerns. -# - @cilium/sig-k8s: -# Provide input on all interactions with Kubernetes, both for standard -# resources and CRDs. Ensure best practices are followed for the coordination -# of clusterwide state in order to minimize memory usage. -# - @cilium/sig-lb: -# Maintain the layers necessary to coordinate all load balancing -# configurations within the agent control plane, including Services, -# ClusterIP, NodePorts, Maglev, local redirect policies, and -# NAT46/NAT64. -# - @cilium/sig-policy: -# Ensure consistency of semantics for all network policy representations. -# Responsible for all policy logic from Kubernetes down to eBPF policymap -# entries, including all intermediate layers such as the Policy Repository, -# SelectorCache, PolicyCache, CachedSelectorPolicy, EndpointPolicy, etc. -# - @cilium/sig-scalability: -# Maintain scalability and performance tests. Provide input on scalability -# and performance related changes. -# - @cilium/sig-servicemesh: -# Provide input on the way that Service Mesh constructs such as Gateway API -# are converted into lower-level constructs backed by eBPF or Envoy -# configurations. Maintain the CRDs necessary for Service Mesh functionality. -# - @cilium/wireguard: -# Maintain the kernel WireGuard configuration and datapath impacts related to -# ensuring traffic is encrypted correctly when WireGuard mode is enabled. -# -# END_CODEOWNERS_DOCS -# -# The following filepaths should be sorted so that more specific paths occur -# after the less specific paths, otherwise the ownership for the specific paths -# is not properly picked up in Github. -/AUTHORS @cilium/release-managers -/CODE_OF_CONDUCT.md @cilium/contributing -/CODEOWNERS @cilium/contributing -/CONTRIBUTING.md @cilium/contributing -/.authors.aux @cilium/contributing -/.clomonitor.yml @cilium/contributing -/.devcontainer @cilium/ci-structure -/.gitattributes @cilium/contributing -/.github/ @cilium/contributing -/.github/ariane-config.yaml @cilium/github-sec @cilium/ci-structure -/.github/renovate.json5 @cilium/github-sec @cilium/ci-structure -/.github/actions/ @cilium/github-sec @cilium/ci-structure -/.github/actions/bpftrace/ @cilium/sig-encryption @cilium/github-sec @cilium/ci-structure -/.github/actions/ipsec* @cilium/ipsec @cilium/github-sec @cilium/ci-structure -/.github/actions/kvstore/ @cilium/sig-clustermesh @cilium/kvstore @cilium/github-sec @cilium/ci-structure -/.github/workflows/ @cilium/github-sec @cilium/ci-structure -/.github/workflows/auto-approve.yaml @cilium/cilium-maintainers -/.github/workflows/*cilium-cli*.yaml @cilium/cli @cilium/github-sec @cilium/ci-structure -/.github/workflows/*clustermesh*.yaml @cilium/sig-clustermesh @cilium/github-sec @cilium/ci-structure -/.github/workflows/*datapath*.yaml @cilium/sig-datapath @cilium/github-sec @cilium/ci-structure -/.github/workflows/*gateway-api*.yaml @cilium/sig-servicemesh @cilium/github-sec @cilium/ci-structure -/.github/workflows/*hubble*.yaml @cilium/sig-hubble @cilium/github-sec @cilium/ci-structure -/.github/workflows/*ipsec*.yaml @cilium/ipsec @cilium/github-sec @cilium/ci-structure -/.github/workflows/*ingress*.yaml @cilium/sig-servicemesh @cilium/github-sec @cilium/ci-structure -/.github/actions/cl2-modules/ @cilium/sig-scalability -/.github/workflows/*scale*.yaml @cilium/sig-scalability @cilium/github-sec @cilium/ci-structure -/.github/workflows/*perf*.yaml @cilium/sig-scalability @cilium/github-sec @cilium/ci-structure -/.github/workflows/conformance-aks.yaml @cilium/azure @cilium/ipsec @cilium/github-sec @cilium/ci-structure -/.github/workflows/conformance-aws-cni.yaml @cilium/aws @cilium/github-sec @cilium/ci-structure -/.github/workflows/conformance-eks.yaml @cilium/aws @cilium/ipsec @cilium/github-sec @cilium/ci-structure -/.github/workflows/tests-ces-migrate.yaml @cilium/sig-scalability @cilium/github-sec @cilium/ci-structure -/.gitignore @cilium/contributing -/.golangci.yaml @cilium/ci-structure -/.mailmap @cilium/release-managers -/.nvim @cilium/contributing -/.vscode @cilium/contributing -/api/ @cilium/api -/api/v1/Makefile @cilium/sig-hubble-api -/api/v1/Makefile.protoc @cilium/sig-hubble-api -/api/v1/flow/ @cilium/sig-hubble-api -/api/v1/health/ @cilium/api @cilium/sig-agent -/api/v1/observer/ @cilium/sig-hubble-api -/api/v1/operator/ @cilium/api @cilium/operator -/api/v1/peer/ @cilium/sig-hubble-api -/api/v1/recorder/ @cilium/sig-hubble-api -/api/v1/relay/ @cilium/sig-hubble-api -/assets.go @cilium/sig-agent -/bpf/ @cilium/sig-datapath -/bpf/lib/egress_gateway.h @cilium/egress-gateway -Makefile* @cilium/build -/bpf/Makefile* @cilium/loader -/bpf/custom/Makefile* @cilium/build @cilium/loader -/bpf/lib/auth.h @cilium/sig-datapath @cilium/sig-servicemesh -/bpf/lib/encrypt.h @cilium/ipsec -/bpf/lib/policy.h @cilium/sig-datapath @cilium/sig-policy -/bpf/lib/wireguard.h @cilium/wireguard @cilium/sig-datapath -/bpf/bpf_wireguard.c @cilium/wireguard @cilium/sig-datapath -/bpf/lib/ids.h @cilium/loader -/bpf/lib/proxy.h @cilium/proxy @cilium/sig-datapath -/bpf/include/bpf/tailcall.h @cilium/loader -/bpf/include/bpf/config @cilium/loader -/bugtool/ @cilium/cli -/cilium-dbg/ @cilium/cli -/cilium-dbg/cmd/encrypt* @cilium/ipsec @cilium/cli -/cilium-dbg/cmd/preflight_k8s_valid_cnp.go @cilium/sig-k8s -/cilium-cli/ @cilium/cli -/cilium-cli/bgp/ @cilium/sig-bgp -/cilium-cli/cmd/ @cilium/cli -/cilium-cli/clustermesh/ @cilium/sig-clustermesh -/cilium-cli/connectivity/ @cilium/ci-structure -/cilium-cli/connectivity/check/frr.go @cilium/sig-bgp -/cilium-cli/connectivity/check/ipcache.go @cilium/ipcache -/cilium-cli/connectivity/check/metrics*.go @cilium/metrics -/cilium-cli/connectivity/check/policy.go @cilium/sig-policy -/cilium-cli/connectivity/builder/** @cilium/ci-structure -/cilium-cli/connectivity/builder/all_ingress_deny_from_outside.go @cilium/sig-encryption -/cilium-cli/connectivity/builder/bgp_control_plane.go @cilium/sig-bgp -/cilium-cli/connectivity/builder/client_egress.go @cilium/sig-policy -/cilium-cli/connectivity/builder/client_egress_l7*.go @cilium/sig-servicemesh -/cilium-cli/connectivity/builder/client_egress_tls_sni.go @cilium/sig-servicemesh -/cilium-cli/connectivity/builder/client_egress_to_cidr*.go @cilium/sig-policy -/cilium-cli/connectivity/builder/client_egress_to_echo*.go @cilium/sig-policy -/cilium-cli/connectivity/builder/cluster_entity_multi_cluster.go @cilium/sig-clustermesh -/cilium-cli/connectivity/builder/dns_only.go @cilium/fqdn -/cilium-cli/connectivity/builder/echo_ingress.go @cilium/sig-servicemesh -/cilium-cli/connectivity/builder/echo_ingress_auth_always_fail.go @cilium/sig-servicemesh -/cilium-cli/connectivity/builder/echo_ingress_from_other_client_deny.go @cilium/sig-servicemesh -/cilium-cli/connectivity/builder/echo_ingress_from_outside.go @cilium/sig-servicemesh -/cilium-cli/connectivity/builder/echo_ingress_knp.go @cilium/sig-servicemesh -/cilium-cli/connectivity/builder/echo_ingress_l7.go @cilium/sig-servicemesh -/cilium-cli/connectivity/builder/echo_ingress_l7_named_port.go @cilium/sig-servicemesh -/cilium-cli/connectivity/builder/echo_ingress_mutual_auth_spiffe.go @cilium/sig-servicemesh -/cilium-cli/connectivity/builder/egress_gateway.go @cilium/egress-gateway -/cilium-cli/connectivity/builder/egress_gateway_excluded_cidrs.go @cilium/egress-gateway -/cilium-cli/connectivity/builder/egress_gateway_with_l7_policy.go @cilium/egress-gateway -/cilium-cli/connectivity/builder/local_redirect_policy.go @cilium/sig-lb -/cilium-cli/connectivity/builder/no_ipsec_xfrm_errors.go @cilium/sig-encryption -/cilium-cli/connectivity/builder/node_to_node_encryption.go @cilium/sig-encryption -/cilium-cli/connectivity/builder/pod_to_pod_encryption.go @cilium/sig-encryption -/cilium-cli/connectivity/builder/pod_to_pod_encryption_v2.go @cilium/sig-encryption -/cilium-cli/connectivity/perf/** @cilium/sig-scalability -/cilium-cli/connectivity/tests/bgp.go @cilium/sig-bgp -/cilium-cli/connectivity/tests/clustermesh-endpointslice-sync.go @cilium/sig-clustermesh -/cilium-cli/connectivity/tests/egressgateway.go @cilium/egress-gateway -/cilium-cli/connectivity/tests/encryption.go @cilium/sig-encryption -/cilium-cli/connectivity/tests/encryption_v2.go @cilium/sig-encryption -/cilium-cli/connectivity/tests/errors.go @cilium/sig-agent @cilium/sig-datapath -/cilium-cli/connectivity/tests/from-cidr.go @cilium/sig-policy -/cilium-cli/connectivity/tests/health.go @cilium/sig-agent -/cilium-cli/connectivity/tests/host.go @cilium/sig-agent -/cilium-cli/connectivity/tests/ipsec_xfrm.go @cilium/ipsec -/cilium-cli/connectivity/tests/lrp.go @cilium/sig-lb -/cilium-cli/connectivity/tests/pod.go @cilium/sig-agent -/cilium-cli/connectivity/tests/service.go @cilium/sig-lb -/cilium-cli/connectivity/tests/to-cidr.go @cilium/sig-policy -/cilium-cli/connectivity/tests/upgrade.go @cilium/sig-datapath -/cilium-cli/connectivity/tests/world.go @cilium/proxy -/cilium-cli/encrypt/ @cilium/sig-encryption -/cilium-cli/hubble/ @cilium/sig-hubble -/cilium-cli/install/ @cilium/cli @cilium/helm -/cilium-cli/install/azure.go @cilium/azure -/cilium-cli/k8s/ @cilium/sig-k8s -/cilium-health/ @cilium/sig-agent -/cilium-health/cmd/ @cilium/sig-agent @cilium/cli -/clustermesh-apiserver @cilium/sig-clustermesh -/contrib/ @cilium/contributing -/contrib/containerlab/ @cilium/sig-bgp -/contrib/coccinelle/ @cilium/sig-datapath -/contrib/scripts/portgen.py @cilium/sig-datapath -/contrib/scripts/check-datapathconfig.sh @cilium/loader -/daemon/ @cilium/sig-agent -/daemon/cmd/datapath.go @cilium/sig-datapath -/daemon/cmd/endpoint* @cilium/endpoint -/daemon/cmd/fqdn* @cilium/fqdn -/daemon/cmd/health* @cilium/sig-agent -/daemon/cmd/ipcache* @cilium/ipcache -/daemon/cmd/kube_proxy* @cilium/sig-datapath -/daemon/cmd/bootstrap_statistics.go @cilium/metrics -/daemon/cmd/policy* @cilium/sig-policy -/daemon/cmd/state.go @cilium/endpoint -/daemon/cmd/cells*.go @cilium/sig-foundations -/Documentation/ @cilium/docs-structure -/Documentation/_static/ @cilium/docs-structure -/Documentation/api.rst @cilium/sig-agent @cilium/docs-structure -/Documentation/beta.rst @cilium/docs-structure -/Documentation/reference-guides/bpf/ @cilium/sig-datapath @cilium/docs-structure -/Documentation/reference-guides/xfrm/ @cilium/ipsec @cilium/docs-structure -/Documentation/check-build.sh @cilium/docs-structure -/Documentation/check-cmdref.sh @cilium/docs-structure -/Documentation/check-crd-compat-table.sh @cilium/docs-structure -/Documentation/check-examples.sh @cilium/docs-structure -/Documentation/check-helmvalues.sh @cilium/docs-structure -/Documentation/cmdref/ -/Documentation/community/community.rst @cilium/contributing -/Documentation/community/governance.rst @cilium/contributing -/Documentation/community/roadmap.rst @cilium/contributing @cilium/docs-structure -/Documentation/contributing/ @cilium/contributing @cilium/docs-structure -/Documentation/conf.py @cilium/docs-structure -/Documentation/configuration/index.rst @cilium/docs-structure -/Documentation/contributing/ @cilium/contributing @cilium/docs-structure -/Documentation/contributing/development/reviewers_committers/review_vendor.rst @cilium/vendor -/Documentation/crdlist.rst -/Documentation/Dockerfile @cilium/docs-structure -/Documentation/gettingstarted/demo.rst @cilium/docs-structure -/Documentation/gettingstarted/gettinghelp.rst @cilium/contributing @cilium/docs-structure -/Documentation/glossary.rst @cilium/docs-structure -/Documentation/helm-values.rst -/Documentation/images/re-request-review.png @cilium/contributing @cilium/docs-structure -/Documentation/index.rst @cilium/docs-structure -/Documentation/installation/alibabacloud* @cilium/alibabacloud @cilium/docs-structure -/Documentation/installation/aws* @cilium/aws @cilium/docs-structure -/Documentation/installation/cni-chaining-aws-cni.rst @cilium/aws @cilium/docs-structure -/Documentation/installation/cni-chaining-azure-cni.rst @cilium/azure @cilium/docs-structure -/Documentation/installation/kind-configure.rst @cilium/docs-structure -/Documentation/internals/index.rst @cilium/docs-structure -/Documentation/internals/cilium_operator.rst @cilium/operator @cilium/docs-structure -/Documentation/internals/hubble.rst @cilium/sig-hubble @cilium/docs-structure -/Documentation/images/bpf* @cilium/sig-datapath @cilium/docs-structure -/Documentation/images/hubble_getflows.png @cilium/sig-hubble @cilium/docs-structure -/Documentation/Makefile @cilium/docs-structure -/Documentation/network/bgp* @cilium/sig-bgp @cilium/docs-structure -/Documentation/network/clustermesh/ @cilium/sig-clustermesh @cilium/docs-structure -/Documentation/network/concepts/fragmentation.rst @cilium/sig-datapath @cilium/docs-structure -/Documentation/network/concepts/ipam/ @cilium/sig-ipam @cilium/docs-structure -/Documentation/network/concepts/ipam/azure* @cilium/sig-ipam @cilium/azure @cilium/docs-structure -/Documentation/network/concepts/ipam/eni* @cilium/sig-ipam @cilium/aws @cilium/docs-structure -/Documentation/network/concepts/masquerading.rst @cilium/sig-datapath @cilium/docs-structure -/Documentation/network/ebpf/ @cilium/sig-datapath @cilium/docs-structure -/Documentation/network/egress-gateway-toc.rst @cilium/egress-gateway @cilium/docs-structure -/Documentation/network/egress-gateway/ @cilium/egress-gateway @cilium/docs-structure -/Documentation/network/kubernetes/ @cilium/sig-k8s @cilium/docs-structure -/Documentation/network/kubernetes/bandwidth-manager.rst @cilium/sig-datapath @cilium/docs-structure -/Documentation/network/kubernetes/ipam* @cilium/sig-ipam @cilium/docs-structure -/Documentation/network/kubernetes/kubeproxy-free.rst @cilium/sig-lb @cilium/docs-structure -/Documentation/network/kubernetes/local-redirect-policy.rst @cilium/sig-lb @cilium/docs-structure -/Documentation/network/kubernetes/ciliumendpointslice.rst @cilium/sig-scalability @cilium/docs-structure -/Documentation/network/lb-ipam.rst @cilium/sig-lb @cilium/docs-structure -/Documentation/network/multicast.rst @cilium/sig-datapath @cilium/docs-structure -/Documentation/network/servicemesh/ @cilium/sig-servicemesh @cilium/docs-structure -/Documentation/observability/ @cilium/sig-policy @cilium/docs-structure -/Documentation/observability/hubble* @cilium/sig-hubble @cilium/docs-structure -/Documentation/operations/performance/ @cilium/sig-datapath @cilium/docs-structure -/Documentation/operations/system_requirements.rst @cilium/sig-datapath @cilium/docs-structure -/Documentation/operations/troubleshooting_clustermesh.rst @cilium/sig-clustermesh @cilium/docs-structure -/Documentation/overview/component-overview.rst @cilium/docs-structure -/Documentation/overview/intro.rst @cilium/docs-structure -/Documentation/requirements.txt @cilium/docs-structure -/Documentation/security/http.rst @cilium/sig-policy @cilium/docs-structure -/Documentation/security/images/cilium_threat_model* @cilium/security @cilium/docs-structure -/Documentation/security/network/encryption-ipsec.rst @cilium/ipsec @cilium/docs-structure -/Documentation/security/network/encryption-wireguard.rst @cilium/wireguard @cilium/docs-structure -/Documentation/security/network/proxy/ @cilium/proxy @cilium/docs-structure -/Documentation/security/policy-creation.rst @cilium/sig-policy @cilium/docs-structure -/Documentation/security/policy/ @cilium/sig-policy @cilium/docs-structure -/Documentation/security/threat-model.rst @cilium/security @cilium/docs-structure -/Documentation/spelling_wordlist.txt @cilium/docs-structure -/Documentation/update-cmdref.sh @cilium/docs-structure -/Documentation/update-spelling_wordlist.sh @cilium/docs-structure -/Documentation/yaml.config @cilium/docs-structure -/examples/ @cilium/docs-structure -/examples/hubble/ @cilium/sig-hubble -/examples/kubernetes/ @cilium/sig-k8s -/examples/kubernetes/clustermesh/ @cilium/sig-clustermesh -/examples/minikube/ @cilium/sig-k8s -/examples/policies/kubernetes/clustermesh/ @cilium/sig-clustermesh -/FURTHER_READINGS.rst @cilium/docs-structure -/hack/ @cilium/contributing -/hubble/ @cilium/sig-hubble -/hubble-relay/ @cilium/sig-hubble -/images @cilium/build -/images/builder/install-protoc.sh @cilium/sig-hubble-api -/images/builder/install-protoplugins.sh @cilium/sig-hubble-api -/images/builder/update-cilium-builder-image.sh @cilium/github-sec -/images/hubble-relay @cilium/sig-hubble -/images/runtime/update-cilium-runtime-image.sh @cilium/github-sec -/install/kubernetes/ @cilium/sig-k8s @cilium/helm -/install/kubernetes/cilium/**/cilium-envoy @cilium/sig-k8s @cilium/helm @cilium/envoy @cilium/sig-servicemesh -/install/kubernetes/cilium/**/spire @cilium/sig-k8s @cilium/helm @cilium/sig-servicemesh -/install/kubernetes/cilium/templates/clustermesh* @cilium/sig-k8s @cilium/helm @cilium/sig-clustermesh -/install/kubernetes/cilium/templates/hubble* @cilium/sig-k8s @cilium/helm @cilium/sig-hubble -/LICENSE @cilium/contributing -/MAINTAINERS.md @cilium/contributing -/netlify.toml @cilium/ci-structure -/operator/ @cilium/operator -/operator/doublewrite @cilium/metrics -/operator/pkg/bgpv2 @cilium/sig-bgp -/operator/pkg/ciliumendpointslice @cilium/sig-scalability -/operator/pkg/ciliumenvoyconfig @cilium/sig-servicemesh -/operator/pkg/controller-runtime @cilium/envoy @cilium/sig-servicemesh -/operator/pkg/gateway-api @cilium/sig-servicemesh -/operator/pkg/ingress @cilium/sig-servicemesh -/operator/pkg/lbipam @cilium/sig-lb -/operator/pkg/model @cilium/sig-servicemesh -/operator/pkg/networkpolicy @cilium/sig-policy -/operator/pkg/secretsync @cilium/envoy @cilium/sig-servicemesh -/pkg/act/ @cilium/sig-datapath @cilium/metrics -/pkg/annotation @cilium/sig-k8s -/pkg/alibabacloud/ @cilium/alibabacloud -/pkg/alignchecker/ @cilium/sig-datapath @cilium/loader -/pkg/allocator/ @cilium/kvstore -/pkg/api/ @cilium/api -/pkg/auth/ @cilium/sig-servicemesh -/pkg/aws/ @cilium/aws -/pkg/azure/ @cilium/azure -/pkg/backoff/ @cilium/sig-agent -/pkg/bufuuid/ @cilium/sig-scalability -/pkg/datapath/linux/bandwidth/ @cilium/sig-datapath -/pkg/bgpv1/ @cilium/sig-bgp -/pkg/bpf/ @cilium/loader -/pkg/byteorder/ @cilium/sig-datapath @cilium/api -/pkg/cgroups/ @cilium/sig-datapath -/pkg/cidr/ @cilium/sig-agent -/pkg/ciliumenvoyconfig/ @cilium/envoy @cilium/sig-servicemesh -/pkg/cleanup/ @cilium/sig-agent -/pkg/client @cilium/api -/pkg/clustermesh @cilium/sig-clustermesh -/pkg/cmdref @cilium/cli -/pkg/command/ @cilium/cli -/pkg/common/ @cilium/sig-agent -/pkg/common/ipsec/ @cilium/ipsec -/pkg/comparator/ @cilium/sig-agent -/pkg/completion/ @cilium/proxy -/pkg/components/ @cilium/sig-agent -/pkg/container/ @cilium/sig-foundations -/pkg/container/bitlpm/ @cilium/ipcache @cilium/sig-policy -/pkg/container/set/ @cilium/sig-policy -/pkg/controller @cilium/sig-agent -/pkg/counter @cilium/sig-datapath -/pkg/crypto/certificatemanager @cilium/envoy @cilium/sig-servicemesh -/pkg/crypto/certloader @cilium/sig-hubble -/pkg/datapath/ @cilium/sig-datapath -/pkg/datapath/fake/ipsec.go @cilium/ipsec -/pkg/datapath/linux/config/ @cilium/loader -/pkg/datapath/linux/ipsec/ @cilium/ipsec -/pkg/datapath/linux/ipsec/xfrm_collector* @cilium/ipsec @cilium/metrics -/pkg/datapath/linux/ipsec.go @cilium/ipsec -/pkg/datapath/linux/node.go @cilium/sig-datapath -/pkg/datapath/linux/probes/ @cilium/loader -/pkg/datapath/linux/requirements.go @cilium/loader -/pkg/datapath/linux/sysctl/ @cilium/sig-datapath -/pkg/datapath/types/ipsec.go @cilium/ipsec -/pkg/datapath/types/loader.go @cilium/loader -/pkg/datapath/loader/ @cilium/loader -/pkg/datapath/ipcache/ @cilium/ipcache -/pkg/defaults @cilium/sig-agent -/pkg/debug @cilium/sig-agent -/pkg/dial @cilium/sig-agent -/pkg/driftchecker @cilium/sig-foundations -/pkg/dynamicconfig @cilium/sig-foundations -/pkg/ebpf @cilium/sig-datapath -/pkg/egressgateway/ @cilium/egress-gateway -/pkg/endpoint/ @cilium/endpoint -/pkg/endpointcleanup/ @cilium/endpoint -/pkg/endpointmanager/ @cilium/endpoint -/pkg/endpointstate/ @cilium/endpoint -/pkg/envoy/ @cilium/envoy -/pkg/eventqueue/ @cilium/sig-agent -/pkg/dynamiclifecycle/ @cilium/sig-foundations -/pkg/flowdebug/ @cilium/proxy -/pkg/fqdn/ @cilium/fqdn -/pkg/fswatcher/ @cilium/sig-datapath @cilium/sig-hubble -/pkg/gops/ @cilium/sig-agent -/pkg/health/ @cilium/sig-agent -/pkg/hive/ @cilium/sig-foundations -/pkg/hubble/ @cilium/sig-hubble -/pkg/hubble/metrics @cilium/hubble-metrics -/pkg/iana/ @cilium/sig-agent -/pkg/identity @cilium/sig-policy -/pkg/idpool/ @cilium/kvstore -/pkg/ip/ @cilium/sig-agent -/pkg/ipalloc/ @cilium/sig-ipam -/pkg/ipam/ @cilium/sig-ipam -/pkg/ipam/allocator/alibabacloud/ @cilium/sig-ipam @cilium/alibabacloud -/pkg/ipam/allocator/aws/ @cilium/sig-ipam @cilium/aws -/pkg/ipam/allocator/azure/ @cilium/sig-ipam @cilium/azure -/pkg/ipam/allocator/clusterpool/ @cilium/sig-ipam @cilium/operator -/pkg/ipcache/ @cilium/ipcache -/pkg/ipmasq @cilium/sig-agent -/pkg/k8s/ @cilium/sig-k8s -/pkg/k8s/apis/cilium.io/client/crds/v2/ @cilium/sig-k8s -/pkg/k8s/apis/cilium.io/client/crds/v2/ciliumegressgatewaypolicies.yaml @cilium/egress-gateway -/pkg/k8s/apis/cilium.io/v2/cegp_types.go @cilium/egress-gateway -/pkg/k8s/apis/cilium.io/v2/ @cilium/api @cilium/sig-k8s -/pkg/kvstore/ @cilium/kvstore -/pkg/kvstore/etcdinit @cilium/sig-clustermesh @cilium/kvstore -/pkg/l2announcer/ @cilium/sig-agent -/pkg/labels @cilium/sig-policy @cilium/api -/pkg/labelsfilter @cilium/sig-policy -/pkg/launcher @cilium/sig-agent -/pkg/loadbalancer @cilium/sig-lb -/pkg/loadinfo/ @cilium/sig-agent -/pkg/lock @cilium/sig-agent -/pkg/logging/ @cilium/cli -/pkg/mac @cilium/sig-datapath -/pkg/maglev @cilium/sig-lb -/pkg/maps/ @cilium/sig-datapath -/pkg/maps/egressmap @cilium/egress-gateway -/pkg/mcastmanager @cilium/sig-datapath -/pkg/metrics @cilium/metrics -/pkg/monitor @cilium/sig-datapath -/pkg/monitor/api @cilium/api @cilium/sig-datapath -/pkg/monitor/datapath_trace.go @cilium/sig-datapath @cilium/sig-hubble -/pkg/monitor/format @cilium/cli @cilium/sig-datapath -/pkg/monitor/payload @cilium/api @cilium/sig-datapath -/pkg/mountinfo @cilium/sig-datapath -/pkg/mtu @cilium/sig-datapath -/pkg/multicast @cilium/sig-datapath -/pkg/murmur3/ @cilium/sig-datapath -/pkg/netns/ @cilium/sig-datapath @cilium/sig-k8s -/pkg/node @cilium/sig-agent -/pkg/nodediscovery/ @cilium/sig-agent -/pkg/option @cilium/sig-agent @cilium/cli -/pkg/pidfile @cilium/sig-agent -/pkg/policy @cilium/sig-policy -/pkg/policy/api/ @cilium/api -/pkg/policy/groups/aws/ @cilium/sig-policy @cilium/aws -/pkg/policy/k8s @cilium/sig-policy -/pkg/pprof @cilium/sig-foundations -/pkg/promise @cilium/sig-foundations -/pkg/proxy/ @cilium/proxy -/pkg/proxy/accesslog @cilium/proxy @cilium/api -/pkg/proxy/dns.go @cilium/proxy @cilium/fqdn -/pkg/proxy/envoyproxy.go @cilium/proxy @cilium/envoy -/pkg/rate/ @cilium/sig-agent -/pkg/rate/metrics @cilium/metrics -/pkg/recorder @cilium/sig-datapath -/pkg/redirectpolicy @cilium/sig-lb -/pkg/resiliency @cilium/sig-agent -/pkg/revert/ @cilium/sig-agent -/pkg/safeio @cilium/sig-agent -/pkg/safetime/ @cilium/sig-agent -/pkg/service @cilium/sig-lb -/pkg/shortener @cilium/sig-foundations @cilium/sig-k8s -/pkg/signal @cilium/sig-datapath -/pkg/slices @cilium/sig-foundations -/pkg/socketlb @cilium/loader -/pkg/source @cilium/ipcache -/pkg/spanstat/ @cilium/sig-agent -/pkg/status/ @cilium/sig-agent -/pkg/testutils/ @cilium/ci-structure -/pkg/time @cilium/sig-agent -/pkg/trigger/ @cilium/sig-agent -/pkg/tuple @cilium/sig-datapath -/pkg/types/ @cilium/sig-datapath -/pkg/u8proto/ @cilium/sig-agent -/pkg/wireguard @cilium/wireguard -/pkg/version/ @cilium/sig-agent -/pkg/versioncheck/ @cilium/sig-agent -/pkg/xds/ @cilium/envoy -/plugins/cilium-cni/ @cilium/sig-k8s -/plugins/cilium-docker/ @cilium/docker -/README.rst @cilium/docs-structure -/SECURITY.md @cilium/contributing -/SECURITY-INSIGHTS.yml @cilium/security -/stable.txt @cilium/release-managers -/test/ @cilium/ci-structure -/test/Makefile* @cilium/ci-structure @cilium/build -# Service handling tests -/test/k8s/services.go @cilium/sig-lb @cilium/ci-structure -# Datapath tests -/bpf/tests/bpftest/ @cilium/sig-datapath -/test/k8s/bandwidth.go @cilium/sig-datapath @cilium/ci-structure -/test/k8s/chaos.go @cilium/sig-datapath @cilium/ci-structure -/test/k8s/datapath_configuration.go @cilium/sig-datapath @cilium/ci-structure -/test/runtime/connectivity.go @cilium/sig-datapath @cilium/ci-structure -/test/verifier @cilium/loader @cilium/ci-structure -# Policy tests -/test/k8s/net_policies.go @cilium/sig-policy @cilium/ci-structure -/test/runtime/net_policies.go @cilium/sig-policy @cilium/ci-structure -# Hubble/monitoring tests -/test/k8s/hubble.go @cilium/sig-hubble @cilium/ci-structure -/test/runtime/monitor.go @cilium/sig-hubble @cilium/ci-structure -# L7 proxy tests -/test/k8s/fqdn.go @cilium/fqdn @cilium/ci-structure -/test/k8s/kafka_policies.go @cilium/envoy @cilium/ci-structure -/test/runtime/fqdn.go @cilium/fqdn @cilium/ci-structure -# BIG TCP tests -/test/bigtcp @cilium/sig-datapath @cilium/ci-structure -# Misc. tests -/test/runtime/kvstore.go @cilium/kvstore @cilium/ci-structure -/test/runtime/chaos_agent.go @cilium/sig-agent @cilium/ci-structure -/tools/dpgen @cilium/loader -/tools/ @cilium/contributing -/USERS.md @cilium/community -/go.sum @cilium/vendor -/go.mod @cilium/vendor -/vendor/ @cilium/vendor -/VERSION @cilium/release-managers -/.clang-format @cilium/contributing diff --git a/vendor/github.com/cilium/cilium/CODE_OF_CONDUCT.md b/vendor/github.com/cilium/cilium/CODE_OF_CONDUCT.md deleted file mode 100644 index eea0dbd783..0000000000 --- a/vendor/github.com/cilium/cilium/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,46 +0,0 @@ -## Community Code of Conduct v1.0 - -This is Code of Conduct is based on the [CNCF Code of -Conduct](https://github.com/cncf/foundation/blob/main/code-of-conduct.md). -See the referred document for translated versions into different languages. The -text below is modified with Cilium community specific contact details. - -### Contributor Code of Conduct - -As contributors and maintainers of this project, and in the interest of fostering -an open and welcoming community, we pledge to respect all people who contribute -through reporting issues, posting feature requests, updating documentation, -submitting pull requests or patches, and other activities. - -We are committed to making participation in this project a harassment-free experience for -everyone, regardless of level of experience, gender, gender identity and expression, -sexual orientation, disability, personal appearance, body size, race, ethnicity, age, -religion, or nationality. - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery -* Personal attacks -* Trolling or insulting/derogatory comments -* Public or private harassment -* Publishing others' private information, such as physical or electronic addresses, - without explicit permission -* Other unethical or unprofessional conduct. - -Project maintainers have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are not -aligned to this Code of Conduct. By adopting this Code of Conduct, project maintainers -commit themselves to fairly and consistently applying these principles to every aspect -of managing this project. Project maintainers who do not follow or enforce the Code of -Conduct may be permanently removed from the project team. - -This code of conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported by contacting the code of conduct team via -[conduct@cilium.io](mailto:conduct@cilium.io). - -This Code of Conduct is adapted from the Contributor Covenant -(http://contributor-covenant.org), version 1.2.0, available at -http://contributor-covenant.org/version/1/2/0/ diff --git a/vendor/github.com/cilium/cilium/CONTRIBUTING.md b/vendor/github.com/cilium/cilium/CONTRIBUTING.md deleted file mode 100644 index 622295d267..0000000000 --- a/vendor/github.com/cilium/cilium/CONTRIBUTING.md +++ /dev/null @@ -1,23 +0,0 @@ -# How to Contribute - -## To Cilium/Hubble - -See the [Developer / Contributor -Guide](https://docs.cilium.io/en/stable/contributing/development/contributing_guide/) for detailed information on -how to contribute, get started and find good first issues. - -## To the cilium.io website - -Please see the [cilium.io website contributing guide](https://github.com/cilium/cilium.io/blob/main/CONTRIBUTING.md) for detailed -information on how to add blogs, trainings, and other resources. - -## To the Cilium documentation - -Please see the [Cilium documentation contributing guide](https://docs.cilium.io/en/stable/contributing/docs/) for detailed -information on how to contribute to the Cilium documentation. - -## Support Expectations & Enterprise Options - -Cilium is an open source project maintained by the community, and we greatly appreciate all contributions that help improve it. If you find an issue or need a new feature, we encourage you to open an issue, submit a pull request, start a discussion, or join the weekly developer meeting. - -However, if your organization requires stronger guarantees, priority support, or dedicated assistance, enterprise support is available from vendors in the ecosystem. You can find more details on the [website](https://cilium.io/enterprise/). diff --git a/vendor/github.com/cilium/cilium/FURTHER_READINGS.rst b/vendor/github.com/cilium/cilium/FURTHER_READINGS.rst deleted file mode 100644 index b8a8936c6f..0000000000 --- a/vendor/github.com/cilium/cilium/FURTHER_READINGS.rst +++ /dev/null @@ -1,87 +0,0 @@ -Further Reading -=============== - -.. further-reading-begin - -Related Material ----------------- - -* `BPF for security—and chaos—in Kubernetes `_ -* `k8s-snowflake: Configs and scripts for bootstrapping an opinionated - Kubernetes cluster anywhere using Cilium plugin - `_ -* `Using Cilium for NetworkPolicy: Kubernetes documentation on how to use Cilium - to implement NetworkPolicy - `_ - -Presentations -------------- - -* Kubernetes on Edge Day, Europe 2022 - Connecting Klusters on the Edge with Deep Dive into Cilium Cluster Mesh: - `Video `__ -* Cloud Native Telco Day, Europe 2022 - Leveraging Cilium and SRv6 for Telco Networking: - `Video `__ -* KubeCon, Europe 2022 - A Guided Tour of Cilium Service Mesh: - `Video `__ -* eBPF Day, Europe, 2022 - IKEA Private Cloud, eBPF Based Networking, Load Balancing, and Observability with Cilium: - `Video `__ -* KubeCon, North America 2021 - Beyond printf & tcpdump: Debugging Kubernetes Networking with eBPF: - `Video `__ -* eBPF Summit, Virtual 2020 - Our eBPF Journey at Datadog: - `Video `__ -* eBPF Summit, Virtual 2020 - Building a Secure and Maintainable PaaS Leveraging Cilium: - `Video `__ -* eBPF Summit, Virtual 2020 - The Past, Present and Future of Cilium and Hubble at Palantir: - `Video `__ -* KubeCon, Europe 2020 - Hubble - eBPF Based Observability for Kubernetes: - `Video `__ -* Fosdem, Brussels, 2020 - BPF as a revolutionary technology for the container landscape: - `Slides `__, `Video `__ -* KubeCon, North America 2019 - Understanding and Troubleshooting the eBPF Datapath in Cilium: - `Video `__ -* KubeCon, North America 2019 - Liberating Kubernetes from kube-proxy and iptables: - `Slides `__, `Video `__ -* KubeCon, Europe 2019 - Using eBPF to Bring Kubernetes-Aware Security to the Linux Kernel: - `Video `__ -* KubeCon, Europe 2019 - Transparent Chaos Testing with Envoy , Cilium and BPF: - `Slides `__, `Video `__ -* All Systems Go!, Berlin, Sept 2018 - Cilium - Bringing the BPF Revolution to Kubernetes Networking and Security - `Slides `__, `Video `__ -* QCon, San Francisco 2018 - How to Make Linux Microservice-Aware with Cilium and eBPF: - `Slides `__, `Video `__ -* KubeCon, North America 2018 - Connecting Kubernetes Clusters Across Cloud Providers: - `Slides `__, `Video `__ -* KubeCon, North America 2018 - Implementing Least Privilege Security and Networking with BPF on Kubernetes: - `Slides `__, `Video `__ -* KubeCon, Europe 2018 - Accelerating Envoy with the Linux Kernel: - `Video `__ -* Open Source Summit, North America - Cilium: Networking and security for containers with BPF and XDP: - `Video `__ -* DockerCon, Austin TX, Apr 2017 - Cilium - Network and Application Security with BPF and XDP: `Slides - `__, `Video `__ -* CNCF/KubeCon Meetup, Berlin, Mar 2017 - Linux Native, HTTP Aware Network Security: - `Slides `__, `Video `__ -* Docker Distributed Systems Summit, Berlin, Oct 2016: - `Slides `__, `Video `__ -* NetDev1.2, Tokyo, Sep 2016 - cls_bpf/eBPF updates since netdev 1.1: - `Slides `__, `Video `__ -* NetDev1.2, Tokyo, Sep 2016 - Advanced programmability and recent updates with tc’s cls_bpf: - `Slides `__, `Video `__ -* ContainerCon NA, Toronto, Aug 2016 - Fast IPv6 container networking with BPF & XDP: - `Slides `__ - -Podcasts --------- - -* Software Gone Wild by Ivan Pepelnjak, Oct 2016: `Blog `__, `MP3 `__ -* OVS Orbit by Ben Pfaff, May 2016: `Blog `__, `MP3 `__ - -Community blog posts --------------------- - -* `Cilium for Network and Application Security with BPF and XDP, Apr 2017 - `_ -* `Cilium, BPF and XDP, Google Open Source Blog, Nov 2016 - `_ - -.. further-reading-end diff --git a/vendor/github.com/cilium/cilium/MAINTAINERS.md b/vendor/github.com/cilium/cilium/MAINTAINERS.md deleted file mode 100644 index dcfa99014b..0000000000 --- a/vendor/github.com/cilium/cilium/MAINTAINERS.md +++ /dev/null @@ -1,132 +0,0 @@ -# Maintainers - -See [Governance](https://github.com/cilium/community/blob/main/GOVERNANCE.md) for -governance, commit, and vote guidelines as well as committer responsibilities. -Everybody listed is a committer as per governance definition. See the -[Contributor Ladder](https://github.com/cilium/community/blob/main/CONTRIBUTOR-LADDER.md) -to learn how to level up through the project. - -## Cilium Committers - - * [Aditi Ghag] (Isovalent) - * [Alexandre Perrin] (Isovalent) - * [André Martins] (Isovalent) - * [Beatriz Martínez] (Isovalent) - * [Bill Mulligan] (Isovalent) - * [Bruno M. Custódio] (Isovalent) - * [Casey Callendrello] (Isovalent) - * [Chance Zibolski] (Isovalent) - * [Chris Tarazi] (Isovalent) - * [Daniel Borkmann] (Isovalent) - * [Dan Wendlandt] (Isovalent) - * [Deepesh Pathak] - * [Dorde Lapcevic] (Google) - * [Dylan Reimerink] (Isovalent) - * [Gilberto Bertin] (Isovalent) - * [Glib Smaga] (Isovalent) - * [Hemanth Malla] (Datadog) - * [Ian Vernon] - * [Jarno Rajahalme] (Isovalent) - * [Joe Stringer] (Isovalent) - * [John Fastabend] (Isovalent) - * [Julian Wiedmann] (Isovalent) - * [Jussi Mäki] (Isovalent) - * [Kornilios Kourtis] (Isovalent) - * [Laurent Bernaille] (Datadog) - * [Liz Rice] (Isovalent) - * [Lorenz Bauer] (Isovalent) - * [Louis DeLosSantos] (Isovalent) - * [Maciej Kwiek] (Isovalent) - * [Marco Iorio] (Isovalent) - * [Martynas Pumputis] (Isovalent) - * [Michal Rostecki] (Deepfence) - * [Michi Mutsuzaki] (Isovalent) - * [Natália Réka Ivánkó] (Isovalent) - * [Nathan Sweet] (Isovalent) - * [Nick Young] (Isovalent) - * [Nicolas Busseneau] (Isovalent) - * [Nirmoy Das] (AMD) - * [Paul Chaignon] (Isovalent) - * [Quentin Monnet] (Hedgehog) - * [Robin Hahling] (Isovalent) - * [Sebastian Wicki] (Isovalent) - * [Tam Mach] (Isovalent) - * [Thomas Graf] (Isovalent) - * [Timo Beckers] (Isovalent) - * [Tobias Klauser] (Isovalent) - * [Tom Hadlaw] (Isovalent) - * [Vlad Ungureanu] (Palantir) - * [Yutaro Hayakawa] (Isovalent) - -## Cilium & Hubble Emeritus Committers - -We would like to acknowledge previous committers and their huge contributions to our collective success: - - * [Eloy Coto] (Red Hat) - * [Ilya Dmitrichenko] (Docker) - * [Ray Bejjani] - * [Tom Payne] - * [Weilong Cui] (Google) - * [Yongkun Gui] (Google) - * [Zang Li] (Google) - - -Please see the AUTHORS file for the full list of contributors to the Cilium -project. - -[Aditi Ghag]: https://github.com/aditighag -[Alexandre Perrin]: https://github.com/kaworu -[André Martins]: https://github.com/aanm -[Beatriz Martínez]: https://github.com/b3a-dev -[Bill Mulligan]: https://github.com/xmulligan -[Bruno M. Custódio]: https://github.com/bmcustodio -[Casey Callendrello]: https://github.com/squeed -[Chance Zibolski]: https://github.com/chancez -[Chris Tarazi]: https://github.com/christarazi -[Daniel Borkmann]: https://github.com/borkmann -[Dan Wendlandt]: https://github.com/danwent -[Deepesh Pathak]: https://github.com/fristonio -[Dorde Lapcevic]: https://github.com/dlapcevic -[Dylan Reimerink]: https://github.com/dylandreimerink -[Eloy Coto]: https://github.com/eloycoto -[Gilberto Bertin]: https://github.com/jibi -[Glib Smaga]: https://github.com/glibsm -[Hemanth Malla]: https://github.com/hemanthmalla -[Ian Vernon]: https://github.com/ianvernon -[Ilya Dmitrichenko]: https://github.com/errordeveloper -[Jarno Rajahalme]: https://github.com/jrajahalme -[Joe Stringer]: https://github.com/joestringer -[John Fastabend]: https://github.com/jrfastab -[Julian Wiedmann]: https://github.com/julianwiedmann -[Jussi Mäki]: https://github.com/joamaki -[Kornilios Kourtis]: https://github.com/kkourt -[Laurent Bernaille]: https://github.com/lbernail -[Liz Rice]: https://github.com/lizrice -[Lorenz Bauer]: https://github.com/lmb -[Louis DeLosSantos]: https://github.com/ldelossa -[Maciej Kwiek]: https://github.com/nebril -[Marco Iorio]: https://github.com/giorio94 -[Martynas Pumputis]: https://github.com/brb -[Michal Rostecki]: https://github.com/vadorovsky -[Michi Mutsuzaki]: https://github.com/michi-covalent -[Natália Réka Ivánkó]: https://github.com/sharlns -[Nathan Sweet]: https://github.com/nathanjsweet -[Nick Young]: https://github.com/youngnick -[Nicolas Busseneau]: https://github.com/nbusseneau -[Nirmoy Das]: https://github.com/nirmoy -[Paul Chaignon]: https://github.com/pchaigno -[Quentin Monnet]: https://github.com/qmonnet -[Ray Bejjani]: https://github.com/raybejjani -[Robin Hahling]: https://github.com/rolinh -[Sebastian Wicki]: https://github.com/gandro -[Tam Mach]: https://github.com/sayboras -[Thomas Graf]: https://github.com/tgraf -[Timo Beckers]: https://github.com/ti-mo -[Tobias Klauser]: https://github.com/tklauser -[Tom Hadlaw]: https://github.com/tommyp1ckles -[Tom Payne]: https://github.com/twpayne -[Vlad Ungureanu]: https://github.com/ungureanuvladvictor -[Weilong Cui]: https://github.com/Weil0ng -[Yongkun Gui]: https://github.com/anfernee -[Yutaro Hayakawa]: https://github.com/YutaroHayakawa -[Zang Li]: https://github.com/lzang diff --git a/vendor/github.com/cilium/cilium/Makefile b/vendor/github.com/cilium/cilium/Makefile deleted file mode 100644 index 1710dc32e6..0000000000 --- a/vendor/github.com/cilium/cilium/Makefile +++ /dev/null @@ -1,539 +0,0 @@ -# Copyright Authors of Cilium -# SPDX-License-Identifier: Apache-2.0 - -##@ Default -all: precheck build postcheck ## Default make target that perform precheck -> build -> postcheck - @echo "Build finished." - -##@ Build, Install and Test -debug: export NOOPT=1 ## Builds Cilium by disabling inlining, compiler optimizations and without stripping debug symbols, useful for debugging. -debug: export NOSTRIP=1 -debug: all - -include Makefile.defs - -SUBDIRS_CILIUM_CONTAINER := cilium-dbg daemon cilium-health bugtool tools/mount tools/sysctlfix plugins/cilium-cni -SUBDIR_OPERATOR_CONTAINER := operator -SUBDIR_RELAY_CONTAINER := hubble-relay - -ifdef LIBNETWORK_PLUGIN -SUBDIRS_CILIUM_CONTAINER += plugins/cilium-docker -endif - -# Add the ability to override variables --include Makefile.override - -# List of subdirectories used for global "make build", "make clean", etc -SUBDIRS := $(SUBDIRS_CILIUM_CONTAINER) $(SUBDIR_OPERATOR_CONTAINER) plugins tools $(SUBDIR_RELAY_CONTAINER) bpf clustermesh-apiserver - -# Filter out any directories where the parent directory is also present, to avoid -# building or cleaning a subdirectory twice. -# For example: The directory "tools" is transformed into a match pattern "tools/%", -# which is then used to filter out items such as "tools/mount" and "tools/sysctlfx" -SUBDIRS := $(filter-out $(foreach dir,$(SUBDIRS),$(dir)/%),$(SUBDIRS)) - -# Space-separated list of Go packages to test, equivalent to 'go test' package patterns. -# Because is treated as a Go package pattern, the special '...' sequence is supported, -# meaning 'all subpackages of the given package'. -TESTPKGS ?= ./... -UNPARALLELTESTPKGS ?= ./pkg/datapath/linux/ipsec/... - -GOTEST_BASE := -timeout 720s -GOTEST_COVER_OPTS += -coverprofile=coverage.out -BENCH_EVAL := "." -BENCH ?= $(BENCH_EVAL) -BENCHFLAGS_EVAL := -bench=$(BENCH) -run=^$$ -benchtime=10s -BENCHFLAGS ?= $(BENCHFLAGS_EVAL) -SKIP_KVSTORES ?= "false" -SKIP_K8S_CODE_GEN_CHECK ?= "true" -SKIP_CUSTOMVET_CHECK ?= "false" - -JOB_BASE_NAME ?= cilium_test - -TEST_LDFLAGS=-ldflags "-X github.com/cilium/cilium/pkg/kvstore.etcdDummyAddress=http://etcd:4002" - -TEST_UNITTEST_LDFLAGS= - -build: $(SUBDIRS) ## Builds all the components for Cilium by executing make in the respective sub directories. - -build-container: ## Builds components required for cilium-agent container. - for i in $(SUBDIRS_CILIUM_CONTAINER); do $(MAKE) $(SUBMAKEOPTS) -C $$i all; done - -build-container-operator: ## Builds components required for cilium-operator container. - $(MAKE) $(SUBMAKEOPTS) -C $(SUBDIR_OPERATOR_CONTAINER) all - -build-container-operator-generic: ## Builds components required for a cilium-operator generic variant container. - $(MAKE) $(SUBMAKEOPTS) -C $(SUBDIR_OPERATOR_CONTAINER) cilium-operator-generic - -build-container-operator-aws: ## Builds components required for a cilium-operator aws variant container. - $(MAKE) $(SUBMAKEOPTS) -C $(SUBDIR_OPERATOR_CONTAINER) cilium-operator-aws - -build-container-operator-azure: ## Builds components required for a cilium-operator azure variant container. - $(MAKE) $(SUBMAKEOPTS) -C $(SUBDIR_OPERATOR_CONTAINER) cilium-operator-azure - -build-container-operator-alibabacloud: ## Builds components required for a cilium-operator alibabacloud variant container. - $(MAKE) $(SUBMAKEOPTS) -C $(SUBDIR_OPERATOR_CONTAINER) cilium-operator-alibabacloud - -build-container-hubble-relay: - $(MAKE) $(SUBMAKEOPTS) -C $(SUBDIR_RELAY_CONTAINER) all - -$(SUBDIRS): force ## Execute default make target(make all) for the provided subdirectory. - @ $(MAKE) $(SUBMAKEOPTS) -C $@ all - -tests-privileged: ## Run Go tests including ones that require elevated privileges. - @$(ECHO_CHECK) running privileged tests... - ## We split tests into two parts: one that can be run in parallel - ## and tests that cannot be run in parallel with other packages - ## One drawback of this approach is that - ## if first set of tests fails, second one is not run - { PRIVILEGED_TESTS=true PATH=$(PATH):$(ROOT_DIR)/bpf $(GO_TEST) $(TEST_LDFLAGS) \ - $(TESTPKGS) $(GOTEST_BASE) $(GOTEST_COVER_OPTS) \ - && PRIVILEGED_TESTS=true PATH=$(PATH):$(ROOT_DIR)/bpf $(GO_TEST) $(TEST_LDFLAGS) \ - $(UNPARALLELTESTPKGS) $(GOTEST_BASE) -json -covermode=count -coverprofile=coverage2.out -p 1 --tags=unparallel; } | $(GOTEST_FORMATTER) - tail -n+2 coverage2.out >> coverage.out - rm coverage2.out - $(MAKE) generate-cov - -start-kvstores: ## Start running kvstores (etcd container) for integration tests. -ifeq ($(SKIP_KVSTORES),"false") - @echo Starting key-value store container... - -$(QUIET)$(CONTAINER_ENGINE) rm -f "cilium-etcd-test-container" 2> /dev/null - $(QUIET)$(CONTAINER_ENGINE) run -d \ - -e ETCD_UNSUPPORTED_ARCH=$(GOARCH) \ - --name "cilium-etcd-test-container" \ - -p 4002:4001 \ - $(ETCD_IMAGE) \ - etcd -name etcd0 \ - -advertise-client-urls http://0.0.0.0:4001 \ - -listen-client-urls http://0.0.0.0:4001 \ - -listen-peer-urls http://0.0.0.0:2380 \ - -initial-cluster-token etcd-cluster-1 \ - -initial-cluster-state new -endif - -stop-kvstores: ## Forcefully removes running kvstore components (etcd container) for integration tests. -ifeq ($(SKIP_KVSTORES),"false") - $(QUIET)$(CONTAINER_ENGINE) rm -f "cilium-etcd-test-container" -endif - -generate-cov: ## Generate HTML coverage report at coverage-all.html. - -@# Remove generated code from coverage -ifneq ($(SKIP_COVERAGE),) - @echo "Skipping generate-cov because SKIP_COVERAGE is set." -else - $(QUIET) grep -Ev '(^github.com/cilium/cilium/api/v1)|(generated.deepcopy.go)|(^github.com/cilium/cilium/pkg/k8s/client/)' \ - coverage.out > coverage.out.tmp - $(QUIET)$(GO) tool cover -html=coverage.out.tmp -o=coverage-all.html - $(QUIET) rm coverage.out.tmp -endif - @rmdir ./daemon/1 ./daemon/1_backup 2> /dev/null || true - -integration-tests: start-kvstores ## Run Go tests including ones that are marked as integration tests. - @$(ECHO_CHECK) running integration tests... - INTEGRATION_TESTS=true $(GO_TEST) $(TEST_UNITTEST_LDFLAGS) $(TESTPKGS) $(GOTEST_BASE) $(GOTEST_COVER_OPTS) | $(GOTEST_FORMATTER) - $(MAKE) generate-cov - $(MAKE) stop-kvstores - -bench: start-kvstores ## Run benchmarks for Cilium integration-tests in the repository. - $(GO_TEST) $(TEST_UNITTEST_LDFLAGS) $(GOTEST_BASE) $(BENCHFLAGS) $(TESTPKGS) - $(MAKE) stop-kvstores - -bench-privileged: ## Run benchmarks for privileged tests. - PRIVILEGED_TESTS=true $(GO_TEST) $(TEST_UNITTEST_LDFLAGS) $(GOTEST_BASE) $(BENCHFLAGS) $(TESTPKGS) - -clean-tags: ## Remove all the tags files from the repository. - @$(ECHO_CLEAN) tags - @-rm -f cscope.out cscope.in.out cscope.po.out cscope.files tags - -.PHONY: cscope.files -cscope.files: ## Generate cscope.files with the list of all files to generate ctags for. - @# Argument to -f must be double-quoted since shell removes backslashes that appear - @# before newlines. Otherwise, backslashes will appear in the output file. - @go list -f "{{ \$$p := .ImportPath }} \ - {{- range .GoFiles }}{{ printf \"%s/%s\n\" \$$p . }}{{ end }} \ - {{- range .TestGoFiles }}{{ printf \"%s/%s\n\" \$$p . }}{{ end }}" ./... \ - | sed 's#github.com/cilium/cilium/##g' | sort | uniq > cscope.files - - @echo "$(BPF_SRCFILES)" | sed 's/ /\n/g' | sort >> cscope.files - -tags: cscope.files ## Generate tags for Go and BPF source files. - @ctags -L cscope.files - cscope -R -b -q - -clean-container: ## Perform `make clean` for each component required in cilium-agent container. - -$(QUIET) for i in $(SUBDIRS_CILIUM_CONTAINER); do $(MAKE) $(SUBMAKEOPTS) -C $$i clean; done - -clean: ## Perform overall cleanup for Cilium. - -$(QUIET) for i in $(SUBDIRS); do $(MAKE) $(SUBMAKEOPTS) -C $$i clean; done - -veryclean: ## Perform complete cleanup for container engine images(including build cache). - -$(QUIET) $(CONTAINER_ENGINE) image prune -af - -$(QUIET) $(CONTAINER_ENGINE) builder prune -af - -install-bpf: ## Copies over the BPF source files from bpf/ to /var/lib/cilium/bpf/ - $(QUIET)$(INSTALL) -m 0750 -d $(DESTDIR)$(LOCALSTATEDIR)/lib/cilium - -rm -rf $(DESTDIR)$(LOCALSTATEDIR)/lib/cilium/bpf/* - $(foreach bpfsrc,$(BPF_SRCFILES), $(INSTALL) -D -m 0644 $(bpfsrc) $(DESTDIR)$(LOCALSTATEDIR)/lib/cilium/$(bpfsrc);) - -install: install-bpf ## Performs install for all the Cilium sub components (daemon, operator, relay etc.) - $(QUIET)$(INSTALL) -m 0755 -d $(DESTDIR)$(BINDIR) - for i in $(SUBDIRS); do $(MAKE) $(SUBMAKEOPTS) -C $$i install; done - -install-container: install-bpf ## Performs install for all components required for cilium-agent container. - $(QUIET)$(INSTALL) -m 0755 -d $(DESTDIR)$(BINDIR) - for i in $(SUBDIRS_CILIUM_CONTAINER); do $(MAKE) $(SUBMAKEOPTS) -C $$i install; done - -install-container-binary: install-bpf ## Install binaries for all components required for cilium-agent container. - $(QUIET)$(INSTALL) -m 0755 -d $(DESTDIR)$(BINDIR) - for i in $(SUBDIRS_CILIUM_CONTAINER); do $(MAKE) $(SUBMAKEOPTS) -C $$i install-binary; done - -install-bash-completion: ## Install bash completion for all components required for cilium-agent container. - $(QUIET)$(INSTALL) -m 0755 -d $(DESTDIR)$(BINDIR) - for i in $(SUBDIRS_CILIUM_CONTAINER); do $(MAKE) $(SUBMAKEOPTS) -C $$i install-bash-completion; done - -install-container-binary-operator: ## Install binaries for all components required for cilium-operator container. - $(QUIET)$(INSTALL) -m 0755 -d $(DESTDIR)$(BINDIR) - $(MAKE) $(SUBMAKEOPTS) -C $(SUBDIR_OPERATOR_CONTAINER) install - -install-container-binary-operator-generic: ## Install binaries for all components required for cilium-operator generic variant container. - $(QUIET)$(INSTALL) -m 0755 -d $(DESTDIR)$(BINDIR) - $(MAKE) $(SUBMAKEOPTS) -C $(SUBDIR_OPERATOR_CONTAINER) install-generic - -install-container-binary-operator-aws: ## Install binaries for all components required for cilium-operator aws variant container. - $(QUIET)$(INSTALL) -m 0755 -d $(DESTDIR)$(BINDIR) - $(MAKE) $(SUBMAKEOPTS) -C $(SUBDIR_OPERATOR_CONTAINER) install-aws - -install-container-binary-operator-azure: ## Install binaries for all components required for cilium-operator azure variant container. - $(QUIET)$(INSTALL) -m 0755 -d $(DESTDIR)$(BINDIR) - $(MAKE) $(SUBMAKEOPTS) -C $(SUBDIR_OPERATOR_CONTAINER) install-azure - -install-container-binary-operator-alibabacloud: ## Install binaries for all components required for cilium-operator alibabacloud variant container. - $(QUIET)$(INSTALL) -m 0755 -d $(DESTDIR)$(BINDIR) - $(MAKE) $(SUBMAKEOPTS) -C $(SUBDIR_OPERATOR_CONTAINER) install-alibabacloud - -install-container-binary-hubble-relay: - $(QUIET)$(INSTALL) -m 0755 -d $(DESTDIR)$(BINDIR) - $(MAKE) $(SUBMAKEOPTS) -C $(SUBDIR_RELAY_CONTAINER) install-binary - -# Workaround for not having git in the build environment -# Touch the file only if needed -GIT_VERSION: force - @if [ "$(GIT_VERSION)" != "`cat 2>/dev/null GIT_VERSION`" ] ; then echo "$(GIT_VERSION)" >GIT_VERSION; fi - -check_deps: - @$(CILIUM_CLI) --help > /dev/null 2>&1 || ( echo "ERROR: '$(CILIUM_CLI)' not found. Please install it." && exit 1) - -include Makefile.kind - --include Makefile.docker - -manifests: ## Generate K8s manifests e.g. CRD, RBAC etc. - contrib/scripts/k8s-manifests-gen.sh - -.PHONY: generate-apis -generate-apis: generate-api generate-health-api generate-hubble-api generate-operator-api generate-kvstoremesh-api generate-sdp-api - -generate-api: api/v1/openapi.yaml ## Generate cilium-agent client, model and server code from openapi spec. - @$(ECHO_GEN)api/v1/openapi.yaml - -$(QUIET)$(SWAGGER) generate server -s server -a restapi \ - -t api/v1 \ - -f api/v1/openapi.yaml \ - --default-scheme=unix \ - -C api/v1/cilium-server.yml \ - -r hack/spdx-copyright-header.txt - -$(QUIET)$(SWAGGER) generate client -a restapi \ - -t api/v1 \ - -f api/v1/openapi.yaml \ - -r hack/spdx-copyright-header.txt - @# sort goimports automatically - -$(QUIET)$(GO) run golang.org/x/tools/cmd/goimports -w ./api/v1/client ./api/v1/models ./api/v1/server - -generate-health-api: api/v1/health/openapi.yaml ## Generate cilium-health client, model and server code from openapi spec. - @$(ECHO_GEN)api/v1/health/openapi.yaml - -$(QUIET)$(SWAGGER) generate server -s server -a restapi \ - -t api/v1 \ - -t api/v1/health/ \ - -f api/v1/health/openapi.yaml \ - --default-scheme=unix \ - -C api/v1/cilium-server.yml \ - -r hack/spdx-copyright-header.txt - -$(QUIET)$(SWAGGER) generate client -a restapi \ - -t api/v1 \ - -t api/v1/health/ \ - -f api/v1/health/openapi.yaml \ - -r hack/spdx-copyright-header.txt - @# sort goimports automatically - -$(QUIET)$(GO) run golang.org/x/tools/cmd/goimports -w ./api/v1/health - -generate-operator-api: api/v1/operator/openapi.yaml ## Generate cilium-operator client, model and server code from openapi spec. - @$(ECHO_GEN)api/v1/operator/openapi.yaml - -$(QUIET)$(SWAGGER) generate server -s server -a restapi \ - -t api/v1 \ - -t api/v1/operator/ \ - -f api/v1/operator/openapi.yaml \ - --default-scheme=http \ - -C api/v1/cilium-server.yml \ - -r hack/spdx-copyright-header.txt - -$(QUIET)$(SWAGGER) generate client -a restapi \ - -t api/v1 \ - -t api/v1/operator/ \ - -f api/v1/operator/openapi.yaml \ - -r hack/spdx-copyright-header.txt - @# sort goimports automatically - -$(QUIET)$(GO) run golang.org/x/tools/cmd/goimports -w ./api/v1/operator - -generate-kvstoremesh-api: api/v1/kvstoremesh/openapi.yaml ## Generate kvstoremesh client, model and server code from openapi spec. - @$(ECHO_GEN)api/v1/kvstoremesh/openapi.yaml - -$(QUIET)$(SWAGGER) generate server -s server -a restapi \ - -t api/v1 \ - -t api/v1/kvstoremesh/ \ - -f api/v1/kvstoremesh/openapi.yaml \ - --default-scheme=http \ - -C api/v1/cilium-server.yml \ - -r hack/spdx-copyright-header.txt - -$(QUIET)$(SWAGGER) generate client -a restapi \ - -t api/v1 \ - -t api/v1/kvstoremesh/ \ - -f api/v1/kvstoremesh/openapi.yaml \ - -r hack/spdx-copyright-header.txt - @# sort goimports automatically - -$(QUIET)$(GO) run golang.org/x/tools/cmd/goimports -w ./api/v1/kvstoremesh - -generate-hubble-api: api/v1/flow/flow.proto api/v1/peer/peer.proto api/v1/observer/observer.proto api/v1/relay/relay.proto ## Generate hubble proto Go sources. - $(QUIET) $(MAKE) $(SUBMAKEOPTS) -C api/v1 - - -generate-sdp-api: api/v1/standalone-dns-proxy/standalone-dns-proxy.proto - $(QUIET) $(MAKE) $(SUBMAKEOPTS) -C api/v1 - -define generate_k8s_protobuf - $(GO) install k8s.io/code-generator/cmd/go-to-protobuf/protoc-gen-gogo && \ - $(GO) install golang.org/x/tools/cmd/goimports && \ - $(GO) run k8s.io/code-generator/cmd/go-to-protobuf \ - --apimachinery-packages='-k8s.io/apimachinery/pkg/util/intstr,$\ - -k8s.io/apimachinery/pkg/api/resource,$\ - -k8s.io/apimachinery/pkg/runtime/schema,$\ - -k8s.io/apimachinery/pkg/runtime,$\ - -k8s.io/apimachinery/pkg/apis/meta/v1,$\ - -k8s.io/apimachinery/pkg/apis/meta/v1beta1'\ - --drop-embedded-fields="github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1.TypeMeta" \ - --proto-import="$$PWD" \ - --proto-import="$$PWD/vendor" \ - --proto-import="$$PWD/tools/protobuf" \ - --packages=$(subst $(newline),$(comma),$(1)) \ - --go-header-file "$$PWD/hack/custom-boilerplate.go.txt" \ - --output-dir=$$GOPATH/src -endef - -define K8S_PROTO_PACKAGES -github.com/cilium/cilium/pkg/k8s/slim/k8s/api/core/v1 -github.com/cilium/cilium/pkg/k8s/slim/k8s/api/discovery/v1 -github.com/cilium/cilium/pkg/k8s/slim/k8s/api/discovery/v1beta1 -github.com/cilium/cilium/pkg/k8s/slim/k8s/api/networking/v1 -github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/apiextensions/v1 -github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1 -github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1beta1 -github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/util/intstr -endef - -.PHONY: generate-k8s-api-local -generate-k8s-api-local: - $(ASSERT_CILIUM_MODULE) - - $(eval TMPDIR := $(shell mktemp -d -t cilium.tmpXXXXXXXX)) - - $(QUIET) $(call generate_k8s_protobuf,${K8S_PROTO_PACKAGES},"$(TMPDIR)") - $(QUIET) contrib/scripts/k8s-code-gen.sh "$(TMPDIR)" - - $(QUIET) rm -rf "$(TMPDIR)" - -.PHONY: generate-k8s-api -generate-k8s-api: ## Generate Cilium k8s API client, deepcopy and deepequal Go sources. - contrib/scripts/builder.sh \ - $(MAKE) -C /go/src/github.com/cilium/cilium/ generate-k8s-api-local - -check-k8s-clusterrole: ## Ensures there is no diff between preflight's clusterrole and runtime's clusterrole. - ./contrib/scripts/check-preflight-clusterrole.sh - -##@ Development -vps: ## List all the running vagrant VMs. - VBoxManage list runningvms - -reload: ## Reload cilium-agent and cilium-docker systemd service after installing built binaries. - sudo systemctl stop cilium cilium-docker - sudo $(MAKE) install - sudo systemctl start cilium cilium-docker - sleep 6 - cilium status - -release: ## Perform a Git release for Cilium. - @echo "Visit https://github.com/cilium/release/issues/new/choose to initiate the release process." - -gofmt: ## Run gofmt on Go source files in the repository. - $(QUIET)$(GO) fmt ./... - -govet: ## Run govet on Go source files in the repository. - @$(ECHO_CHECK) vetting all packages... - $(QUIET) $(GO_VET) ./... - -golangci-lint: ## Run golangci-lint -ifneq (,$(findstring $(GOLANGCILINT_WANT_VERSION:v%=%),$(GOLANGCILINT_VERSION))) - @$(ECHO_CHECK) golangci-lint $(GOLANGCI_LINT_ARGS) - $(QUIET) golangci-lint run $(GOLANGCI_LINT_ARGS) -else - $(QUIET) $(CONTAINER_ENGINE) run --rm -v `pwd`:/app -w /app docker.io/golangci/golangci-lint:$(GOLANGCILINT_WANT_VERSION)@$(GOLANGCILINT_IMAGE_SHA) golangci-lint run $(GOLANGCI_LINT_ARGS) -endif - -golangci-lint-fix: ## Run golangci-lint to automatically fix warnings - $(QUIET)$(MAKE) golangci-lint GOLANGCI_LINT_ARGS="--fix" - -lint: golangci-lint - -lint-fix: golangci-lint-fix - -logging-subsys-field: ## Validate logrus subsystem field for logs in Go source code. - @$(ECHO_CHECK) contrib/scripts/check-logging-subsys-field.sh - $(QUIET) contrib/scripts/check-logging-subsys-field.sh - -check-microk8s: ## Validate if microk8s is ready to install cilium. - @$(ECHO_CHECK) microk8s is ready... - $(QUIET)microk8s.status >/dev/null \ - || (echo "Error: Microk8s is not running" && exit 1) - -LOCAL_IMAGE_TAG=local -microk8s: export DOCKER_REGISTRY=localhost:32000 -microk8s: export LOCAL_AGENT_IMAGE=$(DOCKER_REGISTRY)/$(DOCKER_DEV_ACCOUNT)/cilium-dev:$(LOCAL_IMAGE_TAG) -microk8s: export LOCAL_OPERATOR_IMAGE=$(DOCKER_REGISTRY)/$(DOCKER_DEV_ACCOUNT)/operator:$(LOCAL_IMAGE_TAG) -microk8s: check-microk8s ## Build cilium-dev docker image and import to microk8s - $(QUIET)$(MAKE) dev-docker-image DOCKER_IMAGE_TAG=$(LOCAL_IMAGE_TAG) - @echo " DEPLOY image to microk8s ($(LOCAL_AGENT_IMAGE))" - $(QUIET)./contrib/scripts/microk8s-import.sh $(LOCAL_AGENT_IMAGE) - $(QUIET)$(MAKE) dev-docker-operator-image DOCKER_IMAGE_TAG=$(LOCAL_IMAGE_TAG) - @echo " DEPLOY image to microk8s ($(LOCAL_OPERATOR_IMAGE))" - $(QUIET)./contrib/scripts/microk8s-import.sh $(LOCAL_OPERATOR_IMAGE) - -precheck: logging-subsys-field ## Peform build precheck for the source code. -ifeq ($(SKIP_K8S_CODE_GEN_CHECK),"false") - @$(ECHO_CHECK) contrib/scripts/check-k8s-code-gen.sh - $(QUIET) contrib/scripts/check-k8s-code-gen.sh -endif - @$(ECHO_CHECK) contrib/scripts/check-fmt.sh - $(QUIET) contrib/scripts/check-fmt.sh - @$(ECHO_CHECK) contrib/scripts/check-log-newlines.sh - $(QUIET) contrib/scripts/check-log-newlines.sh - @$(ECHO_CHECK) contrib/scripts/check-test-tags.sh - $(QUIET) contrib/scripts/check-test-tags.sh - @$(ECHO_CHECK) contrib/scripts/lock-check.sh - $(QUIET) contrib/scripts/lock-check.sh - @$(ECHO_CHECK) contrib/scripts/check-viper.sh - $(QUIET) contrib/scripts/check-viper.sh -ifeq ($(SKIP_CUSTOMVET_CHECK),"false") - @$(ECHO_CHECK) contrib/scripts/custom-vet-check.sh - $(QUIET) contrib/scripts/custom-vet-check.sh -endif - @$(ECHO_CHECK) contrib/scripts/check-time.sh - $(QUIET) contrib/scripts/check-time.sh - @$(ECHO_CHECK) contrib/scripts/check-go-testdata.sh - $(QUIET) contrib/scripts/check-go-testdata.sh - @$(ECHO_CHECK) contrib/scripts/check-source-info.sh - $(QUIET) contrib/scripts/check-source-info.sh - @$(ECHO_CHECK) contrib/scripts/check-xfrmstate.sh - $(QUIET) contrib/scripts/check-xfrmstate.sh - @$(ECHO_CHECK) contrib/scripts/check-legacy-header-guard.sh - $(QUIET) contrib/scripts/check-legacy-header-guard.sh - @$(ECHO_CHECK) contrib/scripts/check-logrus.sh - $(QUIET) contrib/scripts/check-logrus.sh - @$(ECHO_CHECK) contrib/scripts/check-safenetlink.sh - $(QUIET) contrib/scripts/check-safenetlink.sh - @$(ECHO_CHECK) contrib/scripts/check-datapathconfig.sh - $(QUIET) contrib/scripts/check-datapathconfig.sh - -pprof-heap: ## Get Go pprof heap profile. - $(QUIET)$(GO) tool pprof http://localhost:6060/debug/pprof/heap - -pprof-profile: ## Get Go pprof profile. - $(QUIET)$(GO) tool pprof http://localhost:6060/debug/pprof/profile - -pprof-block: ## Get Go pprof block profile. - $(QUIET)$(GO) tool pprof http://localhost:6060/debug/pprof/block - -pprof-trace-5s: ## Get Go pprof trace for a duration of 5 seconds. - curl http://localhost:6060/debug/pprof/trace?seconds=5 - -pprof-mutex: ## Get Go pprof mutex profile. - $(QUIET)$(GO) tool pprof http://localhost:6060/debug/pprof/mutex - -update-authors: ## Update AUTHORS file for Cilium repository. - @echo "Updating AUTHORS file..." - @echo "The following people, in alphabetical order, have either authored or signed" > AUTHORS - @echo "off on commits in the Cilium repository:" >> AUTHORS - @echo "" >> AUTHORS - @contrib/scripts/extract_authors.sh >> AUTHORS - @cat .authors.aux >> AUTHORS - -generate-crd-docs: ## Generate CRD List for documentation - $(QUIET)$(GO) run ./tools/crdlistgen - -test-docs: ## Build HTML documentation. - $(MAKE) -C Documentation html - -render-docs: ## Run server with live preview to render documentation. - $(MAKE) -C Documentation live-preview - -manpages: ## Generate manpage for Cilium CLI. - -rm -r man - mkdir -p man - cilium cmdman -d man - -install-manpages: ## Install manpages the Cilium CLI. - cp man/* /usr/local/share/man/man1/ - mandb - -postcheck: build ## Run Cilium build postcheck (update-cmdref, build documentation etc.). - $(QUIET) SKIP_BUILD=true $(MAKE) $(SUBMAKEOPTS) -C Documentation check - -licenses-all: ## Generate file with all the License from dependencies. - @$(GO) run ./tools/licensegen > LICENSE.all || ( rm -f LICENSE.all ; false ) - -dev-doctor: ## Run Cilium dev-doctor to validate local development environment. - $(QUIET)$(GO) version 2>/dev/null || ( echo "go not found, see https://golang.org/doc/install" ; false ) - $(QUIET)$(GO) run ./tools/dev-doctor - -help: ## Display help for the Makefile, from https://www.thapaliya.com/en/writings/well-documented-makefiles/. - $(call print_help_from_makefile) - @# There is also a list of target we have to manually put the information about. - @# These are templated targets. - $(call print_help_line,"docker-cilium-image","Build cilium-agent docker image") - $(call print_help_line,"dev-docker-image","Build cilium-agent development docker image") - $(call print_help_line,"dev-docker-image-debug","Build cilium-agent development docker debug image") - $(call print_help_line,"docker-plugin-image","Build cilium-docker plugin image") - $(call print_help_line,"docker-hubble-relay-image","Build hubble-relay docker image") - $(call print_help_line,"docker-clustermesh-apiserver-image","Build docker image for Cilium clustermesh APIServer") - $(call print_help_line,"docker-operator-image","Build cilium-operator docker image") - $(call print_help_line,"docker-operator-*-image","Build platform specific cilium-operator images(alibabacloud, aws, azure, generic)") - $(call print_help_line,"docker-operator-*-image-debug","Build platform specific cilium-operator debug images(alibabacloud, aws, azure, generic)") - $(call print_help_line,"docker-*-image-unstripped","Build unstripped version of above docker images(cilium, hubble-relay, operator etc.)") - -.PHONY: help clean clean-container dev-doctor force generate-api generate-health-api generate-operator-api generate-kvstoremesh-api generate-hubble-api generate-sdp-api install licenses-all veryclean run_bpf_tests run-builder -force :; - -BPF_TEST_FILE ?= "" -BPF_TEST_DUMP_CTX ?= "" -BPF_TEST_VERBOSE ?= 0 - -run_bpf_tests: ## Build and run the BPF unit tests using the cilium-builder container image. - DOCKER_ARGS=--privileged RUN_AS_ROOT=1 contrib/scripts/builder.sh \ - "make" "-j$(shell nproc)" "-C" "bpf/tests/" "run" "BPF_TEST_FILE=$(BPF_TEST_FILE)" "BPF_TEST_DUMP_CTX=$(BPF_TEST_DUMP_CTX)" "V=$(BPF_TEST_VERBOSE)" - -run-builder: ## Drop into a shell inside a container running the cilium-builder image. - DOCKER_ARGS=-it contrib/scripts/builder.sh bash - -.PHONY: renovate-local -renovate-local: ## Run a local linter for the renovate configuration - $(CONTAINER_ENGINE) run --rm -ti \ - -e LOG_LEVEL=debug \ - -e GITHUB_COM_TOKEN="$(gh auth token)" \ - -v /tmp:/tmp \ - -v $(ROOT_DIR):/usr/src/app \ - docker.io/renovate/renovate:slim \ - renovate --platform=local diff --git a/vendor/github.com/cilium/cilium/Makefile.defs b/vendor/github.com/cilium/cilium/Makefile.defs deleted file mode 100644 index f96711c4aa..0000000000 --- a/vendor/github.com/cilium/cilium/Makefile.defs +++ /dev/null @@ -1,227 +0,0 @@ -# Copyright Authors of Cilium -# SPDX-License-Identifier: Apache-2.0 - -SHELL := /usr/bin/env bash -.SHELLFLAGS := -eu -o pipefail -c - -# define a function replacing spaces with commas in a list -empty := -space := $(empty) $(empty) -comma := , -join-with-comma = $(subst $(space),$(comma),$(strip $1)) - -define newline - - -endef - -ROOT_DIR := $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) -RELATIVE_DIR := $(shell echo $(realpath .) | sed "s;$(ROOT_DIR)[/]*;;") -include $(ROOT_DIR)/Makefile.quiet - -PREFIX?=/usr -BINDIR?=$(PREFIX)/bin -CNIBINDIR?=/opt/cni/bin -CNICONFDIR?=/etc/cni/net.d -LIBDIR?=$(PREFIX)/lib -LOCALSTATEDIR?=/var -RUNDIR?=/var/run -CONFDIR?=/etc - -export GO ?= go -NATIVE_ARCH = $(shell GOARCH= $(GO) env GOARCH) -export GOARCH ?= $(NATIVE_ARCH) - -INSTALL = install - -CONTAINER_ENGINE?=docker -DOCKER_FLAGS?= -DOCKER_BUILD_FLAGS?= - -# use gsed if available, otherwise use sed. -# gsed is needed for MacOS to make in-place replacement work correctly. -SED ?= $(if $(shell command -v gsed),gsed,sed) - -# Set DOCKER_DEV_ACCOUNT with "cilium" by default -ifeq ($(DOCKER_DEV_ACCOUNT),) - DOCKER_DEV_ACCOUNT=cilium -endif - -ifneq ($(CI_BUILD),) - DOCKER_IMAGE_SUFFIX=-ci - DOCKER_IMAGE_TAG=$(shell git rev-parse HEAD) -endif - -# Set DOCKER_IMAGE_TAG with "latest" by default -ifeq ($(DOCKER_IMAGE_TAG),) - DOCKER_IMAGE_TAG=latest -endif - -# renovate: datasource=docker depName=gcr.io/etcd-development/etcd -ETCD_IMAGE_VERSION = v3.5.19 -ETCD_IMAGE_SHA = sha256:c415170328bbb2cb497c79dbd8494b1c8d58886dccf95fbfdd3845c13c6d1ca3 -ETCD_IMAGE=gcr.io/etcd-development/etcd:$(ETCD_IMAGE_VERSION)@$(ETCD_IMAGE_SHA) - -CILIUM_BUILDER_IMAGE=$(shell cat $(ROOT_DIR)/images/cilium/Dockerfile | grep "ARG CILIUM_BUILDER_IMAGE=" | cut -d"=" -f2) - -export CILIUM_CLI ?= cilium -export KUBECTL ?= kubectl - -# Till the self-hosted renovate PR #30185 is merged, we might need to run the below -# command locally for any version update. -# make generate-api generate-health-api generate-hubble-api generate-operator-api generate-kvstoremesh-api -# renovate: datasource=docker depName=quay.io/goswagger/swagger -SWAGGER_VERSION = 0.31.0 -SWAGGER_IMAGE_SHA = sha256:5f36c14131ea569ad687ac546d6cdd2ccf0feff662eae5f5bf37d9d8a0b51cbc -SWAGGER := $(CONTAINER_ENGINE) run -u $(shell id -u):$(shell id -g) --rm -v $(ROOT_DIR):$(ROOT_DIR) -w $(ROOT_DIR) --entrypoint swagger quay.io/goswagger/swagger:$(SWAGGER_VERSION)@$(SWAGGER_IMAGE_SHA) - -# go build/test/clean flags -# these are declared here so they are treated explicitly -# as non-immediate variables -GO_BUILD_FLAGS ?= -GO_TEST_FLAGS ?= -GO_CLEAN_FLAGS ?= -GO_BUILD_LDFLAGS ?= -# go build/test -tags values -GO_TAGS_FLAGS += osusergo - -# This is declared here as it is needed to change the covermode depending on if -# RACE is specified. -GOTEST_COVER_OPTS = - -# By default, just print go test output immediately to the terminal. If tparse -# is installed, use it to format the output. Use -progress instead of -follow, -# as the latter is too verbose for most of the test suite. -GOTEST_FORMATTER ?= cat -ifneq ($(shell command -v tparse),) - GOTEST_COVER_OPTS += -json - GOTEST_FORMATTER_FLAGS := -ifneq ($(V),0) - GOTEST_FORMATTER_FLAGS += -follow -endif -ifneq ($(LOG_CODEOWNERS),) - GOTEST_FORMATTER = tee >($(GO) run ./tools/testowners) >(tparse $(GOTEST_FORMATTER_FLAGS)) >/dev/null -else - GOTEST_FORMATTER = tparse $(GOTEST_FORMATTER_FLAGS) -endif -endif - -# renovate: datasource=docker depName=golangci/golangci-lint -GOLANGCILINT_WANT_VERSION = v1.64.5 -GOLANGCILINT_IMAGE_SHA = sha256:9faef4dda4304c4790a14c5b8c8cd8c2715a8cb754e13f61d8ceaa358f5a454a -GOLANGCILINT_VERSION = $(shell golangci-lint version --format short 2>/dev/null) - -VERSION = $(shell cat $(dir $(lastword $(MAKEFILE_LIST)))/VERSION) -VERSION_MAJOR = $(shell cat $(dir $(lastword $(MAKEFILE_LIST)))/VERSION | cut -d. -f1) -# Use git only if in a Git repo -ifneq ($(wildcard $(dir $(lastword $(MAKEFILE_LIST)))/.git/HEAD),) - GIT_VERSION = $(shell git show -s --format='format:%h %aI') -else - GIT_VERSION = $(shell cat 2>/dev/null $(ROOT_DIR)/GIT_VERSION) -endif -FULL_BUILD_VERSION = $(VERSION) $(GIT_VERSION) -GO_BUILD_LDFLAGS += -X "github.com/cilium/cilium/pkg/version.ciliumVersion=$(FULL_BUILD_VERSION)" - -ifeq ($(NOSTRIP),) - # Note: these options will not remove annotations needed for stack - # traces, so panic backtraces will still be readable. - # - # -w: Omit the DWARF symbol table. - # -s: Omit the symbol table and debug information. - GO_BUILD_LDFLAGS += -s -w -endif - -ifneq ($(wildcard $(dir $(lastword $(MAKEFILE_LIST)))/images/cilium/Dockerfile),) - CILIUM_ENVOY_REF=$(shell sed -E -e 's/^ARG CILIUM_ENVOY_IMAGE=([^ ]*)/\1/p;d' < $(ROOT_DIR)/images/cilium/Dockerfile) - CILIUM_ENVOY_SHA=$(shell echo $(CILIUM_ENVOY_REF) | sed -E -e 's/[^/]*\/[^:]*:(.*-)?([^:@]*).*/\2/p;d') - GO_BUILD_LDFLAGS += -X "github.com/cilium/cilium/pkg/envoy.requiredEnvoyVersionSHA=$(CILIUM_ENVOY_SHA)" -endif - -# Use git only if in a Git repo, otherwise find the files from the file system -BPF_SRCFILES_IGNORE = bpf/.gitignore -ifneq ($(wildcard $(dir $(lastword $(MAKEFILE_LIST)))/.git/HEAD),) - BPF_SRCFILES := $(shell git ls-files $(ROOT_DIR)/bpf/ | LC_ALL=C sort | tr "\n" ' ') -else - # this line has to be in-sync with bpf/.gitignore, please note usage of make patterns like `%.i` - BPF_SRCFILES_IGNORE += bpf/%.i bpf/%.s bpf/.rebuild_all - BPF_SRCFILES := $(shell find $(ROOT_DIR)/bpf/ -type f | LC_ALL=C sort | tr "\n" ' ') -endif - -# ROOT_DIR can be either `../` or absolute path, each of these need to be stripped -BPF_SRCFILES := $(filter-out $(BPF_SRCFILES_IGNORE),$(subst ../,,$(subst $(ROOT_DIR)/,,$(BPF_SRCFILES)))) - -GO_BUILD_FLAGS += -mod=vendor -GO_TEST_FLAGS += -mod=vendor -vet=all -GO_CLEAN_FLAGS += -mod=vendor - -GO_BUILD = CGO_ENABLED=0 $(GO) build - -# Support CGO cross-compiling for amd64 and arm64 targets -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_WITH_CGO = CGO_ENABLED=1 $(CGO_CC) $(GO) build - -ifneq ($(RACE),) - GO_BUILD_FLAGS += -race - GO_TEST_FLAGS += -race - GOTEST_COVER_OPTS += -covermode=atomic - - # GO_BUILD becomes GO_BUILD_WITH_CGO as `-race` requires CGO - GO_BUILD = $(GO_BUILD_WITH_CGO) - ifeq ($(LOCKDEBUG),) - LOCKDEBUG=1 - endif -else - GOTEST_COVER_OPTS += -covermode=count -endif - -ifneq ($(LOCKDEBUG),) - GO_TAGS_FLAGS += lockdebug -endif - -GO_BUILD_FLAGS += -ldflags '$(GO_BUILD_LDFLAGS) $(EXTRA_GO_BUILD_LDFLAGS)' -tags=$(call join-with-comma,$(GO_TAGS_FLAGS)) $(EXTRA_GO_BUILD_FLAGS) -GO_TEST_FLAGS += -tags=$(call join-with-comma,$(GO_TAGS_FLAGS)) - -ifeq ($(NOOPT),1) - GO_BUILD_FLAGS += -gcflags="all=-N -l" -endif - -GO_BUILD += $(GO_BUILD_FLAGS) -GO_BUILD_WITH_CGO += $(GO_BUILD_FLAGS) - -GO_TEST = CGO_ENABLED=0 $(GO) test $(GO_TEST_FLAGS) -GO_CLEAN = $(GO) clean $(GO_CLEAN_FLAGS) - -GO_VET = $(GO) vet -GO_LIST = $(GO) list - -HELM_TOOLBOX_VERSION ?= "v1.1.0" -HELM_TOOLBOX_SHA ?= "961693f182b9b456ed90e5274ac5df81e4af4343104e252666959cdf9570ce9e" -HELM_TOOLBOX_IMAGE ?= "quay.io/cilium/helm-toolbox:$(HELM_TOOLBOX_VERSION)@sha256:$(HELM_TOOLBOX_SHA)" - -YQ_VERSION ?= "4.40.5" -YQ_SHA ?= "32be61dc94d0acc44f513ba69d0fc05f1f92c2e760491f2a27e11fc13cde6327" -YQ_IMAGE ?= "docker.io/mikefarah/yq:$(YQ_VERSION)@sha256:$(YQ_SHA)" - -define print_help_line - @printf " \033[36m%-29s\033[0m %s.\n" $(1) $(2) -endef - -define print_help_from_makefile - @awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m\033[0m\n"} /^[a-zA-Z0-9][a-zA-Z0-9 _-]*:.*?##/ { split($$1, targets, " "); for (i in targets) { printf " \033[36m%-28s\033[0m %s\n", targets[i], $$2 } } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST) -endef - -# Use to ensure the CWD, or any child of it, belongs to Cilium's go module. -CILIUM_GO_MODULE = github.com/cilium/cilium -CURRENT_GO_MODULE = $(shell go list -m) -define ASSERT_CILIUM_MODULE - $(if $(filter $(CILIUM_GO_MODULE), $(CURRENT_GO_MODULE)) ,, $(error "Could not locate Cilium's go.mod file, are you in Cilium's repository?")) -endef diff --git a/vendor/github.com/cilium/cilium/Makefile.docker b/vendor/github.com/cilium/cilium/Makefile.docker deleted file mode 100644 index 026d516872..0000000000 --- a/vendor/github.com/cilium/cilium/Makefile.docker +++ /dev/null @@ -1,153 +0,0 @@ -# Copyright Authors of Cilium -# SPDX-License-Identifier: Apache-2.0 - -DOCKER_BUILDER := default - -# Export with value expected by docker -export DOCKER_BUILDKIT=1 - -# Docker Buildx support. If ARCH is defined, a builder instance 'cross' -# on the local node is configured for amd64 and arm64 platform targets. -# Otherwise build on the current (typically default) builder for the host -# platform only. -ifdef ARCH - # Default to multi-arch builds, always create the builder for all the platforms we support - DOCKER_PLATFORMS := linux/arm64,linux/amd64 - DOCKER_BUILDER := $(shell docker buildx ls | grep -E -e "[a-zA-Z0-9-]+ \*" | cut -d ' ' -f1) - ifneq (,$(filter $(DOCKER_BUILDER),default desktop-linux)) - DOCKER_BUILDKIT_DRIVER := - ifdef DOCKER_BUILDKIT_IMAGE - DOCKER_BUILDKIT_DRIVER := --driver docker-container --driver-opt image=$(DOCKER_BUILDKIT_IMAGE) - endif - BUILDER_SETUP := $(shell docker buildx create --platform $(DOCKER_PLATFORMS) $(DOCKER_BUILDKIT_DRIVER) --use) - endif - # Override default for a single platform - ifneq ($(ARCH),multi) - DOCKER_PLATFORMS := linux/$(ARCH) - endif - DOCKER_FLAGS += --push --platform $(DOCKER_PLATFORMS) -else - ifeq ($(findstring --output,$(DOCKER_FLAGS)),) - ifeq ($(findstring --push,$(DOCKER_FLAGS)),) - # ARCH, --output, and --push are not specified, build for the host platform without pushing, mimicking regular docker build - DOCKER_FLAGS += --load - endif - endif -endif -DOCKER_EXISTS := $(shell command -v docker 2>/dev/null) -ifdef DOCKER_EXISTS -DOCKER_BUILDER := $(shell docker buildx ls | grep -E -e "[a-zA-Z0-9-]+ \*" | cut -d ' ' -f1) -endif - -##@ Docker Images -.PHONY: builder-info -builder-info: ## Print information about the docker builder that will be used for building images. - @echo "Using Docker Buildx builder \"$(DOCKER_BUILDER)\" with build flags \"$(DOCKER_FLAGS)\"." - -# Generic rule for augmented .dockerignore files. -GIT_IGNORE_FILES := $(shell find . -not -path "./vendor*" -name .gitignore -print) -.PRECIOUS: %.dockerignore -%.dockerignore: $(GIT_IGNORE_FILES) Makefile.docker - @-mkdir -p $(dir $@) - @echo "/hack" > $@ - @echo ".git" >> $@ - @echo "/Makefile.docker" >> $@ - @echo $(dir $(GIT_IGNORE_FILES)) | tr ' ' '\n' | xargs -P1 -I {DIR} -n1 sed \ - -e '# Remove lines with white space, comments and files that must be passed to docker, "$$" due to make. #' \ - -e '/^[[:space:]]*$$/d' -e '/^#/d' -e '/GIT_VERSION/d' \ - -e '# Apply pattern in all directories if it contains no "/", keep "!" up front. #' \ - -e '/^[^!/][^/]*$$/s<^<**/<' -e '/^![^/]*$$/s<^!> $@ - -DOCKER_REGISTRY ?= quay.io -ifeq ($(findstring /,$(DOCKER_DEV_ACCOUNT)),/) - # DOCKER_DEV_ACCOUNT already contains '/', assume it specifies a registry - IMAGE_REPOSITORY := $(DOCKER_DEV_ACCOUNT) -else - IMAGE_REPOSITORY := $(DOCKER_REGISTRY)/$(DOCKER_DEV_ACCOUNT) -endif - -# -# Template for Docker images. Paramaters are: -# $(1) image target name -# $(2) Dockerfile path -# $(3) image name stem (e.g., cilium, cilium-operator, etc) -# $(4) image tag -# $(5) target -# -define DOCKER_IMAGE_TEMPLATE -.PHONY: $(1) -$(1): GIT_VERSION $(2) $(2).dockerignore GIT_VERSION builder-info - $(ECHO_DOCKER)$(2) $(IMAGE_REPOSITORY)/$(IMAGE_NAME)$${UNSTRIPPED}:$(4) - $(eval IMAGE_NAME := $(subst %,$$$$*,$(3))) -ifeq ($(5),debug) - @export NOSTRIP=1 -endif - $(QUIET) $(CONTAINER_ENGINE) buildx build -f $(subst %,$$*,$(2)) \ - $(DOCKER_BUILD_FLAGS) $(DOCKER_FLAGS) \ - $(if $(BASE_IMAGE),--build-arg BASE_IMAGE=$(BASE_IMAGE),) \ - --build-arg MODIFIERS="NOSTRIP=$${NOSTRIP} NOOPT=${NOOPT} LOCKDEBUG=${LOCKDEBUG} RACE=${RACE} V=${V} LIBNETWORK_PLUGIN=${LIBNETWORK_PLUGIN} ${ADDITIONAL_MODIFIERS}" \ - --build-arg CILIUM_SHA=$(firstword $(GIT_VERSION)) \ - --build-arg OPERATOR_VARIANT=$(IMAGE_NAME) \ - --build-arg DEBUG_HOLD=$(DEBUG_HOLD) \ - --target $(5) \ - -t $(IMAGE_REPOSITORY)/$(IMAGE_NAME)$${UNSTRIPPED}$(DOCKER_IMAGE_SUFFIX):$(4) . -ifneq ($(KIND_LOAD),) - sleep 1 - kind load docker-image $(IMAGE_REPOSITORY)/$(IMAGE_NAME)$${UNSTRIPPED}$(DOCKER_IMAGE_SUFFIX):$(4) -else - ifeq ($(findstring --push,$(DOCKER_FLAGS)),) - @echo 'Define "DOCKER_FLAGS=--push" to push the build results.' - else - $(CONTAINER_ENGINE) buildx imagetools inspect $(IMAGE_REPOSITORY)/$(IMAGE_NAME)$${UNSTRIPPED}$(DOCKER_IMAGE_SUFFIX):$(4) - @echo '^^^ Images pushed, multi-arch manifest should be above. ^^^' - endif -endif - -$(1)-unstripped: NOSTRIP=1 -$(1)-unstripped: UNSTRIPPED=-unstripped -$(1)-unstripped: $(1) - @echo -endef - -# docker-cilium-image -$(eval $(call DOCKER_IMAGE_TEMPLATE,docker-cilium-image,images/cilium/Dockerfile,cilium,$(DOCKER_IMAGE_TAG),release)) - -# dev-docker-image -$(eval $(call DOCKER_IMAGE_TEMPLATE,dev-docker-image,images/cilium/Dockerfile,cilium-dev,$(DOCKER_IMAGE_TAG),release)) - -# dev-docker-image-debug -$(eval $(call DOCKER_IMAGE_TEMPLATE,dev-docker-image-debug,images/cilium/Dockerfile,cilium-dev,$(DOCKER_IMAGE_TAG),debug)) - -# docker-plugin-image -$(eval $(call DOCKER_IMAGE_TEMPLATE,docker-plugin-image,images/cilium-docker-plugin/Dockerfile,docker-plugin,$(DOCKER_IMAGE_TAG),release)) - -# docker-hubble-relay-image -$(eval $(call DOCKER_IMAGE_TEMPLATE,docker-hubble-relay-image,images/hubble-relay/Dockerfile,hubble-relay,$(DOCKER_IMAGE_TAG),release)) - -# docker-clustermesh-apiserver-image -$(eval $(call DOCKER_IMAGE_TEMPLATE,docker-clustermesh-apiserver-image,images/clustermesh-apiserver/Dockerfile,clustermesh-apiserver,$(DOCKER_IMAGE_TAG),release)) - -# docker-operator-images. -# We eat the ending of "operator" in to the stem ('%') to allow this pattern -# to build also 'docker-operator-image', where the stem would be empty otherwise. -$(eval $(call DOCKER_IMAGE_TEMPLATE,docker-opera%-image,images/operator/Dockerfile,opera%,$(DOCKER_IMAGE_TAG),release)) -$(eval $(call DOCKER_IMAGE_TEMPLATE,dev-docker-opera%-image,images/operator/Dockerfile,opera%,$(DOCKER_IMAGE_TAG),release)) -$(eval $(call DOCKER_IMAGE_TEMPLATE,dev-docker-opera%-image-debug,images/operator/Dockerfile,opera%,$(DOCKER_IMAGE_TAG),debug)) - -# -# docker-*-all targets are mainly used from the CI -# -docker-images-all: docker-cilium-image docker-plugin-image docker-hubble-relay-image docker-clustermesh-apiserver-image docker-operator-images-all ## Build all Cilium related docker images. - -docker-images-all-unstripped: docker-cilium-image-unstripped docker-plugin-image-unstripped docker-hubble-relay-image-unstripped docker-clustermesh-apiserver-image-unstripped docker-operator-images-all-unstripped ## Build all Cilium related unstripped docker images. - -docker-operator-images-all: docker-operator-image docker-operator-aws-image docker-operator-azure-image docker-operator-alibabacloud-image docker-operator-generic-image ## Build all variants of cilium-operator images. - -docker-operator-images-all-unstripped: docker-operator-image-unstripped docker-operator-aws-image-unstripped docker-operator-azure-image-unstripped docker-operator-alibabacloud-image-unstripped docker-operator-generic-image-unstripped ## Build all variants of unstripped cilium-operator images. diff --git a/vendor/github.com/cilium/cilium/Makefile.kind b/vendor/github.com/cilium/cilium/Makefile.kind deleted file mode 100644 index a2cf93c76d..0000000000 --- a/vendor/github.com/cilium/cilium/Makefile.kind +++ /dev/null @@ -1,494 +0,0 @@ -# Copyright Authors of Cilium -# SPDX-License-Identifier: Apache-2.0 - -##@ Development (Kind) - -WAIT_DURATION ?= 30s - -.PHONY: kind -kind: ## Create a kind cluster for Cilium development. - $(QUIET)SED=$(SED) ./contrib/scripts/kind.sh - -.PHONY: kind-egressgw -kind-egressgw: ## Create a kind cluster for egress gateway Cilium development. - $(QUIET)SED=$(SED) WORKERS=3 ./contrib/scripts/kind.sh - kubectl patch node kind-worker3 --type=json -p='[{"op":"add","path":"/metadata/labels/cilium.io~1no-schedule","value":"true"}]' - -.PHONY: kind-down -kind-down: ## Destroy a kind cluster for Cilium development. - $(QUIET)./contrib/scripts/kind-down.sh - -.PHONY: kind-clustermesh -kind-clustermesh: ## Create two kind clusters for clustermesh development. - @echo " If you have problems with too many open file, check https://kind.sigs.k8s.io/docs/user/known-issues/#pod-errors-due-to-too-many-open-files" - $(QUIET) CLUSTER_NAME=clustermesh1 IPFAMILY=dual PODSUBNET=10.1.0.0/16,fd00:10:1::/48 SERVICESUBNET=172.20.1.0/24,fd00:10:f1::/112 ./contrib/scripts/kind.sh - $(QUIET) CLUSTER_NAME=clustermesh2 AGENTPORTPREFIX=236 OPERATORPORTPREFIX=237 IPFAMILY=dual PODSUBNET=10.2.0.0/16,fd00:10:2::/48 SERVICESUBNET=172.20.2.0/24,fd00:10:f2::/112 ./contrib/scripts/kind.sh - -.PHONY: kind-clustermesh-down -kind-clustermesh-down: ## Destroy kind clusters for clustermesh development. - $(QUIET)./contrib/scripts/kind-down.sh clustermesh1 clustermesh2 - -.PHONY: kind-clustermesh-ready -kind-clustermesh-ready: ## Check if both kind clustermesh clusters exist - @$(ECHO_CHECK) clustermesh kind is ready... - @kind get clusters 2>&1 | grep "clustermesh1" \ - && exit 0 || exit 1 - @kind get clusters 2>&1 | grep "clustermesh2" \ - && exit 0 || exit 1 - -.PHONY: kind-bgp-v4 -kind-bgp-v4: - $(QUIET) $(MAKE) -C contrib/containerlab/bgp-cplane-dev-v4 deploy - -.PHONY: kind-bgp-v4-down -kind-bgp-v4-down: - $(QUIET) $(MAKE) -C contrib/containerlab/bgp-cplane-dev-v4 destroy - -.PHONY: kind-bgp-v4-apply-policy -kind-bgp-v4-apply-policy: - $(QUIET) $(MAKE) -C contrib/containerlab/bgp-cplane-dev-v4 apply-policy - -.PHONY: kind-bgp-v6 -kind-bgp-v6: - $(QUIET) $(MAKE) -C contrib/containerlab/bgp-cplane-dev-v6 deploy - -.PHONY: kind-bgp-v6-down -kind-bgp-v6-down: - $(QUIET) $(MAKE) -C contrib/containerlab/bgp-cplane-dev-v6 destroy - -.PHONY: kind-bgp-v6-apply-policy -kind-bgp-v6-apply-policy: - $(QUIET) $(MAKE) -C contrib/containerlab/bgp-cplane-dev-v6 apply-policy - -.PHONY: kind-bgp-dual -kind-bgp-dual: - $(QUIET) $(MAKE) -C contrib/containerlab/bgp-cplane-dev-dual deploy - -.PHONY: kind-bgp-dual-down -kind-bgp-dual-down: - $(QUIET) $(MAKE) -C contrib/containerlab/bgp-cplane-dev-dual destroy - -.PHONY: kind-bgp-dual-apply-policy -kind-bgp-dual-apply-policy: - $(QUIET) $(MAKE) -C contrib/containerlab/bgp-cplane-dev-dual apply-policy - -# Template for kind environment for a target. Parameters are: -# $(1) Makefile target name -define KIND_ENV -.PHONY: $(1) -$(1): export DOCKER_REGISTRY=localhost:5000 -$(1): export LOCAL_AGENT_IMAGE=$$(DOCKER_REGISTRY)/$$(DOCKER_DEV_ACCOUNT)/cilium-dev:$$(LOCAL_IMAGE_TAG) -$(1): export LOCAL_OPERATOR_IMAGE=$$(DOCKER_REGISTRY)/$$(DOCKER_DEV_ACCOUNT)/operator-generic:$$(LOCAL_IMAGE_TAG) -$(1): export LOCAL_CLUSTERMESH_IMAGE=$$(DOCKER_REGISTRY)/$$(DOCKER_DEV_ACCOUNT)/clustermesh-apiserver:$$(LOCAL_IMAGE_TAG) -endef - -$(eval $(call KIND_ENV,kind-clustermesh-images)) -kind-clustermesh-images: kind-clustermesh-ready kind-build-clustermesh-apiserver kind-build-image-agent kind-build-image-operator ## Builds images and imports them into clustermesh clusters - $(QUIET)kind load docker-image $(LOCAL_CLUSTERMESH_IMAGE) --name clustermesh1 - $(QUIET)kind load docker-image $(LOCAL_CLUSTERMESH_IMAGE) --name clustermesh2 - $(QUIET)kind load docker-image $(LOCAL_AGENT_IMAGE) --name clustermesh1 - $(QUIET)kind load docker-image $(LOCAL_AGENT_IMAGE) --name clustermesh2 - $(QUIET)kind load docker-image $(LOCAL_OPERATOR_IMAGE) --name clustermesh1 - $(QUIET)kind load docker-image $(LOCAL_OPERATOR_IMAGE) --name clustermesh2 - -.PHONY: kind-connect-clustermesh ## Connect the ClusterMesh clusters. -kind-connect-clustermesh: check_deps kind-clustermesh-ready - @echo " CONNECT the two clusters" - $(CILIUM_CLI) clustermesh connect --context kind-clustermesh1 --destination-context kind-clustermesh2 - $(CILIUM_CLI) clustermesh status --context kind-clustermesh1 --wait - $(CILIUM_CLI) clustermesh status --context kind-clustermesh2 --wait - -ENABLE_KVSTOREMESH ?= true -$(eval $(call KIND_ENV,kind-install-cilium-clustermesh)) -kind-install-cilium-clustermesh: check_deps kind-clustermesh-ready ## Install a local Cilium version into the clustermesh clusters and enable clustermesh. - @echo " INSTALL cilium on clustermesh1 cluster" - -$(CILIUM_CLI) --context=kind-clustermesh1 uninstall >/dev/null - $(CILIUM_CLI) --context=kind-clustermesh1 install \ - --chart-directory=$(ROOT_DIR)/install/kubernetes/cilium \ - --values=$(ROOT_DIR)/contrib/testing/kind-clustermesh1.yaml \ - --set=image.override=$(LOCAL_AGENT_IMAGE) \ - --set=operator.image.override=$(LOCAL_OPERATOR_IMAGE) \ - --set=clustermesh.apiserver.image.override=$(LOCAL_CLUSTERMESH_IMAGE) \ - --set=clustermesh.apiserver.kvstoremesh.enabled=$(ENABLE_KVSTOREMESH) - - @echo " INSTALL cilium on clustermesh2 cluster" - -$(CILIUM_CLI) --context=kind-clustermesh2 uninstall >/dev/null - $(KUBECTL) --context=kind-clustermesh1 get secret -n kube-system cilium-ca -o yaml | \ - $(KUBECTL) --context=kind-clustermesh2 replace --force -f - - $(CILIUM_CLI) --context=kind-clustermesh2 install \ - --chart-directory=$(ROOT_DIR)/install/kubernetes/cilium \ - --values=$(ROOT_DIR)/contrib/testing/kind-clustermesh2.yaml \ - --set=image.override=$(LOCAL_AGENT_IMAGE) \ - --set=operator.image.override=$(LOCAL_OPERATOR_IMAGE) \ - --set=clustermesh.apiserver.image.override=$(LOCAL_CLUSTERMESH_IMAGE) \ - --set=clustermesh.apiserver.kvstoremesh.enabled=$(ENABLE_KVSTOREMESH) - - $(MAKE) kind-connect-clustermesh - -.PHONY: kind-install-cilium-clustermesh-fast -kind-install-cilium-clustermesh-fast: check_deps kind-clustermesh-ready ## "Fast" Install a local Cilium version using volume-mounted binaries into the ClusterMesh clusters and enable ClusterMesh. - @echo " INSTALL cilium on clustermesh1 cluster" - docker pull quay.io/cilium/cilium-ci:latest - kind load docker-image --name clustermesh1 quay.io/cilium/cilium-ci:latest - -$(CILIUM_CLI) --context=kind-clustermesh1 uninstall >/dev/null - $(CILIUM_CLI) --context=kind-clustermesh1 install \ - --chart-directory=$(ROOT_DIR)/install/kubernetes/cilium \ - --values=$(ROOT_DIR)/contrib/testing/kind-clustermesh1.yaml \ - --values=$(ROOT_DIR)/contrib/testing/kind-fast.yaml \ - --set=clustermesh.apiserver.kvstoremesh.enabled=$(ENABLE_KVSTOREMESH) - - @echo " INSTALL cilium on clustermesh2 cluster" - kind load docker-image --name clustermesh2 quay.io/cilium/cilium-ci:latest - -$(CILIUM_CLI) --context=kind-clustermesh2 uninstall >/dev/null - $(KUBECTL) --context=kind-clustermesh1 get secret -n kube-system cilium-ca -o yaml | \ - $(KUBECTL) --context=kind-clustermesh2 replace --force -f - - $(CILIUM_CLI) --context=kind-clustermesh2 install \ - --chart-directory=$(ROOT_DIR)/install/kubernetes/cilium \ - --values=$(ROOT_DIR)/contrib/testing/kind-clustermesh2.yaml \ - --values=$(ROOT_DIR)/contrib/testing/kind-fast.yaml \ - --set=clustermesh.apiserver.kvstoremesh.enabled=$(ENABLE_KVSTOREMESH) - - $(MAKE) kind-image-fast - $(MAKE) kind-connect-clustermesh - -KIND_CLUSTER_NAME ?= $(shell kind get clusters -q | head -n1) - -.PHONY: kind-ready -kind-ready: - @$(ECHO_CHECK) kind-ready - @if [ -n "$(shell kind get clusters -q)" ]; then echo "kind is ready"; else echo "kind not ready"; exit 1; fi - -$(eval $(call KIND_ENV,kind-build-image-agent)) -kind-build-image-agent: ## Build cilium-dev docker image - $(QUIET)$(MAKE) dev-docker-image$(DEBUGGER_SUFFIX) DOCKER_IMAGE_TAG=$(LOCAL_IMAGE_TAG) - -$(eval $(call KIND_ENV,kind-image-agent)) -kind-image-agent: .SHELLFLAGS=-c -kind-image-agent: kind-ready kind-build-image-agent ## Build cilium-dev docker image and import it into kind. - $(QUIET)kind load docker-image $(LOCAL_AGENT_IMAGE) -n $(KIND_CLUSTER_NAME); \ - [ $$? -eq 0 ] || $(QUIET)kind load docker-image $(LOCAL_AGENT_IMAGE) -n $(KIND_CLUSTER_NAME) - -$(eval $(call KIND_ENV,kind-build-image-operator)) -kind-build-image-operator: ## Build cilium-operator-dev docker image - $(QUIET)$(MAKE) dev-docker-operator-generic-image$(DEBUGGER_SUFFIX) DOCKER_IMAGE_TAG=$(LOCAL_IMAGE_TAG) - -$(eval $(call KIND_ENV,kind-image-operator)) -kind-image-operator: .SHELLFLAGS=-c -kind-image-operator: kind-ready kind-build-image-operator ## Build cilium-operator-dev docker image and import it into kind. - $(QUIET)kind load docker-image $(LOCAL_OPERATOR_IMAGE) -n $(KIND_CLUSTER_NAME); \ - [ $$? -eq 0 ] || $(QUIET)kind load docker-image $(LOCAL_OPERATOR_IMAGE) -n $(KIND_CLUSTER_NAME) - -$(eval $(call KIND_ENV,kind-build-clustermesh-apiserver)) -kind-build-clustermesh-apiserver: ## Build cilium-clustermesh-apiserver docker image - $(QUIET)$(MAKE) docker-clustermesh-apiserver-image DOCKER_IMAGE_TAG=$(LOCAL_IMAGE_TAG) - -.PHONY: kind-image -kind-image: ## Build cilium and operator images and import them into kind. - $(MAKE) kind-image-agent - $(MAKE) kind-image-operator - -define KIND_VALUES_FAST_FILES ---helm-values=$(ROOT_DIR)/contrib/testing/kind-common.yaml \ ---helm-values=$(ROOT_DIR)/contrib/testing/kind-fast.yaml -endef - -ifneq ("$(wildcard $(ROOT_DIR)/contrib/testing/kind-custom.yaml)","") - KIND_VALUES_FAST_FILES := $(KIND_VALUES_FAST_FILES) --helm-values=$(ROOT_DIR)/contrib/testing/kind-custom.yaml -endif - -ifdef ADDITIONAL_KIND_VALUES_FILE - KIND_VALUES_FAST_FILES := $(KIND_VALUES_FAST_FILES) --helm-values=$(ROOT_DIR)/$(ADDITIONAL_KIND_VALUES_FILE) -endif - -.PHONY: kind-install-cilium-fast -kind-install-cilium-fast: .SHELLFLAGS=-c -kind-install-cilium-fast: check_deps kind-ready ## "Fast" Install a local Cilium version using volume-mounted binaries into all clusters. - @echo " INSTALL cilium" - docker pull quay.io/cilium/cilium-ci:latest - for cluster_name in $${KIND_CLUSTERS:-$(shell kind get clusters)}; do \ - kind load docker-image --name $$cluster_name quay.io/cilium/cilium-ci:latest; \ - [ $$? -eq 0 ] || kind load docker-image --name $$cluster_name quay.io/cilium/cilium-ci:latest; \ - $(CILIUM_CLI) --context=kind-$$cluster_name uninstall >/dev/null 2>&1 || true; \ - $(CILIUM_CLI) install --context=kind-$$cluster_name \ - --chart-directory=$(ROOT_DIR)/install/kubernetes/cilium \ - $(KIND_VALUES_FAST_FILES) \ - --version= >/dev/null ; \ - done - -.PHONY: build-cli -build-cli: ## Build cilium cli binary - $(QUIET)$(MAKE) -C cilium-dbg GOOS=linux - -.PHONY: build-agent -build-agent: ## Build cilium daemon binary - $(QUIET)$(MAKE) -C daemon GOOS=linux - -.PHONY: build-operator -build-operator: ## Build cilium operator binary - $(QUIET)$(MAKE) -C operator cilium-operator-generic GOOS=linux - -.PHONY: build-clustermesh-apiserver -build-clustermesh-apiserver: ## Build cilium clustermesh-apiserver binary - $(QUIET)$(MAKE) -C clustermesh-apiserver GOOS=linux - -.PHONY: build-hubble-cli -build-hubble-cli: ## Build hubble cli binary - $(QUIET)$(MAKE) -C hubble GOOS=linux - -.PHONY: build-bugtool -build-bugtool: ## Build bugtool binary - $(QUIET)$(MAKE) -C bugtool GOOS=linux - -.PHONY: kind-image-fast-agent -kind-image-fast-agent: kind-ready build-cli build-agent build-hubble-cli build-bugtool ## Build cilium cli, daemon binaries, and hubble cli. Copy the bins and bpf files to kind nodes. - $(eval dst:=/cilium-binaries) - for cluster_name in $${KIND_CLUSTERS:-$(shell kind get clusters)}; do \ - for node_name in $$(kind get nodes -n "$$cluster_name"); do \ - docker exec $${node_name} mkdir -p "${dst}"; \ - \ - docker exec $${node_name} rm -rf "${dst}/var/lib/cilium"; \ - docker exec $${node_name} mkdir -p "${dst}/var/lib/cilium"; \ - docker cp "./bpf/" $${node_name}:"${dst}/var/lib/cilium/bpf"; \ - docker exec $${node_name} find "${dst}/var/lib/cilium/bpf" -type f -exec chmod 0644 {} + ;\ - \ - docker exec $${node_name} rm -f "${dst}/cilium-dbg"; \ - docker cp "./cilium-dbg/cilium-dbg" $${node_name}:"${dst}"; \ - docker exec $${node_name} chmod +x "${dst}/cilium-dbg"; \ - \ - docker exec $${node_name} rm -f "${dst}/cilium-agent"; \ - docker cp "./daemon/cilium-agent" $${node_name}:"${dst}"; \ - docker exec $${node_name} chmod +x "${dst}/cilium-agent"; \ - \ - docker exec $${node_name} rm -f "${dst}/hubble"; \ - docker cp "./hubble/hubble" $${node_name}:"${dst}"; \ - docker exec $${node_name} chmod +x "${dst}/hubble"; \ - \ - docker exec $${node_name} rm -f "${dst}/cilium-bugtool"; \ - docker cp "./bugtool/cilium-bugtool" $${node_name}:"${dst}"; \ - docker exec $${node_name} chmod +x "${dst}/cilium-bugtool"; \ - done; \ - kubectl --context=kind-$${cluster_name} delete pods -n kube-system -l k8s-app=cilium --force; \ - done - -.PHONY: kind-image-fast-operator -kind-image-fast-operator: kind-ready build-operator ## Build cilium operator binary and copy it to all kind nodes. - $(eval dst:=/cilium-binaries) - for cluster_name in $${KIND_CLUSTERS:-$(shell kind get clusters)}; do \ - for node_name in $$(kind get nodes -n "$$cluster_name"); do \ - docker exec $${node_name} mkdir -p "${dst}"; \ - \ - docker exec $${node_name} rm -f "${dst}/cilium-operator-generic"; \ - docker cp "./operator/cilium-operator-generic" $${node_name}:"${dst}"; \ - docker exec $${node_name} chmod +x "${dst}/cilium-operator-generic"; \ - done; \ - kubectl --context=kind-$${cluster_name} delete pods -n kube-system -l name=cilium-operator --force; \ - done - -.PHONY: kind-image-fast-clustermesh-apiserver -kind-image-fast-clustermesh-apiserver: kind-ready build-clustermesh-apiserver ## Build clustermesh-apiserver binary and copy it to all kind nodes. - $(eval dst:=/cilium-binaries) - for cluster_name in $${KIND_CLUSTERS:-$(shell kind get clusters)}; do \ - for node_name in $$(kind get nodes -n "$$cluster_name"); do \ - docker exec $${node_name} mkdir -p "${dst}"; \ - \ - docker exec $${node_name} rm -f "${dst}/clustermesh-apiserver"; \ - docker cp "./clustermesh-apiserver/clustermesh-apiserver" $${node_name}:"${dst}"; \ - docker exec $${node_name} chmod +x "${dst}/clustermesh-apiserver"; \ - done; \ - kubectl --context=kind-$${cluster_name} delete pods -n kube-system -l k8s-app=clustermesh-apiserver --force; \ - done - -.PHONY: kind-image-fast -kind-image-fast: kind-image-fast-agent kind-image-fast-operator kind-image-fast-clustermesh-apiserver ## Build all binaries and copy them to kind nodes. - -define KIND_VALUES_FILES ---helm-values=$(ROOT_DIR)/contrib/testing/kind-common.yaml \ ---helm-values=$(ROOT_DIR)/contrib/testing/kind-values.yaml -endef - -ifdef ADDITIONAL_KIND_VALUES_FILE - KIND_VALUES_FILES := $(KIND_VALUES_FILES) --helm-values=$(ROOT_DIR)/$(ADDITIONAL_KIND_VALUES_FILE) -endif - -ifneq ("$(wildcard $(ROOT_DIR)/contrib/testing/kind-custom.yaml)","") - KIND_VALUES_FILES := $(KIND_VALUES_FILES) --helm-values=$(ROOT_DIR)/contrib/testing/kind-custom.yaml -endif - -.PHONY: kind-install-cilium -kind-install-cilium: check_deps kind-ready ## Install a local Cilium version into the cluster. - @echo " INSTALL cilium" - # cilium-cli doesn't support idempotent installs, so we uninstall and - # reinstall here. https://github.com/cilium/cilium-cli/issues/205 - -@$(CILIUM_CLI) uninstall >/dev/null 2>&1 || true - - # cilium-cli's --wait flag doesn't work, so we just force it to run - # in the background instead and wait for the resources to be available. - # https://github.com/cilium/cilium-cli/issues/1070 - $(CILIUM_CLI) install \ - --chart-directory=$(ROOT_DIR)/install/kubernetes/cilium \ - $(KIND_VALUES_FILES) \ - --version= \ - >/dev/null 2>&1 & - -GW_VERSION ?= $(shell grep "sigs.k8s.io/gateway-api" go.mod | awk '{print $$2}' | awk -F'-' '{print (NF>2)?$$NF:$$0}') -# Set this to "standard" to use the standard CRDs instead -GW_CHANNEL ?= "experimental" -KIND_NET_CIDR ?= $(shell docker network inspect kind-cilium -f '{{json .IPAM.Config}}' | jq -r '.[] | select(.Subnet | test("^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+")) | .Subnet') -LB_CIDR ?= $(shell echo $(KIND_NET_CIDR) | sed "s@0.0/16@255.200\/28@" | sed -e 's/[\/&]/\\&/g') - -.PHONY: kind-servicemesh-install-cilium -kind-servicemesh-install-cilium: check_deps kind-ready ## Install a local Cilium version into the cluster. - @echo " INSTALL cilium" - # cilium-cli doesn't support idempotent installs, so we uninstall and - # reinstall here. https://github.com/cilium/cilium-cli/issues/205 - -@$(CILIUM_CLI) uninstall >/dev/null 2>&1 || true - - kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/$(GW_VERSION)/config/crd/$(GW_CHANNEL)/gateway.networking.k8s.io_gatewayclasses.yaml - kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/$(GW_VERSION)/config/crd/$(GW_CHANNEL)/gateway.networking.k8s.io_gateways.yaml - kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/$(GW_VERSION)/config/crd/$(GW_CHANNEL)/gateway.networking.k8s.io_httproutes.yaml - kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/$(GW_VERSION)/config/crd/$(GW_CHANNEL)/gateway.networking.k8s.io_referencegrants.yaml - kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/$(GW_VERSION)/config/crd/$(GW_CHANNEL)/gateway.networking.k8s.io_grpcroutes.yaml - kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/$(GW_VERSION)/config/crd/experimental/gateway.networking.k8s.io_tlsroutes.yaml - - $(CILIUM_CLI) install \ - --chart-directory=$(ROOT_DIR)/install/kubernetes/cilium \ - $(KIND_VALUES_FILES) \ - --helm-values=$(ROOT_DIR)/contrib/testing/kind-servicemesh.yaml \ - --version= \ - >/dev/null 2>&1 & - - $(CILIUM_CLI) status --wait --wait-duration $(WAIT_DURATION) - - @echo "KIND_NET_CIDR: $(KIND_NET_CIDR)" - @echo "LB_CIDR: $(LB_CIDR)" - - @echo "Deploying LB-IPAM Pool..." - sed -e "s/LB_CIDR/$(LB_CIDR)/g" $(ROOT_DIR)/contrib/testing/servicemesh/ippool.yaml | kubectl apply -f - - - @echo "Deploying L2-Announcement Policy..." - kubectl apply -f $(ROOT_DIR)/contrib/testing/servicemesh/l2policy.yaml - -.PHONY: kind-servicemesh-prereqs -kind-servicemesh-prereqs: check_deps kind-ready - @echo " SETUP Servicemesh" - kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/$(GW_VERSION)/config/crd/$(GW_CHANNEL)/gateway.networking.k8s.io_gatewayclasses.yaml - kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/$(GW_VERSION)/config/crd/$(GW_CHANNEL)/gateway.networking.k8s.io_gateways.yaml - kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/$(GW_VERSION)/config/crd/$(GW_CHANNEL)/gateway.networking.k8s.io_httproutes.yaml - kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/$(GW_VERSION)/config/crd/$(GW_CHANNEL)/gateway.networking.k8s.io_referencegrants.yaml - kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/$(GW_VERSION)/config/crd/$(GW_CHANNEL)/gateway.networking.k8s.io_grpcroutes.yaml - kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/gateway-api/$(GW_VERSION)/config/crd/experimental/gateway.networking.k8s.io_tlsroutes.yaml - - $(eval KIND_VALUES_FAST_FILES += --helm-values=$(ROOT_DIR)/contrib/testing/kind-servicemesh.yaml) - - @echo "KIND_VALUES_FILES $(KIND_VALUES_FAST_FILES)" - -.PHONY: kind-servicemesh-install-cilium-fast -kind-servicemesh-install-cilium-fast: | kind-servicemesh-prereqs kind-image-fast kind-install-cilium-fast - $(CILIUM_CLI) status --wait --wait-duration $(WAIT_DURATION) - - @echo "KIND_NET_CIDR: $(KIND_NET_CIDR)" - @echo "LB_CIDR: $(LB_CIDR)" - - @echo "Deploying LB-IPAM Pool..." - sed -e "s/LB_CIDR/$(LB_CIDR)/g" $(ROOT_DIR)/contrib/testing/servicemesh/ippool.yaml | kubectl apply -f - - - @echo "Deploying L2-Announcement Policy..." - kubectl apply -f $(ROOT_DIR)/contrib/testing/servicemesh/l2policy.yaml - -.PHONY: kind-egressgw-install-cilium -kind-egressgw-install-cilium: check_deps kind-ready ## Install a local Cilium version into the cluster. - @echo " INSTALL cilium" - # cilium-cli doesn't support idempotent installs, so we uninstall and - # reinstall here. https://github.com/cilium/cilium-cli/issues/205 - -@$(CILIUM_CLI) uninstall >/dev/null 2>&1 || true - - # cilium-cli's --wait flag doesn't work, so we just force it to run - # in the background instead and wait for the resources to be available. - # https://github.com/cilium/cilium-cli/issues/1070 - $(CILIUM_CLI) install \ - --chart-directory=$(ROOT_DIR)/install/kubernetes/cilium \ - $(KIND_VALUES_FILES) \ - --helm-values=$(ROOT_DIR)/contrib/testing/kind-egressgw-values.yaml \ - --nodes-without-cilium \ - --version= \ - >/dev/null 2>&1 & - -KVSTORE_POD_NAME ?= "kvstore" -KVSTORE_POD_PORT ?= "2378" - -.PHONY: kind-kvstore-install-cilium -kind-kvstore-install-cilium: check_deps kind-ready kind-kvstore-start ## Install a local Cilium version into the cluster, configured in kvstore mode. - $(MAKE) kind-install-cilium KIND_VALUES_FILES="\ - $(KIND_VALUES_FILES) \ - --set etcd.enabled=true \ - --set etcd.endpoints[0]=http://$(shell kubectl --namespace kube-system get pod $(KVSTORE_POD_NAME) -o jsonpath='{.status.hostIP}'):$(KVSTORE_POD_PORT) \ - --set identityAllocationMode=kvstore \ - " - -.PHONY: kind-kvstore-start -kind-kvstore-start: ## Start an etcd pod serving as Cilium's kvstore - kubectl --namespace kube-system get pod $(KVSTORE_POD_NAME) >/dev/null 2>/dev/null || \ - kubectl --namespace kube-system run $(KVSTORE_POD_NAME) --image $(ETCD_IMAGE) \ - --overrides='{ "apiVersion": "v1", "spec": { "hostNetwork": true, "nodeSelector": {"node-role.kubernetes.io/control-plane": ""}, "tolerations": [{ "operator": "Exists" }] }}' \ - -- etcd --listen-client-urls=http://0.0.0.0:$(KVSTORE_POD_PORT) --advertise-client-urls=http://0.0.0.0:$(KVSTORE_POD_PORT) - - kubectl --namespace kube-system wait --for=condition=Ready pod/$(KVSTORE_POD_NAME) - -.PHONY: kind-kvstore-stop -kind-kvstore-stop: ## Stop the etcd pod serving as Cilium's kvstore - kubectl --namespace kube-system delete pod $(KVSTORE_POD_NAME) --ignore-not-found - kubectl --namespace kube-system wait --for=delete pod/$(KVSTORE_POD_NAME) - -.PHONY: kind-uninstall-cilium -kind-uninstall-cilium: check_deps ## Uninstall Cilium from the cluster. - @echo " UNINSTALL cilium" - -$(CILIUM_CLI) uninstall - -.PHONY: kind-check-cilium -kind-check-cilium: check_deps - @echo " CHECK cilium is ready..." - $(CILIUM_CLI) status --wait --wait-duration 1s >/dev/null 2>/dev/null - -# Template for kind debug targets. Parameters are: -# $(1) agent target -define DEBUG_KIND_TEMPLATE -.PHONY: kind-image$(1)-debug -kind-image$(1)-debug: export DEBUGGER_SUFFIX=-debug -kind-image$(1)-debug: export NOSTRIP=1 -kind-image$(1)-debug: export NOOPT=1 -kind-image$(1)-debug: ## Build cilium$(1) docker image with a dlv debugger wrapper and import it into kind. - $(MAKE) kind-image$(1) -endef - -# kind-image-agent-debug -$(eval $(call DEBUG_KIND_TEMPLATE,-agent)) - -# kind-image-operator-debug -$(eval $(call DEBUG_KIND_TEMPLATE,-operator)) - -$(eval $(call KIND_ENV,kind-debug-agent)) -kind-debug-agent: ## Create a local kind development environment with cilium-agent attached to a debugger. - $(QUIET)$(MAKE) kind-ready 2>/dev/null \ - || $(MAKE) kind - $(MAKE) kind-image-agent-debug - # Not debugging cilium-operator here; any image is good enough. - kind load docker-image $(LOCAL_OPERATOR_IMAGE) \ - || $(MAKE) kind-image-operator - $(MAKE) kind-check-cilium 2>/dev/null \ - || $(MAKE) kind-install-cilium - @echo "Attach delve to localhost on these ports to continue:" - @echo " - 23401: cilium-agent (kind-control-plane)" - @echo " - 23411: cilium-agent (kind-worker)" - -$(eval $(call KIND_ENV,kind-debug)) -kind-debug: ## Create a local kind development environment with cilium-agent & cilium-operator attached to a debugger. - $(QUIET)$(MAKE) kind-ready 2>/dev/null \ - || $(MAKE) kind - $(MAKE) kind-image-agent-debug - $(MAKE) kind-image-operator-debug - $(MAKE) kind-check-cilium 2>/dev/null \ - || $(MAKE) kind-install-cilium - @echo "Attach delve to localhost on these ports to continue:" - @echo " - 23401: cilium-agent (kind-control-plane)" - @echo " - 23411: cilium-agent (kind-worker)" - @echo " - 23511: cilium-operator (kind-worker)" diff --git a/vendor/github.com/cilium/cilium/Makefile.quiet b/vendor/github.com/cilium/cilium/Makefile.quiet deleted file mode 100644 index 397661109e..0000000000 --- a/vendor/github.com/cilium/cilium/Makefile.quiet +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright Authors of Cilium -# SPDX-License-Identifier: Apache-2.0 - -ifeq ($(ROOT_DIR),) - ROOT_DIR ?= $(shell dirname $(realpath $(lastword $(MAKEFILE_LIST)))) - RELATIVE_DIR ?= $(shell echo $(realpath .) | sed "s;$(ROOT_DIR)[/]*;;") -endif -ifeq ($(V),0) - QUIET=@ - ECHO_CC=echo " CC $(RELATIVE_DIR)/$@" - ECHO_CHECK=echo " CHECK $(RELATIVE_DIR)" - ECHO_CLEAN=echo " CLEAN $(RELATIVE_DIR)" - ECHO_DOCKER=echo " DOCKER $(RELATIVE_DIR) $@" - ECHO_GEN=echo " GEN $(RELATIVE_DIR)/" - ECHO_GINKGO=echo " GINKGO $(RELATIVE_DIR)" - ECHO_GO=echo " GO $(RELATIVE_DIR)/$@" - ECHO_TEST=echo " TEST " - SUBMAKEOPTS="-s" -else - # The whitespace at below EOLs is required for verbose case! - ECHO_CC=: - ECHO_CHECK=: - ECHO_CLEAN=: - ECHO_DOCKER=: - ECHO_GEN=: - ECHO_GINKGO=: - ECHO_GO=: - ECHO_TEST=: - SUBMAKEOPTS= -endif diff --git a/vendor/github.com/cilium/cilium/README.rst b/vendor/github.com/cilium/cilium/README.rst deleted file mode 100644 index e2eb647eff..0000000000 --- a/vendor/github.com/cilium/cilium/README.rst +++ /dev/null @@ -1,359 +0,0 @@ -.. raw:: html - - - - Cilium Logo - - -|cii| |go-report| |clomonitor| |artifacthub| |slack| |go-doc| |rtd| |apache| |bsd| |gpl| |fossa| |gateway-api| |codespaces| - -Cilium is a networking, observability, and security solution with an eBPF-based -dataplane. It provides a simple flat Layer 3 network with the ability to span -multiple clusters in either a native routing or overlay mode. It is L7-protocol -aware and can enforce network policies on L3-L7 using an identity based security -model that is decoupled from network addressing. - -Cilium implements distributed load balancing for traffic between pods and to -external services, and is able to fully replace kube-proxy, using efficient -hash tables in eBPF allowing for almost unlimited scale. It also supports -advanced functionality like integrated ingress and egress gateway, bandwidth -management and service mesh, and provides deep network and security visibility and monitoring. - -A new Linux kernel technology called eBPF_ is at the foundation of Cilium. It -supports dynamic insertion of eBPF bytecode into the Linux kernel at various -integration points such as: network IO, application sockets, and tracepoints to -implement security, networking and visibility logic. eBPF is highly efficient -and flexible. To learn more about eBPF, visit `eBPF.io`_. - -.. image:: Documentation/images/cilium-overview.png - :alt: Overview of Cilium features for networking, observability, service mesh, and runtime security - -.. raw:: html - - - - - CNCF Graduated Project - - - - - - Powered by eBPF - - - -Stable Releases -=============== - -The Cilium community maintains minor stable releases for the last three minor -Cilium versions. Older Cilium stable versions from minor releases prior to that -are considered EOL. - -For upgrades to new minor releases please consult the `Cilium Upgrade Guide`_. - -Listed below are the actively maintained release branches along with their latest -patch release, corresponding image pull tags and their release notes: - -+---------------------------------------------------------+------------+------------------------------------+----------------------------------------------------------------------------+ -| `v1.17 `__ | 2025-02-12 | ``quay.io/cilium/cilium:v1.17.1`` | `Release Notes `__ | -+---------------------------------------------------------+------------+------------------------------------+----------------------------------------------------------------------------+ -| `v1.16 `__ | 2025-02-18 | ``quay.io/cilium/cilium:v1.16.7`` | `Release Notes `__ | -+---------------------------------------------------------+------------+------------------------------------+----------------------------------------------------------------------------+ -| `v1.15 `__ | 2025-02-18 | ``quay.io/cilium/cilium:v1.15.14`` | `Release Notes `__ | -+---------------------------------------------------------+------------+------------------------------------+----------------------------------------------------------------------------+ - -Architectures -------------- - -Cilium images are distributed for AMD64 and AArch64 architectures. - -Software Bill of Materials --------------------------- - -Starting with Cilium version 1.13.0, all images include a Software Bill of -Materials (SBOM). The SBOM is generated in `SPDX`_ format. More information -on this is available on `Cilium SBOM`_. - -.. _`SPDX`: https://spdx.dev/ -.. _`Cilium SBOM`: https://docs.cilium.io/en/latest/configuration/sbom/ - -Development -=========== - -For development and testing purpose, the Cilium community publishes snapshots, -early release candidates (RC) and CI container images build from the `main -branch `_. These images are -not for use in production. - -For testing upgrades to new development releases please consult the latest -development build of the `Cilium Upgrade Guide`_. - -Listed below are branches for testing along with their snapshots or RC releases, -corresponding image pull tags and their release notes where applicable: - -+----------------------------------------------------------------------------+------------+-----------------------------------------+---------------------------------------------------------------------------------+ -| `main `__ | daily | ``quay.io/cilium/cilium-ci:latest`` | N/A | -+----------------------------------------------------------------------------+------------+-----------------------------------------+---------------------------------------------------------------------------------+ -| `v1.18.0-pre.0 `__ | 2025-03-03 | ``quay.io/cilium/cilium:v1.18.0-pre.0`` | `Release Notes `__ | -+----------------------------------------------------------------------------+------------+-----------------------------------------+---------------------------------------------------------------------------------+ - -Functionality Overview -====================== - -.. begin-functionality-overview - -Protect and secure APIs transparently -------------------------------------- - -Ability to secure modern application protocols such as REST/HTTP, gRPC and -Kafka. Traditional firewalls operate at Layer 3 and 4. A protocol running on a -particular port is either completely trusted or blocked entirely. Cilium -provides the ability to filter on individual application protocol requests such -as: - -- Allow all HTTP requests with method ``GET`` and path ``/public/.*``. Deny all - other requests. -- Allow ``service1`` to produce on Kafka topic ``topic1`` and ``service2`` to - consume on ``topic1``. Reject all other Kafka messages. -- Require the HTTP header ``X-Token: [0-9]+`` to be present in all REST calls. - -See the section `Layer 7 Policy`_ in our documentation for the latest list of -supported protocols and examples on how to use it. - -Secure service to service communication based on identities ------------------------------------------------------------ - -Modern distributed applications rely on technologies such as application -containers to facilitate agility in deployment and scale out on demand. This -results in a large number of application containers being started in a short -period of time. Typical container firewalls secure workloads by filtering on -source IP addresses and destination ports. This concept requires the firewalls -on all servers to be manipulated whenever a container is started anywhere in -the cluster. - -In order to avoid this situation which limits scale, Cilium assigns a security -identity to groups of application containers which share identical security -policies. The identity is then associated with all network packets emitted by -the application containers, allowing to validate the identity at the receiving -node. Security identity management is performed using a key-value store. - -Secure access to and from external services -------------------------------------------- - -Label based security is the tool of choice for cluster internal access control. -In order to secure access to and from external services, traditional CIDR based -security policies for both ingress and egress are supported. This allows to -limit access to and from application containers to particular IP ranges. - -Simple Networking ------------------ - -A simple flat Layer 3 network with the ability to span multiple clusters -connects all application containers. IP allocation is kept simple by using host -scope allocators. This means that each host can allocate IPs without any -coordination between hosts. - -The following multi node networking models are supported: - -* **Overlay:** Encapsulation-based virtual network spanning all hosts. - Currently, VXLAN and Geneve are baked in but all encapsulation formats - supported by Linux can be enabled. - - When to use this mode: This mode has minimal infrastructure and integration - requirements. It works on almost any network infrastructure as the only - requirement is IP connectivity between hosts which is typically already - given. - -* **Native Routing:** Use of the regular routing table of the Linux host. - The network is required to be capable to route the IP addresses of the - application containers. - - When to use this mode: This mode is for advanced users and requires some - awareness of the underlying networking infrastructure. This mode works well - with: - - - Native IPv6 networks - - In conjunction with cloud network routers - - If you are already running routing daemons - -Load Balancing --------------- - -Cilium implements distributed load balancing for traffic between application -containers and to external services and is able to fully replace components -such as kube-proxy. The load balancing is implemented in eBPF using efficient -hashtables allowing for almost unlimited scale. - -For north-south type load balancing, Cilium's eBPF implementation is optimized -for maximum performance, can be attached to XDP (eXpress Data Path), and supports -direct server return (DSR) as well as Maglev consistent hashing if the load -balancing operation is not performed on the source host. - -For east-west type load balancing, Cilium performs efficient service-to-backend -translation right in the Linux kernel's socket layer (e.g. at TCP connect time) -such that per-packet NAT operations overhead can be avoided in lower layers. - -Bandwidth Management --------------------- - -Cilium implements bandwidth management through efficient EDT-based (Earliest Departure -Time) rate-limiting with eBPF for container traffic that is egressing a node. This -allows to significantly reduce transmission tail latencies for applications and to -avoid locking under multi-queue NICs compared to traditional approaches such as HTB -(Hierarchy Token Bucket) or TBF (Token Bucket Filter) as used in the bandwidth CNI -plugin, for example. - -Monitoring and Troubleshooting ------------------------------- - -The ability to gain visibility and troubleshoot issues is fundamental to the -operation of any distributed system. While we learned to love tools like -``tcpdump`` and ``ping`` and while they will always find a special place in our -hearts, we strive to provide better tooling for troubleshooting. This includes -tooling to provide: - -- Event monitoring with metadata: When a packet is dropped, the tool doesn't - just report the source and destination IP of the packet, the tool provides - the full label information of both the sender and receiver among a lot of - other information. - -- Metrics export via Prometheus: Key metrics are exported via Prometheus for - integration with your existing dashboards. - -- Hubble_: An observability platform specifically written for Cilium. It - provides service dependency maps, operational monitoring and alerting, - and application and security visibility based on flow logs. - -.. _Hubble: https://github.com/cilium/hubble/ -.. _`Layer 7 Policy`: https://docs.cilium.io/en/stable/security/policy/language/#layer-7-examples - -.. end-functionality-overview - -Getting Started -=============== - -* `Why Cilium?`_ -* `Getting Started`_ -* `Architecture and Concepts`_ -* `Installing Cilium`_ -* `Frequently Asked Questions`_ -* Contributing_ - -Community -========= - -Slack ------ - -Join the Cilium `Slack channel `_ to chat with -Cilium developers and other Cilium users. This is a good place to learn about -Cilium, ask questions, and share your experiences. - -Special Interest Groups (SIG) ------------------------------ - -See `Special Interest groups -`_ for a list of all SIGs and their meeting times. - -Developer meetings ------------------- -The Cilium developer community hangs out on Zoom to chat. Everybody is welcome. - -* Weekly, Wednesday, - 5:00 pm `Europe/Zurich time `__ (CET/CEST), - usually equivalent to 8:00 am PT, or 11:00 am ET. `Meeting Notes and Zoom Info`_ -* Third Wednesday of each month, 9:00 am `Japan time `__ (JST). `APAC Meeting Notes and Zoom Info`_ - -eBPF & Cilium Office Hours livestream -------------------------------------- -We host a weekly community `YouTube livestream called eCHO `_ which (very loosely!) stands for eBPF & Cilium Office Hours. Join us live, catch up with past episodes, or head over to the `eCHO repo `_ and let us know your ideas for topics we should cover. - -Governance ----------- -The Cilium project is governed by a group of `Maintainers and Committers `__. -How they are selected and govern is outlined in our `governance document `__. - -Adopters --------- -A list of adopters of the Cilium project who are deploying it in production, and of their use cases, -can be found in file `USERS.md `__. - -License -======= - -.. _apache-license: LICENSE -.. _bsd-license: bpf/LICENSE.BSD-2-Clause -.. _gpl-license: bpf/LICENSE.GPL-2.0 - -The Cilium user space components are licensed under the -`Apache License, Version 2.0 `__. -The BPF code templates are dual-licensed under the -`General Public License, Version 2.0 (only) `__ -and the `2-Clause BSD License `__ -(you can use the terms of either license, at your option). - -.. _`Cilium Upgrade Guide`: https://docs.cilium.io/en/stable/operations/upgrade/ -.. _`Why Cilium?`: https://docs.cilium.io/en/stable/overview/intro -.. _`Getting Started`: https://docs.cilium.io/en/stable/#getting-started -.. _`Architecture and Concepts`: https://docs.cilium.io/en/stable/overview/component-overview/ -.. _`Installing Cilium`: https://docs.cilium.io/en/stable/gettingstarted/k8s-install-default/ -.. _`Frequently Asked Questions`: https://github.com/cilium/cilium/issues?utf8=%E2%9C%93&q=is%3Aissue+label%3Akind%2Fquestion+ -.. _Contributing: https://docs.cilium.io/en/stable/contributing/development/ -.. _Prerequisites: https://docs.cilium.io/en/stable/operations/system_requirements/ -.. _`eBPF`: https://ebpf.io -.. _`eBPF.io`: https://ebpf.io -.. _`Meeting Notes and Zoom Info`: https://docs.google.com/document/d/1Y_4chDk4rznD6UgXPlPvn3Dc7l-ZutGajUv1eF0VDwQ/edit# -.. _`APAC Meeting Notes and Zoom Info`: https://docs.google.com/document/d/1egv4qLydr0geP-GjQexYKm4tz3_tHy-LCBjVQcXcT5M/edit# - -.. |go-report| image:: https://goreportcard.com/badge/github.com/cilium/cilium - :alt: Go Report Card - :target: https://goreportcard.com/report/github.com/cilium/cilium - -.. |go-doc| image:: https://godoc.org/github.com/cilium/cilium?status.svg - :alt: GoDoc - :target: https://godoc.org/github.com/cilium/cilium - -.. |rtd| image:: https://readthedocs.org/projects/docs/badge/?version=latest - :alt: Read the Docs - :target: https://docs.cilium.io/ - -.. |apache| image:: https://img.shields.io/badge/license-Apache-blue.svg - :alt: Apache licensed - :target: apache-license_ - -.. |bsd| image:: https://img.shields.io/badge/license-BSD-blue.svg - :alt: BSD licensed - :target: bsd-license_ - -.. |gpl| image:: https://img.shields.io/badge/license-GPL-blue.svg - :alt: GPL licensed - :target: gpl-license_ - -.. |slack| image:: https://img.shields.io/badge/slack-cilium-brightgreen.svg?logo=slack - :alt: Join the Cilium slack channel - :target: https://slack.cilium.io - -.. |cii| image:: https://bestpractices.coreinfrastructure.org/projects/1269/badge - :alt: CII Best Practices - :target: https://bestpractices.coreinfrastructure.org/projects/1269 - -.. |clomonitor| image:: https://img.shields.io/endpoint?url=https://clomonitor.io/api/projects/cncf/cilium/badge - :alt: CLOMonitor - :target: https://clomonitor.io/projects/cncf/cilium - -.. |artifacthub| image:: https://img.shields.io/endpoint?url=https://artifacthub.io/badge/repository/cilium - :alt: Artifact Hub - :target: https://artifacthub.io/packages/helm/cilium/cilium - -.. |fossa| image:: https://app.fossa.com/api/projects/custom%2B162%2Fgit%40github.com%3Acilium%2Fcilium.git.svg?type=shield - :alt: FOSSA Status - :target: https://app.fossa.com/projects/custom%2B162%2Fgit%40github.com%3Acilium%2Fcilium.git?ref=badge_shield - -.. |gateway-api| image:: https://img.shields.io/badge/Gateway%20API%20Conformance%20v1.2.0-Cilium-green - :alt: Gateway API Status - :target: https://github.com/kubernetes-sigs/gateway-api/tree/main/conformance/reports/v1.2.0/cilium-cilium - -.. |codespaces| image:: https://img.shields.io/badge/Open_in_GitHub_Codespaces-gray?logo=github - :alt: Github Codespaces - :target: https://github.com/codespaces/new?hide_repo_select=true&ref=master&repo=48109239&machine=standardLinux32gb&location=WestEurope diff --git a/vendor/github.com/cilium/cilium/SECURITY-INSIGHTS.yml b/vendor/github.com/cilium/cilium/SECURITY-INSIGHTS.yml deleted file mode 100644 index 3074a9d1e2..0000000000 --- a/vendor/github.com/cilium/cilium/SECURITY-INSIGHTS.yml +++ /dev/null @@ -1,69 +0,0 @@ -header: - schema-version: '1.0.0' - expiration-date: '2025-01-26T01:00:00.000Z' - last-updated: '2024-01-26' - last-reviewed: '2024-01-26' - project-url: https://github.com/cilium/cilium - license: https://github.com/cilium/cilium/blob/main/LICENSE -project-lifecycle: - status: active - bug-fixes-only: false - core-maintainers: - - https://github.com/cilium/cilium/blob/main/MAINTAINERS.md - roadmap: https://docs.cilium.io/en/stable/community/roadmap -contribution-policy: - accepts-pull-requests: true - accepts-automated-pull-requests: true -dependencies: - third-party-packages: true - dependencies-lists: - - https://github.com/cilium/cilium/blob/main/go.mod - sbom: - - sbom-format: SPDX - sbom-url: https://docs.cilium.io/en/stable/configuration/sbom -distribution-points: - - https://github.com/cilium/cilium - - https://hub.docker.com/u/cilium - - https://quay.io/organization/cilium -documentation: - - https://docs.cilium.io/en/stable/ -security-assessments: - - auditor-name: ADA Logics - auditor-url: https://adalogics.com - auditor-report: https://github.com/cilium/cilium.io/blob/main/Security-Reports/CiliumSecurityAudit2022.pdf - report-year: 2022 - - auditor-name: ADA Logics - auditor-url: https://adalogics.com - auditor-report: https://github.com/cilium/cilium.io/blob/main/Security-Reports/CiliumFuzzingAudit2022.pdf - report-year: 2022 -security-contacts: - - type: email - value: security@cilium.io -security-testing: -- tool-type: sca - tool-name: Mend Renovate - tool-url: https://www.mend.io/renovate - tool-version: latest - integration: - ad-hoc: false - ci: true - before-release: true -- tool-type: fuzzer - tool-name: OSS-Fuzz - tool-url: https://github.com/google/oss-fuzz - tool-version: latest - integration: - ad-hoc: false - ci: true - before-release: true -- tool-type: sast - tool-name: Grype - tool-url: https://github.com/anchore/grype - tool-version: latest - integration: - ad-hoc: false - ci: true - before-release: true -vulnerability-reporting: - accepts-vulnerability-reports: true - security-policy: https://github.com/cilium/cilium/security diff --git a/vendor/github.com/cilium/cilium/SECURITY.md b/vendor/github.com/cilium/cilium/SECURITY.md deleted file mode 100644 index 87570c3114..0000000000 --- a/vendor/github.com/cilium/cilium/SECURITY.md +++ /dev/null @@ -1,29 +0,0 @@ -# Security Policy - -## Supported Versions - -| Version | Supported | -|----------| ------------------ | -| main | :white_check_mark: | -| 1.17.x | :white_check_mark: | -| 1.16.x | :white_check_mark: | -| 1.15.x | :white_check_mark: | -| < 1.15.0 | :x: | - -## Reporting a Vulnerability - -We strongly encourage you to report security vulnerabilities to -our private security mailing list: security@cilium.io - first, before -disclosing them in any public forums. - -A threat model for Cilium and recommendations for running Cilium in production -environments can be found [here][threat-model]. Please ensure that you have -taken this threat model into consideration before making a report, including -considering the feasibility of an attack against a correctly secured -environment. - -This is a private mailing list where members of Cilium's -[Security Team](https://github.com/cilium/community/blob/main/roles/Security-Team.md) -are subscribed to, and is treated as top priority. - -[threat-model]: https://docs.cilium.io/en/latest/security/threat-model/ diff --git a/vendor/github.com/cilium/cilium/USERS.md b/vendor/github.com/cilium/cilium/USERS.md deleted file mode 100644 index 6d646f0c5b..0000000000 --- a/vendor/github.com/cilium/cilium/USERS.md +++ /dev/null @@ -1,930 +0,0 @@ -Who is using Cilium? -==================== - -Sharing experiences and learning from other users is essential. We are -frequently asked who is using a particular feature of Cilium so people can get in -contact with other users to share experiences and best practices. People -also often want to know if product/platform X has integrated Cilium. -While the [Cilium Slack community](https://slack.cilium.io) allows -users to get in touch, it can be challenging to find this information quickly. - -The following is a directory of adopters to help identify users of individual -features. The users themselves directly maintain the list. - -Adding yourself as a user -------------------------- - -If you are using Cilium or it is integrated into your product, service, or -platform, please consider adding yourself as a user with a quick -description of your use case by opening a pull request to this file and adding -a section describing your usage of Cilium. If you are open to others contacting -you about your use of Cilium on Slack, add your Slack nickname as well. - - N: Name of user (company) - D: Description - U: Usage of features - L: Link with further information (optional) - Q: Contacts available for questions (optional) - -Example entry: - - * N: Cilium Example User Inc. - D: Cilium Example User Inc. is using Cilium for scientific purposes - U: ENI networking, DNS policies, ClusterMesh - Q: @slacknick1, @slacknick2 - -Requirements to be listed -------------------------- - - * You must represent the user listed. Do *NOT* add entries on behalf of - other users. - * There is no minimum deployment size but we request to list permanent - production deployments only, i.e., no demo or trial deployments. Commercial - use is not required. A well-done home lab setup can be equally - interesting as a large-scale commercial deployment. - -Users (Alphabetically) ----------------------- - - * N: Ænix - D: Ænix uses Cilium in free PaaS platform [Cozystack](https://cozystack.io) for running containers, virtual machines and Kubernetes-as-a-Service. - U: Networking, NetworkPolicy, kube-proxy replacement, CNI-Chaining (with kube-ovn) - L: https://cozystack.io/ - Q: @kvaps - - * N: AccuKnox - D: AccuKnox uses Cilium for network visibility and network policy enforcement. - U: L3/L4/L7 policy enforcement using Identity, External/VM Workloads, Network Visibility using Hubble - L: https://www.accuknox.com/spifee-identity-for-cilium-presentation-at-kubecon-2021, https://www.accuknox.com/cilium - Q: @nyrahul - - * N: Acoss - D: Acoss is using cilium as their main CNI plugin (self hosted k8s, On-premises) - U: CiliumNetworkPolicy, Hubble, BPF NodePort, Direct routing - L: @JrCs - - * N: Adobe, Inc. - D: Adobe's Project Ethos uses Cilium for multi-tenant, multi-cloud clusters - U: L3/L4/L7 policies - L: https://youtu.be/39FLsSc2P-Y - - * N: AirQo - D: AirQo uses Cilium as the CNI plugin - U: CNI, Networking, NetworkPolicy, Cluster Mesh, Hubble, Kubernetes services - L: @airqo-platform - - * N: Alauda - D: Alauda uses Cilium in the Alauda Container Platform product to provide high performance network,observability and security. - U: Networking, NetworkPolicy, Services, Observability - Q:@oilbeater - - * N: Alibaba Cloud - D: Alibaba Cloud is using Cilium together with Terway CNI as the high-performance ENI dataplane - U: Networking, NetworkPolicy, Services, IPVLAN - L: https://www.alibabacloud.com/blog/how-does-alibaba-cloud-build-high-performance-cloud-native-pod-networks-in-production-environments_596590 - - * N: Amazon Web Services (AWS) - D: AWS uses Cilium as the default CNI for EKS Anywhere - U: Networking, NetworkPolicy, Services - L: https://isovalent.com/blog/post/2021-09-aws-eks-anywhere-chooses-cilium - - * N: APPUiO by VSHN - D: VSHN uses Cilium for multi-tenant networking on APPUiO Cloud and as an add-on to APPUiO Managed, both on Red Hat OpenShift and Cloud Kubernetes. - U: CNI, Networking, NetworkPolicy, Hubble, IPAM, Kubernetes services - L: https://products.docs.vshn.ch/products/appuio/managed/addon_cilium.html and https://www.appuio.cloud - - * N: ArangoDB Oasis - D: ArangoDB Oasis is using Cilium in to separate database deployments in our multi-tenant cloud environment - U: Networking, CiliumNetworkPolicy(cluster & local), Hubble, IPAM - L: https://cloud.arangodb.com - Q: @ewoutp @Robert-Stam - - * N: Archer Aviation - D: Archer Aviation uses Cilium as part of the foundation of the Kubernetes cluster. - U: Networking, Observability, Security - L: https://www.archer.com - Q: @Hongbo Miao - - * N: Ascend.io - D: Ascend.io is using Cilium as a consistent CNI for our Data Automation Platform on GKE, EKS, and AKS. - U: Transparent Encryption, Overlay Networking, Cluster Mesh, Egress Gateway, Network Policy, Hubble - L: https://www.ascend.io/ - Q: @Joe Stevens - - * N: Ayedo - D: Ayedo builds and operates cloud-native container platforms based on Kubernetes - U: Hubble for Visibility, Cilium as Mesh between Services - L: https://www.ayedo.de/ - - * N: Back Market - D: Back Market is using Cilium as CNI in all their clusters and environments (kOps + EKS in AWS) - U: CNI, Network Policies, Transparent Encryption (WG), Hubble - Q: @nitrikx - L: https://www.backmarket.com/ - - * N: Berops - D: Cilium is used as a CNI plug-in in our open-source multi-cloud and hybrid-cloud Kubernetes platform - Claudie - U: CNI, Network Policies, Hubble - Q: @Bernard Halas - L: https://github.com/berops/claudie - - * N: Bitnami - D: Cilium is part of the largest open-source application catalog. - U: CNI, Hubble, BGP, eBPF, CiliumNetworkPolicy, CiliumClusterwideNetworkPolicy - L: https://bitnami.com/stack/cilium - Q: @carrodher - - * N: BMC Software - D: Cilium can be optionally used in BMC Helix Innovaton Suite and BMC IT Operations Management On Premise - U: CNI, Hubble - L: https://www.bmc.com - Q: @ryebridge - - * N: ByteDance - D: ByteDance is using Cilium as CNI plug-in for self-hosted Kubernetes. - U: CNI, Networking - L: @Jiang Wang - - * N: Canonical - D: Canonical's Kubernetes distribution microk8s uses Cilium as CNI plugin - U: Networking, NetworkPolicy, and Kubernetes services - L: https://microk8s.io/ - - * N: Capital One - D: Capital One uses Cilium as its standard CNI for all Kubernetes environments - U: CNI, CiliumClusterWideNetworkpolicy, CiliumNetworkPolicy, Hubble, network visibility - L: https://www.youtube.com/watch?v=hwOpCKBaJ-w - - * N: CENGN - Centre of Excellence in Next Generation Networks - D: CENGN is using Cilium in multiple clusters including production and development clusters (self-hosted k8s, On-premises) - U: L3/L4/L7 network policies, Monitoring via Prometheus metrics & Hubble - L: https://www.youtube.com/watch?v=yXm7yZE2rk4 - Q: @rmaika @mohahmed13 - - * N: Cistec - D: Cistec is a clinical information system provider and uses Cilium as the CNI plugin. - U: Networking and network policy - L: https://www.cistec.com/ - - * N: Civo - D: Civo is offering Cilium as the CNI option for Civo users to choose it for their Civo Kubernetes clusters. - U: Networking and network policy - L: https://www.civo.com/kubernetes - - * N: ClickHouse - D: ClickHouse uses Cilium as CNI for AWS Kubernetes environments - U: CiliumNetworkPolicy, Hubble, ClusterMesh - L: https://clickhouse.com - - * N: Cloutomate - D: Cloutomate uses Cilium as CNI for itself and customer installations - U: Networking Observability and Security, Service Mesh, Cluster Mesh - L: https://cloutomate.de - - * N: Cognite - D: Cognite is an industrial DataOps provider and uses Cilium as the CNI plugin - Q: @Robert Collins - - * N: CONNY - D: CONNY is legaltech platform to improve access to justice for individuals - U: Networking, NetworkPolicy, Services - Q: @ant31 - L: https://conny.de - - * N: Cosmonic - D: Cilium is the CNI for Cosmonic's Nomad based PaaS - U: Networking, NetworkPolicy, Transparent Encryption - L: https://cilium.io/blog/2023/01/18/cosmonic-user-story/ - - * N: Crane - D: Crane uses Cilium as the default CNI - U: Networking, NetworkPolicy, Services - L: https://github.com/slzcc/crane - Q: @slzcc - - * N: Cybozu - D: Cybozu deploys Cilium to on-prem Kubernetes Cluster and uses it with Coil by CNI chaining. - U: CNI Chaining, L4 LoadBalancer, NetworkPolicy, Hubble - L: https://cybozu-global.com/ - - * N: Daimler Truck AG - D: The CSG RuntimeDepartment of DaimlerTruck is maintaining an AKS k8s cluster as a shared resource for DevOps crews and is using Cilium as the default CNI (BYOCNI). - U: Networking, NetworkPolicy and Monitoring - L: https://daimlertruck.com - Q: @brandshaide - - * N: DaoCloud - spiderpool & merbridge - D: spiderpool is using Cilium as their main CNI plugin for overlay and merbridge is using Cilium eBPF library to speed up your Service Mesh - U: CNI, Service load-balancing, cluster mesh - L: https://github.com/spidernet-io/spiderpool, https://github.com/merbridge/merbridge - Q: @weizhoublue, @kebe7jun - - * N: Datadog - D: Datadog is using Cilium in AWS (self-hosted k8s) - U: ENI Networking, Service load-balancing, Encryption, Network Policies, Hubble - Q: @lbernail, @roboll, @mvisonneau - - * N: Dcode.tech - D: We specialize in AWS and Kubernetes, and actively implement Cilium at our clients. - U: CNI, CiliumNetworkPolicy, Hubble UI - L: https://dcode.tech/ - Q: @eliranw, @maordavidov - - * N: Deckhouse - D: Deckhouse Kubernetes Platform is using Cilium as a one of the supported CNIs. - U: Networking, Security, Hubble UI for network visibility - L: https://github.com/deckhouse/deckhouse - - * N: Deezer - D: Deezer is using Cilium as CNI for all our on-prem clusters for its performance and security. We plan to leverage BGP features as well soon - U: CNI, Hubble, kube-proxy replacement, eBPF - L: https://github.com/deezer - - * N: DigitalOcean - D: DigitalOcean is using Cilium as the CNI for Digital Ocean's managed Kubernetes Services (DOKS) - U: Networking and network policy - L: https://github.com/digitalocean/DOKS - - * N: Docaposte - D: Docaposte is the digital trust leader in France. We have selected Cilium as our CNI for Kubernetes deployments in production environments, due to its performance and advanced features. - U: eBPF, CiliumclusterWideNetworkPolicy, CiliumNetworkPolicy, kube-proxy replacement, Hubble - L: https://docaposte.fr - Q: @albundy83 - - * N: ECCO Data and AI - D: ECCO Data and AI is using Cilium as CNI in all their clusters and environments (EKS in AWS). - U: CNI, IPv6 networking, Service Load Balancing and Cluster Mesh - L: https://github.com/SneaksAndData - - * N: Edgeless Systems - D: Edgeless Systems is using Cilium as the CNI for Edgeless System's Confidential Kubernetes Distribution (Constellation) - U: Networking (CNI), Transparent Encryption (WG), - L: https://docs.edgeless.systems/constellation/architecture/networking - Q: @m1ghtym0 - - * N: Eficode - D: As a cloud-native and devops consulting firm, we have implemented Cilium on customer engagements - U: CNI, CiliumNetworkPolicy at L7, Hubble - L: https://eficode.com/ - Q: @Andy Allred - - * N: Elastic Path - D: Elastic Path is using Cilium in their CloudOps for Kubernetes production clusters - U: CNI - L: https://documentation.elasticpath.com/cloudops-kubernetes/docs/index.html - Q: @Neil Seward - - * N: Entrywan - D: Entrywan provides Cilium as a CNI option in its managed kubernetes service - U: CNI - L: https://www.entrywan.com/docs#kubernetes - Q: @aarongroom - - * N: Equinix - D: Equinix Metal is using Cilium for production and non-production environments on bare metal - U: CNI, CiliumClusterWideNetworkpolicy, CiliumNetworkPolicy, BGP advertisements, Hubble, network visibility - L: https://metal.equinix.com/ - Q: @tylerauerbeck, @fishnix, @tenyo, @hegartyk - - * N: Equinix - D: Equinix NL Managed Services is using Cilium with their Managed Kubernetes offering - U: CNI, network policies, visibility - L: https://www.equinix.nl/products/support-services/managed-services/netherlands - Q: @jonkerj - - * N: EvoCloud - D: EvoCloud uses Cilium as a Kubernetes proxy replacement, CNI with Gateway API integration, Cluster mesh with BGP enabled, Network policy and Hubble Observability. - U: L4/L7 Networking, L2 Announcement, Network Policies, Kube-proxy replacement, CNI with Gateway API, Hubble for tracing and observability, ClusterMesh and ServiceMesh - L: https://github.com/evocloud-dev/evocloud-paas - Q: @geanttechnology, @escapevelocity17321 - - * N: Exoscale - D: Exoscale is offering Cilium as a CNI option on its managed Kubernetes service named SKS (Scalable Kubernetes Service) - U: CNI, Networking - L: https://www.exoscale.com/sks/ - Q: @Antoine - - * N: finleap connect - D: finleap connect is using Cilium in their production clusters (self-hosted, bare-metal, private cloud) - U: CNI, NetworkPolicies - Q: @chue - - * N: Form3 - D: Form3 is using Cilium in their production clusters (self-hosted, bare-metal, private cloud) - U: Service load-balancing, Encryption, CNI, NetworkPolicies - Q: @kevholditch-f3, samo-f3, ewilde-form3 - - * N: FRSCA - Factory for Repeatable Secure Creation of Artifacts - D: FRSCA is utilizing tetragon integrated with Tekton to create runtime attestation to attest artifact and builder attributes - U: Runtime observability - L: https://github.com/buildsec/frsca - Q: @Parth Patel - - * N: F5 Inc - D: F5 helps customers with Cilium VXLAN tunnel integration with BIG-IP - U: Networking - L: https://github.com/f5devcentral/f5-ci-docs/blob/master/docs/cilium/cilium-bigip-info.rst - Q: @vincentmli - - * N: Gcore - D: Gcore supports Cilium as CNI provider for Gcore Managed Kubernetes Service - U: CNI, Networking, NetworkPolicy, Kubernetes Services - L: https://gcore.com/news/cilium-cni-support - Q: @rzdebskiy - - * N: Giant Swarm - D: Giant Swarm is using Cilium in their Cluster API based managed Kubernetes service (AWS, Azure, GCP, OpenStack, VMware Cloud Director and VMware vSphere) as CNI - U: Networking - L: https://www.giantswarm.io/ - - * N: GitLab - D: GitLab is using Cilium to implement network policies inside Auto DevOps deployed clusters for customers using k8s - U: Network policies - L: https://docs.gitlab.com/ee/user/clusters/applications.html#install-cilium-using-gitlab-ci - Q: @ap4y @whaber - - * N: Google - D: Google is using Cilium in Anthos and Google Kubernetes Engine (GKE) as Dataplane V2 - U: Networking, network policy, and network visibility - L: https://cloud.google.com/blog/products/containers-kubernetes/bringing-ebpf-and-cilium-to-google-kubernetes-engine - - * N: G DATA CyberDefense AG - D: G DATA CyberDefense AG is using Cilium on our managed on premise clusters. - U: Networking, network policy, security, and network visibility - L: https://gdatasoftware.com - Q: @farodin91 - - * N: Guidewire Software, Inc. - D: Guidewire Software, Inc. is using Cilium for the Guidewire Cloud Platform. - U: CNI, network policy, and network visibility - L: https://www.guidewire.com - - * N: IDNIC | Kadabra - D: IDNIC is the National Internet Registry administering IP addresses for INDONESIA, uses Cilium to powered Kadabra project runing services across multi data centers. - U: Networking, Network Policies, kube-proxy Replacement, Service Load Balancing and Cluster Mesh - L: https://ris.idnic.net/ - Q: @ardikabs - - * N: IKEA IT AB - D: IKEA IT AB is using Cilium for production and non-production environments (self-hosted, bare-metal, private cloud) - U: Networking, CiliumclusterWideNetworkPolicy, CiliumNetworkPolicy, kube-proxy replacement, Hubble, Direct routing, egress gateway, hubble-otel, Multi Nic XDP, BGP advertisements, Bandwidth Manager, Service Load Balancing, Cluster Mesh - L: https://www.ingka.com/ - - * N: Immerok - D: Immerok uses Cilium for cross-cluster communication and network isolation; Immerok Cloud is a serverless platform for the full power of [Apache Flink](https://flink.apache.org) at any scale. - U: Networking, network policy, observability, cluster mesh, kube-proxy replacement, security, CNI - L: https://immerok.io - Q: @austince, @dmvk - - * N: Incentive.me - D: Incentive.me use Cilium, Tetragon and Hubble for enterprise networking, observability, and security of all environments. - U: Networking, network policy, observability, cluster mesh, kube-proxy replacement, security, egress gateway, service load balancing, CNI - L: https://incentive.me - Q: @lucasfcnunes - - * N: Infomaniak - D: Infomaniak is using Cilium in their production clusters (self-hosted, bare-metal and openstack) - U: Networking, CiliumNetworkPolicy, BPF NodePort, Direct routing, kube-proxy replacement - L: https://www.infomaniak.com/en - Q: @reneluria - - * N: innoQ Schweiz GmbH - D: As a consulting company we added Cilium to a couple of our customers infrastructure - U: Networking, CiliumNetworkPolicy at L7, kube-proxy replacement, encryption - L: https://www.cloud-migration.ch/ - Q: @fakod - - * N: Intility AS - D: Intility is a managed service provider for enterprises and we use Cilium, Tetragon and Hubble to deliver world class managed Kubernetes clusters to customers from our own private cloud - U: Networking, CiliumNetworkPolicy, CiliumCIDRGroup, security, CNI - L: https://intility.com/container-platform/ - Q: @jonasks, @daniwk, @stianfro - - * N: Isovalent - D: Cilium is the platform that powers Isovalent’s enterprise networking, observability, and security solutions - U: Networking, network policy, observability, cluster mesh, kube-proxy replacement, security, egress gateway, service load balancing, CNI - L: https://isovalent.com/product/ - Q: @BillMulligan - - * N: Jar - D: Cilium is used as Jar's CNI on all prod and pre production environments. - U: Networking, network policy, observability, cluster mesh, kube-proxy replacement, security, egress gateway, service load balancing, CNI - L: https://myjar.app/blog/engineering/ - Q: @rohan-changejar @rohangrge - - - * N: JUMO - D: JUMO is using Cilium as their CNI plugin for all of their AWS-hosted EKS clusters - U: Networking, network policy, network visibility, cluster mesh - Q: @Matthieu ANTOINE, @Carlos Castro, @Joao Coutinho (Slack) - - * N: Kakao - D: Kakao is using Cilium as the CNI for their private cloud's managed Kubernetes service - U: Custom eBPF programs, networking, network policy, kube-proxy replacement - L: https://youtu.be/WRACr5nXl9U - Q: @gyutaeb - - * N: KA-NABELL - D: KA-NABELL harnesses Cilium to deliver Kubernetes networking with robust security and clear observability. - U: CNI/ENI Networking, kube-proxy replacement, Monitoring via Prometheus metrics & Hubble, eBPF, CiliumNetworkPolicy - L: https://speakerdeck.com/andoshin11/envoy-external-authztogrpc-extensionwoli-yong-sita-wan-zhang-ranai-microservicesren-zheng-ren-ke-ji-pan?slide=8 - Q: @kahirokunn - - * N: Keploy - D: Keploy is using the Cilium to capture the network traffic to perform E2E Testing. - U: Networking, network policy, Monitoring, E2E Testing - L: https://keploy.io/ - - * N: Kilo - D: Cilium is a supported CNI for Kilo. When used together, Cilium + Kilo create a full mesh via WireGuard for Kubernetes in edge environments. - U: CNI, Networking, Hubble, kube-proxy replacement, network policy - L: https://kilo.squat.ai/ - Q: @squat, @arpagon - - * N: Koyeb - D: Koyeb hosts microVMs on its own servers and uses Cilium to power a mesh in between those - U: Networking, policies inside a non-Kubernetes environment - L: https://www.koyeb.com/blog/70-faster-deployments-and-high-performance-private-network - Q: @koyeb on Twitter / https://community.koyeb.com/ - - * N: kOps - D: kOps is using Cilium as one of the supported CNIs - U: Networking, Hubble, Encryption, kube-proxy replacement - L: kops.sigs.k8s.io/ - Q: @olemarkus - - * N: Kryptos Logic - D: Kryptos is a cyber security company that is using Kubernetes on-prem in which Cilium is our CNI of choice. - U: Networking, Observability, kube-proxy replacement - - * N: kubeasz - D: kubeasz, a certified kubernetes installer, is using Cilium as a one of the supported CNIs. - U: Networking, network policy, Hubble for network visibility - L: https://github.com/easzlab/kubeasz - - * N: Kube-OVN - D: Kube-OVN uses Cilium to enhance service performance, security and monitoring. - U: CNI-Chaining, Hubble, kube-proxy replacement - L: https://github.com/kubeovn/kube-ovn/blob/master/docs/IntegrateCiliumIntoKubeOVN.md - Q: @oilbeater - - * N: Kube-Hetzner - D: Kube-Hetzner is a open-source Terraform project that uses Cilium as an possible CNI in its cluster deployment on Hetzner Cloud. - U: Networking, Hubble, kube-proxy replacement - L: https://github.com/kube-hetzner/terraform-hcloud-kube-hetzner#cni - Q: @MysticalTech - - * N: Kubermatic - D: Kubermatic Kubernetes Platform is using Cilium as a one of the supported CNIs. - U: Networking, network policy, Hubble for network visibility - L: https://github.com/kubermatic/kubermatic - - * N: KubeSphere - KubeKey - D: KubeKey is an open-source lightweight tool for deploying Kubernetes clusters and addons efficiently. It uses Cilium as one of the supported CNIs. - U: Networking, Security, Hubble UI for network visibility - L: https://github.com/kubesphere/kubekey - Q: @FeynmanZhou - - * N: K8e - Simple Kubernetes Distribution - D: Kubernetes Easy (k8e) is a lightweight, Extensible, Enterprise Kubernetes distribution. It uses Cilium as default CNI network. - U: Networking, network policy, Hubble for network visibility - L: https://github.com/xiaods/k8e - Q: @xds2000 - - * N: LinkPool - D: LinkPool is a professional Web3 infrastructure provider. - U: LinkPool is using Cilium as the CNI for its on-premise production clusters - L: https://linkpool.com - Q: @jleeh - - * N: Liquid Reply - D: Liquid Reply is a professional service provider and utilizes Cilium on suitable projects and implementations. - U: Networking, network policy, Hubble for network visibility, Security - L: http://liquidreply.com - Q: @mkorbi - - * N: Magic Leap - D: Magic Leap is using Hubble plugged to GKE Dataplane v2 clusters - U: Hubble - Q: @romachalm - - * N: Melenion Inc - D: Melenion is using Cilium as the CNI for its on-premise production clusters - U: Service Load Balancing, Hubble - Q: @edude03 - - * N: Meltwater - D: Meltwater is using Cilium in AWS on self-hosted multi-tenant k8s clusters as the CNI plugin - U: ENI Networking, Encryption, Monitoring via Prometheus metrics & Hubble - Q: @recollir, @dezmodue - - * N: Microsoft - D: Microsoft is using Cilium in "Azure CNI powered by Cilium" AKS (Azure Kubernetes Services) clusters - L: https://techcommunity.microsoft.com/t5/azure-networking-blog/azure-cni-powered-by-cilium-for-azure-kubernetes-service-aks/ba-p/3662341 - Q: @tamilmani1989 @chandanAggarwal - - * N: Mobilab - D: Mobilab uses Cilium as the CNI for its internal cloud - U: CNI - L: https://mobilabsolutions.com/2019/01/why-we-switched-to-cilium/ - - * N: MyFitnessPal - D: MyFitnessPal trusts Cilium with high volume user traffic in AWS on self-hosted k8s clusters as the CNI plugin and in GKE with Dataplane V2 - U: Networking (CNI, Maglev, kube-proxy replacement, local redirect policy), Observability (Network metrics with Hubble, DNS proxy, service maps, policy troubleshooting) and Security (Network Policy) - L: https://www.myfitnesspal.com - - * N: Mux, Inc. - D: Mux deploys Cilium on self-hosted k8s clusters (Cluster API) in GCP and AWS to run its video streaming/analytics platforms. - U: Pod networking (CNI, IPAM, Host-reachable Services), Hubble, Cluster-mesh. TBD: Network Policy, Transparent Encryption (WG), Host Firewall. - L: https://mux.com - Q: @dilyevsky - - * N: NetBird - D: NetBird uses Cilium to compile BPF to Go for cross-platform DNS management and NAT traversal - U: bpf2go to compile a C source file into eBPF bytecode and then to Go - L: https://netbird.io/knowledge-hub/using-xdp-ebpf-to-share-default-dns-port-between-resolvers - Q: @braginini - - * N: Netcloud AG - D: As a Swiss ICT company we are using Cilium as their CNI for mission critical, on premise k8s clusters. - U: Overlay Networking, CNI, Network Policy, Kube-Proxy Replacement, Service load-balancing - L: https://www.netcloud.ch - - * N: NETWAYS Web Services - D: NETWAYS Web Services offers Cilium to their clients as CNI option for their Managed Kubernetes clusters. - U: Networking (CNI), Observability (Hubble) - L: https://nws.netways.de/managed-kubernetes/ - - * N: New York Times (the) - D: The New York Times is using Cilium on EKS to build multi-region multi-tenant shared clusters - U: Networking (CNI, EKS IPAM, Maglev, kube-proxy replacement, Direct Routing), Observability (Network metrics with Hubble, policy troubleshooting) and Security (Network Policy) - L: https://www.nytimes.com/, https://youtu.be/9FDpMNvPrCw - Q: @abebars - - * N: Nexxiot - D: Nexxiot is an IoT SaaS provider using Cilium as the main CNI plugin on AWS EKS clusters - U: Networking (IPAM, CNI), Security (Network Policies), Visibility (hubble) - L: https://nexxiot.com - - * N: Nine Internet Solutions AG - D: Nine uses Cilium on all Nine Kubernetes Engine clusters - U: CNI, network policy, kube-proxy replacement, host firewall - L: https://www.nine.ch/en/kubernetes - - * N: Northflank - D: Northflank is a PaaS and uses Cilium as the main CNI plugin across GCP, Azure, AWS and bare-metal - U: Networking, network policy, hubble, packet monitoring and network visibility - L: https://northflank.com - Q: @NorthflankWill, @Champgoblem - - * N: Nutanix - D: Nutanix uses Cilium as the default CNI plugin for NKP (Nutanix Kubernetes Platform) when deployed on AHV - U: Networking, NetworkPolicy, Services - L: https://www.nutanix.com/products/kubernetes-management-platform - Q: @tuxtof - - * N: Overstock Inc. - D: Overstock is using Cilium as the main CNI plugin on bare-metal clusters (self hosted k8s). - U: Networking, network policy, hubble, observability - - * N: Palantir Technologies Inc. - D: Palantir is using Cilium as their main CNI plugin in all major cloud providers [AWS/Azure/GCP] (self hosted k8s). - U: ENI networking, L3/L4 policies, FQDN based policy, FQDN filtering, IPSec - Q: ungureanuvladvictor - - * N: Palark GmbH - D: Palark uses Cilium for networking in its Kubernetes platform provided to numerous customers as a part of its DevOps as a Service offering. - U: CNI, Networking, Network policy, Security, Hubble UI - L: https://blog.palark.com/why-cilium-for-kubernetes-networking/ - Q: @shurup - - * N: Parseable - D: Parseable uses Tertragon for collecting and ingesting eBPF logs for Kubernetes clusters. - U: Security, eBPF, Tetragon - L: https://www.parseable.io/blog/ebpf-log-analytics - Q: @nitisht - - * N: Pionative - D: Pionative supplies all its clients across cloud providers with - Kubernetes running Cilium to deliver the best performance out there. - U: CNI, Networking, Security, eBPF - L: https://www.pionative.com - Q: @Pionerd - - * N: Plaid Inc - D: Plaid is using Cilium as their CNI plugin in self-hosted Kubernetes on AWS. - U: CNI, network policies - L: [https://plaid.com](https://plaid.com/contact/) - Q: @diversario @jandersen-plaid - - * N: PlanetScale - D: PlanetScale is using Cilium as the CNI for its serverless database platform. - U: Networking (CNI, IPAM, kube-proxy replacement, native routing), Network Security, Cluster Mesh, Load Balancing - L: https://planetscale.com/ - Q: @dctrwatson - - * N: plusserver Kubernetes Engine (PSKE) - D: PSKE uses Cilium for multiple scenarios, for examples for managed Kubernetes clusters provided with Gardener Project across AWS and OpenStack. - U: CNI , Overlay Network, Network Policies - L: https://www.plusserver.com/en/product/managed-kubernetes/, https://github.com/gardener/gardener-extension-networking-cilium - - * N: Polar Signals - D: Polar Signals uses Cilium as the CNI on its GKE dataplane v2 based clusters. - U: Networking - L: https://polarsignals.com - Q: @polarsignals @brancz - - * N: Polverio - D: Polverio KubeLift is a single-node Kubernetes distribution optimized for Azure, using Cilium as the CNI. - U: CNI, IPAM - L: https://polverio.com - Q: @polverio @stuartpreston - - * N: Poseidon Laboratories - D: Poseidon's Typhoon Kubernetes distro uses Cilium as the default CNI and its used internally - U: Networking, policies, service load balancing - L: https://github.com/poseidon/typhoon/ - Q: @dghubble @typhoon8s - - * N: PostFinance AG - D: PostFinance is using Cilium as their CNI for all mission critical, on premise k8s clusters - U: Networking, network policies, kube-proxy replacement - L: https://github.com/postfinance - - * N: Proton AG - D: Proton is using Cilium as their CNI for all their Kubernetes clusters - U: Networking, network policies, host firewall, kube-proxy replacement, Hubble - L: https://proton.me/ - Q: @j4m3s @MrFreezeex - - * N: Radio France - D: Radio France is using Cilium in their production clusters (self-hosted k8s with kops on AWS) - U: Mainly Service load-balancing - Q: @francoisj - - * N: Qpoint - D: An eBPF-based egress observability platform for your cloud and production applications - U: CNI, bpf2go to compile a C source file into eBPF bytecode and then to Go - L: https://www.qpoint.io/ and https://github.com/qpoint-io - Q: @Marc Barry - - * N: Rancher Labs, now part of SUSE - D: Rancher Labs certified Kubernetes distribution RKE2 can be deployed with Cilium. - U: Networking and network policy - L: https://github.com/rancher/rke and https://github.com/rancher/rke2 - - * N: Rapyuta Robotics. - D: Rapyuta is using cilium as their main CNI plugin. (self hosted k8s) - U: CiliumNetworkPolicy, Hubble, Service Load Balancing. - Q: @Gowtham - - * N: Rafay Systems - D: Rafay's Kubernetes Operations Platform uses Cilium for centralized network visibility and network policy enforcement - U: NetworkPolicy, Visibility via Prometheus metrics & Hubble - L: https://rafay.co/platform/network-policy-manager/ - Q: @cloudnativeboy @mohanatreya - - * N: Robinhood Markets - D: Robinhood uses Cilium for Kubernetes overlay networking in an environment where we run tests for backend services - U: CNI, Overlay networking - Q: @Madhu CS - - * N: Santa Claus & the Elves - D: All our infrastructure to process children's letters and wishes, toy making, and delivery, distributed over multiple clusters around the world, is now powered by Cilium. - U: ClusterMesh, L4LB, XDP acceleration, Bandwidth manager, Encryption, Hubble - L: https://qmonnet.github.io/whirl-offload/2024/01/02/santa-switches-to-cilium/ - - * N: SAP - D: SAP uses Cilium for multiple internal scenarios. For examples for self-hosted Kubernetes scenarios on AWS with SAP Concur and for managed Kubernetes clusters provided with Gardener Project across AWS, Azure, GCP, and OpenStack. - U: CNI , Overlay Network, Network Policies - L: https://www.concur.com, https://gardener.cloud/, https://github.com/gardener/gardener-extension-networking-cilium - Q: @dragan (SAP Concur), @docktofuture & @ScheererJ (Gardener) - - * N: Sapian - D: Sapian uses Cilium as the default CNI in our product DialBox Cloud; DialBox cloud is an Edge Kubernetes cluster using [kilo](https://github.com/squat/kilo) for WireGuard mesh connectivity inter-nodes. Therefore, Cilium is crucial for low latency in real-time communications environments. - U: CNI, Network Policies, Hubble, kube-proxy replacement - L: https://sapian.com.co, https://arpagon.co/blog/k8s-edge - Q: @arpagon - - * N: Schenker AG - D: Land transportation unit of Schenker uses Cilium as default CNI in self-managed kubernetes clusters running in AWS - U: CNI, Monitoring, kube-proxy replacement - L: https://www.dbschenker.com/global - Q: @amirkkn - - * N: Scigility AG - D: We use Cilium as the default CNI across client implementations and also for our internal platform. - U: CNI, Monitoring, kube-proxy replacement, Hubble - L: https://scigility.com/ - Q: @ciil - - * N: Sealos - D: Sealos is using Cilium as a consistent CNI for our Sealos Cloud. - U: Networking, Service, kube-proxy replacement, Network Policy, Hubble - L: https://sealos.io - Q: @fanux, @yangchuansheng - - * N: SeatGeek - D: SeatGeek uses Cilium as the default CNI/service mesh for AWS hosted clusters - U: CNI, ClusterMesh, Network Policy, Hubble, L7 Mesh - L: https://seatgeek.com - Q: @byxorna, @aetimmes - - * N: Seznam.cz - D: Seznam.cz uses Cilium in multiple scenarios in on-prem DCs. At first as L4LB which loadbalances external traffic into k8s+openstack clusters then as CNI in multiple k8s and openstack clusters which are all connected in a clustermesh to enforce NetworkPolicies across pods/VMs. - U: L4LB, L3/4 CNPs+CCNPs, KPR, Hubble, HostPolicy, Direct-routing, IPv4+IPv6, ClusterMesh - Q: @oblazek - - * N: Simple - D: Simple uses cilium as default CNI in Kubernetes clusters (AWS EKS) for both development and production environments. - U: CNI, Network Policies, Hubble - L: https://simple.life - Q: @sergeyshevch - - * N: Scaleway - D: Scaleway uses Cilium as the default CNI for Kubernetes Kapsule - U: Networking, NetworkPolicy, Services - L: @jtherin @remyleone - - * N: Schuberg Philis - D: Schuberg Philis uses Cilium as CNI for mission critical kubernetes clusters we run for our customers. - U: CNI (instead of amazon-vpc-cni-k8s), DefaultDeny(Zero Trust), Hubble, CiliumNetworkPolicy, CiliumClusterwideNetworkPolicy, EKS - L: https://schubergphilis.com/en - Q: @stimmerman @shoekstra @mbaumann - - * N: SDV Services - D: SDV Services uses Cilium to host Wordpress multi-tenant the cloud-native way and also as the CNI for customer Kubernetes clusters. - U: CNI, Networking, NetworkPolicy, Hubble, IPAM, Kubernetes services - L: https://sdvservices.nl - Q: @Sjouke de Vries - - * N: SI Analytics - D: SI Analytics uses Cilium as CNI in self-managed Kubernetes clusters in on-prem DCs. And also use Cilium as CNI in its GKE dataplane v2 based clusters. - U: CNI, Network Policies, Hubble - L: https://si-analytics.ai, https://ovision.ai - Q: @jholee - - * N: SIGHUP - D: SIGHUP integrated Cilium as a supported CNI for KFD (Kubernetes Fury Distribution), our enterprise-grade OSS reference architecture - U: Available supported CNI - L: https://sighup.io, https://github.com/sighupio/fury-kubernetes-networking - Q: @jnardiello @nutellino - - * N: SINAD - D: SINAD uses Cilium and integrates Tetragon (Which is amazing) to their application EzyKube - U: CNI, Networking, Node2Node & Pod2Pod Encryption, Kube-Proxy Replacement, eBPF, security - L: https://sinad.io - - * N: SmileDirectClub - D: SmileDirectClub is using Cilium in manufacturing clusters (self-hosted on vSphere and AWS EC2) - U: CNI - Q: @joey, @onur.gokkocabas - - * N: Snapp - D: Snapp is using Cilium in production for its on premise openshift clusters - U: CNI, Network Policies, Hubble - Q: @m-yosefpor - - * N: Solo.io - D: Cilium is part of Gloo Application Networking platform, with a “batteries included but swappable” manner - U: CNI, Network Policies - Q: @linsun - - * N: S&P Global - D: S&P Global uses Cilium as their multi-cloud CNI - U: CNI - L: https://www.youtube.com/watch?v=6CZ_SSTqb4g - - * N: Spectro Cloud - D: Spectro Cloud uses & promotes Cilium for clusters its K8S management platform (Palette) deploys - U: CNI, Overlay network, kube-proxy replacement - Q: @Kevin Reeuwijk - - * N: Spherity - D: Spherity is using Cilium on AWS EKS - U: CNI/ENI Networking, Network policies, Hubble - Q: @solidnerd - - * N: Sportradar - D: Sportradar is using Cilium as their main CNI plugin in AWS (using kops) - U: L3/L4 policies, Hubble, BPF NodePort, CiliumClusterwideNetworkPolicy - Q: @Eric Bailey, @Ole Markus - - * N: Sproutfi - D: Sproutfi uses Cilium as the CNI on its GKE based clusters - U: Service Load Balancing, Hubble, Datadog Integration for Prometheus metrics - Q: @edude03 - - * N: SuperOrbital - D: As a Kubernetes-focused consulting firm, we have implemented Cilium on customer engagements - U: CNI, CiliumNetworkPolicy at L7, Hubble - L: https://superorbital.io/ - Q: @jmcshane - - * N: Syself - D: Syself uses Cilium as the CNI for Syself Autopilot, a managed Kubernetes platform - U: CNI, HostFirewall, Monitoring, CiliumClusterwideNetworkPolicy, Hubble - L: https://syself.com - Q: @sbaete - - * N: Talos - D: Cilium is one of the supported CNI's in Talos - U: Networking, NetworkPolicy, Hubble, BPF NodePort - L: https://github.com/talos-systems/talos - Q: @frezbo, @smira, @Ulexus - - * N: Tencent Cloud - D: Tencent Cloud container team designed the TKE hybrid cloud container network solution with Cilium as the cluster network base - U: Networking, CNI - L: https://segmentfault.com/a/1190000040298428/en - - * N: teuto.net Netzdienste GmbH - D: teuto.net is using cilium for their managed k8s service, t8s - U: CNI, CiliumNetworkPolicy, Hubble, Encryption, ... - L: https://teuto.net/managed-kubernetes - Q: @cwrau - - * N: Trendyol - D: Trendyol.com has recently implemented Cilium as the default CNI for its production Kubernetes clusters starting from version 1.26. - U: Networking, kube-proxy replacement, eBPF, Network Visibility with Hubble and Grafana, Local Redirect Policy - L: https://t.ly/FDCZK - - * N: T-Systems International - D: TSI uses Cilium for it's Open Sovereign Cloud product, including as a CNI for Gardener-based Kubernetes clusters and bare-metal infrastructure managed by OnMetal. - U: CNI, overlay network, NetworkPolicies - Q: @ManuStoessel - - * N: uSwitch - D: uSwitch is using Cilium in AWS for all their production clusters (self hosted k8s) - U: ClusterMesh, CNI-Chaining (with amazon-vpc-cni-k8s) - Q: @jirving - - * N: United Cloud - D: United Cloud is using Cilium for all non-production and production clusters (on-premises) - U: CNI, Hubble, CiliumNetworkPolicy, CiliumClusterwideNetworkPolicy, ClusterMesh, Encryption - L: https://united.cloud - Q: @boris - - * N: Utmost Software, Inc - D: Utmost is using Cilium in all tiers of its Kubernetes ecosystem to implement zero trust - U: CNI, DefaultDeny(Zero Trust), Hubble, CiliumNetworkPolicy, CiliumClusterwideNetworkPolicy - L: https://blog.utmost.co/zero-trust-security-at-utmost - Q: @andrewholt - - * N: Trip.com - D: Trip.com is using Cilium in their production clusters (self-hosted k8s, On-premises and AWS) - U: ENI Networking, Service load-balancing, Direct routing (via Bird) - L: https://ctripcloud.github.io/cilium/network/2020/01/19/trip-first-step-towards-cloud-native-networking.html - Q: @ArthurChiao - - * N: Tailor Brands - D: Tailor Brands is using Cilium in their production, staging, and development clusters (AWS EKS) - U: CNI (instead of amazon-vpc-cni-k8s), Hubble, Datadog Integration for Prometheus metrics - Q: @liorrozen - - * N: Twilio - D: Twilio Segment is using Cilium across their k8s-based compute platform - U: CNI, EKS direct routing, kube-proxy replacement, Hubble, CiliumNetworkPolicies - Q: @msaah - - * N: ungleich - D: ungleich is using Cilium as part of IPv6-only Kubernetes deployments. - U: CNI, IPv6 only networking, BGP, eBPF - Q: @Nico Schottelius, @nico:ungleich.ch (Matrix) - - * N: Veepee - D: Veepee is using Cilium on their on-premise Kubernetes clusters, hosting majority of their applications. - U. CNI, BGP, eBPF, Hubble, DirectRouting (via kube-router) - Q: @nerzhul - - * N: Virtuozzo - D: Cilium CNI is the default network plugin for Kubernetes clusters within Virtuozzo Hybrid Infrastructure. - U: Networking, NetworkPolicy, Services - L: https://docs.virtuozzo.com/virtuozzo_hybrid_infrastructure_6_3_admins_guide/index.html#provisioning-kubernetes.html - Q: egor.ustinov@virtuozzo.com - - * N: VMware by Broadcom - D: VMware offers multi-arch (ARM, AMD) and multi-distro (Ubuntu, RedHat UBI, Debian, PhotonOS) Cilium as part of the Tanzu Application Catalog, enabling customers to deploy it in their Kubernetes infrastructure. - U: CNI, Hubble, BGP, eBPF, CiliumNetworkPolicy, CiliumClusterwideNetworkPolicy - L: https://app-catalog.vmware.com/catalog?gfilter=cilium - Q: @carrodher - - * N: Wildlife Studios - D: Wildlife Studios is using Cilium in AWS for all their game production clusters (self hosted k8s) - U: ClusterMesh, Global Service Load Balancing. - Q: @Oki @luanguimaraesla @rsafonseca - - * N: WSO2 - D: WSO2 is using Cilium to implemented Zero Trust Network Security for their Kubernetes clusters - U: CNI, WireGuard Transparent Encryption, CiliumClusterWideNetworkpolicy, CiliumNetworkPolicy, Hubble, Layer 7 visibility and Service Mesh via Cilium Envoy - L: https://www.cncf.io/case-studies/wso2/ - Q: @lakwarus @isala404 @tharinduwijewardane - - * N: Yahoo! - D: Yahoo is using Cilium for L4 North-South Load Balancing for Kubernetes Services - L: https://www.youtube.com/watch?v=-C86fBMcp5Q - - * N: ZeroHash - D: Zero Hash is using Cilium as CNI for networking, security and monitoring features for Kubernetes clusters - U: CNI/ENI Networking, Network policies, Hubble - Q: @eugenestarchenko diff --git a/vendor/github.com/cilium/cilium/VERSION b/vendor/github.com/cilium/cilium/VERSION deleted file mode 100644 index ee017091ff..0000000000 --- a/vendor/github.com/cilium/cilium/VERSION +++ /dev/null @@ -1 +0,0 @@ -1.18.0-dev diff --git a/vendor/github.com/cilium/cilium/api/v1/flow/flow.pb.go b/vendor/github.com/cilium/cilium/api/v1/flow/flow.pb.go index ca90a8a6d5..a71c0c56c5 100644 --- a/vendor/github.com/cilium/cilium/api/v1/flow/flow.pb.go +++ b/vendor/github.com/cilium/cilium/api/v1/flow/flow.pb.go @@ -4,7 +4,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.5 -// protoc v5.29.3 +// protoc v6.30.1 // source: flow/flow.proto package flow diff --git a/vendor/github.com/cilium/cilium/api/v1/observer/observer.pb.go b/vendor/github.com/cilium/cilium/api/v1/observer/observer.pb.go index bc136ad831..cf2a39cfa4 100644 --- a/vendor/github.com/cilium/cilium/api/v1/observer/observer.pb.go +++ b/vendor/github.com/cilium/cilium/api/v1/observer/observer.pb.go @@ -4,7 +4,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.5 -// protoc v5.29.3 +// protoc v6.30.1 // source: observer/observer.proto package observer diff --git a/vendor/github.com/cilium/cilium/api/v1/observer/observer_grpc.pb.go b/vendor/github.com/cilium/cilium/api/v1/observer/observer_grpc.pb.go index f9e149cf81..c4e670e6a9 100644 --- a/vendor/github.com/cilium/cilium/api/v1/observer/observer_grpc.pb.go +++ b/vendor/github.com/cilium/cilium/api/v1/observer/observer_grpc.pb.go @@ -4,7 +4,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.5.1 -// - protoc v5.29.3 +// - protoc v6.30.1 // source: observer/observer.proto package observer diff --git a/vendor/github.com/cilium/cilium/api/v1/relay/relay.pb.go b/vendor/github.com/cilium/cilium/api/v1/relay/relay.pb.go index 3af74c961a..c232915ef0 100644 --- a/vendor/github.com/cilium/cilium/api/v1/relay/relay.pb.go +++ b/vendor/github.com/cilium/cilium/api/v1/relay/relay.pb.go @@ -4,7 +4,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.36.5 -// protoc v5.29.3 +// protoc v6.30.1 // source: relay/relay.proto package relay diff --git a/vendor/github.com/cilium/cilium/assets.go b/vendor/github.com/cilium/cilium/assets.go deleted file mode 100644 index cca1fcf7bf..0000000000 --- a/vendor/github.com/cilium/cilium/assets.go +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -// Cilium provides access to top-level files in the tree for Cilium development. -package cilium - -import ( - _ "embed" -) - -//go:embed CODEOWNERS -var CodeOwnersRaw string diff --git a/vendor/github.com/cilium/cilium/cilium-cli/cli/connectivity.go b/vendor/github.com/cilium/cilium/cilium-cli/cli/connectivity.go index 4fa7caeb36..6fbae86479 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/cli/connectivity.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/cli/connectivity.go @@ -17,12 +17,12 @@ import ( "github.com/spf13/cobra" "github.com/spf13/pflag" - assets "github.com/cilium/cilium" "github.com/cilium/cilium/cilium-cli/api" "github.com/cilium/cilium/cilium-cli/connectivity" "github.com/cilium/cilium/cilium-cli/connectivity/check" "github.com/cilium/cilium/cilium-cli/defaults" "github.com/cilium/cilium/cilium-cli/sysdump" + owners_util "github.com/cilium/cilium/cilium-cli/utils/codeowners" "github.com/cilium/cilium/cilium-cli/utils/features" "github.com/cilium/cilium/pkg/option" ) @@ -44,6 +44,8 @@ var params = check.Parameters{ ExternalDeploymentPort: 8080, EchoServerHostPort: 4000, JunitProperties: make(map[string]string), + NamespaceLabels: make(map[string]string), + NamespaceAnnotations: make(map[string]string), NodeSelector: make(map[string]string), Writer: os.Stdout, SysdumpOptions: sysdump.Options{ @@ -95,12 +97,17 @@ func RunE(hooks api.Hooks) func(cmd *cobra.Command, args []string) error { return nil } - logger := check.NewConcurrentLogger(params.Writer, params.TestConcurrency) - owners, err := codeowners.ParseFile(strings.NewReader(assets.CodeOwnersRaw)) - if err != nil { - return fmt.Errorf("🐛 Failed to parse CODEOWNERS. Developer BUG? %w", err) + var owners codeowners.Ruleset + if params.LogCodeOwners { + var err error + + owners, err = owners_util.Load(params.CodeOwners) + if err != nil { + return fmt.Errorf("❗ Failed to load code owners: %w", err) + } } + logger := check.NewConcurrentLogger(params.Writer) connTests, err := newConnectivityTests(params, hooks, logger, owners) if err != nil { return err @@ -143,9 +150,6 @@ func newCmdConnectivityTest(hooks api.Hooks) *cobra.Command { cmd.Flags().StringVar(¶ms.AgentPodSelector, "agent-pod-selector", defaults.AgentPodSelector, "Label on cilium-agent pods to select with") cmd.Flags().StringVar(¶ms.CiliumPodSelector, "cilium-pod-selector", defaults.CiliumPodSelector, "Label selector matching all cilium-related pods") cmd.Flags().Var(option.NewNamedMapOptions("node-selector", ¶ms.NodeSelector, nil), "node-selector", "Restrict connectivity pods to nodes matching this label") - cmd.Flags().Var(¶ms.NamespaceAnnotations, "namespace-annotations", "Add annotations to the connectivity test namespace, e.g. '{\"foo\":\"bar\"}'") - cmd.Flags().MarkHidden("namespace-annotations") - cmd.Flags().MarkHidden("deployment-pod-annotations") cmd.Flags().StringVar(¶ms.MultiCluster, "multi-cluster", "", "Test across clusters to given context") cmd.Flags().StringSliceVar(&tests, "test", []string{}, "Run tests that match one of the given regular expressions, skip tests by starting the expression with '!', target Scenarios with e.g. '/pod-to-cidr'") cmd.Flags().StringVar(¶ms.FlowValidation, "flow-validation", check.FlowValidationModeWarning, "Enable Hubble flow validation { disabled | warning | strict }") @@ -155,7 +159,7 @@ func newCmdConnectivityTest(hooks api.Hooks) *cobra.Command { cmd.Flags().BoolVarP(¶ms.Timestamp, "timestamp", "t", false, "Show timestamp in messages") cmd.Flags().BoolVarP(¶ms.PauseOnFail, "pause-on-fail", "p", false, "Pause execution on test failure") cmd.Flags().StringVar(¶ms.ExternalTarget, "external-target", "one.one.one.one.", "Domain name to use as external target in connectivity tests") - cmd.Flags().StringVar(¶ms.ExternalOtherTarget, "external-other-target", "cilium.io.", "Domain name to use as a second external target in connectivity tests") + cmd.Flags().StringVar(¶ms.ExternalOtherTarget, "external-other-target", "k8s.io.", "Domain name to use as a second external target in connectivity tests") cmd.Flags().StringVar(¶ms.ExternalTargetCANamespace, "external-target-ca-namespace", "", "Namespace of the CA secret for the external target. Used by client-egress-l7-tls test cases.") cmd.Flags().StringVar(¶ms.ExternalTargetCAName, "external-target-ca-name", "cabundle", "Name of the CA secret for the external target. Used by client-egress-l7-tls test cases.") cmd.Flags().StringVar(¶ms.ExternalCIDR, "external-cidr", "1.0.0.0/8", "CIDR to use as external target in connectivity tests") @@ -197,6 +201,7 @@ func newCmdConnectivityTest(hooks api.Hooks) *cobra.Command { cmd.Flags().BoolVar(¶ms.IncludeConnDisruptTest, "include-conn-disrupt-test", false, "Include conn disrupt test") cmd.Flags().BoolVar(¶ms.IncludeConnDisruptTestNSTraffic, "include-conn-disrupt-test-ns-traffic", false, "Include conn disrupt test for NS traffic") + cmd.Flags().BoolVar(¶ms.IncludeConnDisruptTestEgressGateway, "include-conn-disrupt-test-egw", false, "Include conn disrupt test for Egress Gateway") cmd.Flags().BoolVar(¶ms.ConnDisruptTestSetup, "conn-disrupt-test-setup", false, "Set up conn disrupt test dependencies") cmd.Flags().StringVar(¶ms.ConnDisruptTestRestartsPath, "conn-disrupt-test-restarts-path", "/tmp/cilium-conn-disrupt-restarts", "Conn disrupt test temporary result file (used internally)") cmd.Flags().StringVar(¶ms.ConnDisruptTestXfrmErrorsPath, "conn-disrupt-test-xfrm-errors-path", "/tmp/cilium-conn-disrupt-xfrm-errors", "Conn disrupt test temporary result file (used internally)") @@ -207,6 +212,8 @@ func newCmdConnectivityTest(hooks api.Hooks) *cobra.Command { cmd.Flags().StringSliceVar(¶ms.ExpectedXFRMErrors, "expected-xfrm-errors", defaults.ExpectedXFRMErrors, "List of expected XFRM errors") cmd.Flags().MarkHidden("expected-xfrm-errors") + cmd.Flags().StringSliceVar(¶ms.CodeOwners, "code-owners", []string{}, "Use the code owners defined in these files for --log-code-owners") + cmd.Flags().MarkHidden("code-owners") cmd.Flags().BoolVar(¶ms.LogCodeOwners, "log-code-owners", defaults.LogCodeOwners, "Log code owners for tests that fail") cmd.Flags().MarkHidden("log-code-owners") cmd.Flags().StringSliceVar(¶ms.ExcludeCodeOwners, "exclude-code-owners", []string{}, "Exclude specific code owners from --log-code-owners") @@ -254,7 +261,9 @@ func newCmdConnectivityPerf(hooks api.Hooks) *cobra.Command { cmd.Flags().BoolVar(¶ms.PerfParameters.RR, "rr", true, "Run RR test") cmd.Flags().BoolVar(¶ms.PerfParameters.UDP, "udp", false, "Run UDP tests") cmd.Flags().BoolVar(¶ms.PerfParameters.Throughput, "throughput", true, "Run throughput test") + cmd.Flags().BoolVar(¶ms.PerfParameters.ThroughputMulti, "throughput-multi", true, "Run throughput test with multiple streams") cmd.Flags().IntVar(¶ms.PerfParameters.Samples, "samples", 1, "Number of Performance samples to capture (how many times to run each test)") + cmd.Flags().UintVar(¶ms.PerfParameters.Streams, "streams", 4, "The parallelism of tests with multiple streams") cmd.Flags().BoolVar(¶ms.PerfParameters.HostNet, "host-net", true, "Test host network") cmd.Flags().BoolVar(¶ms.PerfParameters.PodNet, "pod-net", true, "Test pod network") cmd.Flags().BoolVar(¶ms.PerfParameters.PodToHost, "pod-to-host", false, "Test pod-to-host traffic") @@ -279,7 +288,11 @@ func newCmdConnectivityPerf(hooks api.Hooks) *cobra.Command { func registerCommonFlags(flags *pflag.FlagSet) { flags.BoolVarP(¶ms.Debug, "debug", "d", false, "Show debug messages") flags.StringVar(¶ms.TestNamespace, "test-namespace", defaults.ConnectivityCheckNamespace, "Namespace to perform the connectivity in (always suffixed with a sequence number to be compliant with test-concurrency param, e.g.: cilium-test-1)") + flags.Var(option.NewNamedMapOptions("namespace-labels", ¶ms.NamespaceLabels, nil), "namespace-labels", "Add labels to the connectivity test namespace") + flags.Var(option.NewNamedMapOptions("namespace-annotations", ¶ms.NamespaceAnnotations, nil), "namespace-annotations", "Add annotations to the connectivity test namespace") + flags.MarkHidden("namespace-annotations") flags.Var(¶ms.DeploymentAnnotations, "deployment-pod-annotations", "Add annotations to the connectivity pods, e.g. '{\"client\":{\"foo\":\"bar\"}}'") + flags.MarkHidden("deployment-pod-annotations") flags.BoolVar(¶ms.PrintImageArtifacts, "print-image-artifacts", false, "Prints the used image artifacts") } diff --git a/vendor/github.com/cilium/cilium/cilium-cli/clustermesh/clustermesh.go b/vendor/github.com/cilium/cilium/cilium-cli/clustermesh/clustermesh.go index d6e84b2e38..ed8880e3e5 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/clustermesh/clustermesh.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/clustermesh/clustermesh.go @@ -1319,10 +1319,8 @@ func (k *K8sClusterMesh) checkConnectionMode() error { defaults.ClusterMeshConnectionModeUnicast, } - for _, mode := range validModes { - if k.params.ConnectionMode == mode { - return nil - } + if slices.Contains(validModes, k.params.ConnectionMode) { + return nil } k.Log("❌ %s is not a correct connection mode.", k.params.ConnectionMode) @@ -1497,7 +1495,7 @@ func (k *K8sClusterMesh) retrieveRemoteHelmValues(ctx context.Context, remoteCli } func removeStringFromSlice(name string, names []string) []string { - namesCopy := append([]string{}, names...) + namesCopy := slices.Clone(names) namesCopy = slices.DeleteFunc(namesCopy, func(n string) bool { return n == name }) diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/builder.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/builder.go index 261fdeff24..eb113e0646 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/builder.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/builder.go @@ -78,6 +78,9 @@ var ( //go:embed manifests/client-egress-l7-tls-sni.yaml clientEgressL7TLSSNIPolicyYAML string + //go:embed manifests/client-egress-l7-tls-other-sni.yaml + clientEgressL7TLSOtherSNIPolicyYAML string + //go:embed manifests/client-egress-l7-tls.yaml clientEgressL7TLSPolicyYAML string @@ -262,14 +265,7 @@ func concurrentTests(connTests []*check.ConnectivityTest) error { echoIngressAuthAlwaysFail{}, echoIngressMutualAuthSpiffe{}, podToIngressService{}, - podToIngressServiceDenyAll{}, - podToIngressServiceDenyIngressIdentity{}, - podToIngressServiceDenyBackendService{}, - podToIngressServiceAllowIngressIdentity{}, outsideToIngressService{}, - outsideToIngressServiceDenyWorldIdentity{}, - outsideToIngressServiceDenyCidr{}, - outsideToIngressServiceDenyAllIngress{}, dnsOnly{}, toFqdns{}, podToControlplaneHost{}, @@ -317,6 +313,7 @@ func renderTemplates(clusterName string, param check.Parameters) (map[string]str "clientEgressTLSSNIPolicyYAML": clientEgressTLSSNIPolicyYAML, "clientEgressTLSSNIOtherPolicyYAML": clientEgressTLSSNIOtherPolicyYAML, "clientEgressL7TLSSNIPolicyYAML": clientEgressL7TLSSNIPolicyYAML, + "clientEgressL7TLSOtherSNIPolicyYAML": clientEgressL7TLSOtherSNIPolicyYAML, "clientEgressL7TLSPolicyYAML": clientEgressL7TLSPolicyYAML, "clientEgressL7TLSPolicyPortRangeYAML": clientEgressL7TLSPolicyPortRangeYAML, "clientEgressL7HTTPMatchheaderSecretYAML": clientEgressL7HTTPMatchheaderSecretYAML, diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/client_egress_tls_sni.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/client_egress_tls_sni.go index 55ec4379e5..eaa9cc6545 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/client_egress_tls_sni.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/client_egress_tls_sni.go @@ -67,4 +67,25 @@ func clientEgressL7TlsSniTest(ct *check.ConnectivityTest, templates map[string]s WithExpectations(func(a *check.Action) (egress, ingress check.Result) { return check.ResultOK, check.ResultNone }) + + // This test is similar to the previous one, but with a different SNI. + // So the expectation is curl ssl error (e.g. exit code 35) instead. + testName = "client-egress-l7-tls-headers-other-sni" + yamlFile = templates["clientEgressL7TLSOtherSNIPolicyYAML"] + newTest(testName, ct). + WithCiliumVersion("!1.14.15 !1.14.16 !1.15.9 !1.15.10 !1.16.2 !1.16.3"). + WithFeatureRequirements(features.RequireEnabled(features.L7Proxy)). + WithFeatureRequirements(features.RequireEnabled(features.PolicySecretsOnlyFromSecretsNamespace)). + WithCABundleSecret(). + WithCertificate("externaltarget-tls", ct.Params().ExternalTarget). + WithCiliumPolicy(yamlFile). // L7 allow policy TLS SNI enforcement + WithCiliumPolicy(templates["clientEgressOnlyDNSPolicyYAML"]). // DNS resolution only + WithScenarios(tests.PodToWorldWithTLSIntercept("-H", "X-Very-Secret-Token: 42")). + WithExpectations(func(a *check.Action) (egress, ingress check.Result) { + if a.Destination().Port() == 443 { + // SSL error as another external target (e.g. cilium.io) SNI is not allowed + return check.ResultCurlSSLError, check.ResultNone + } + return check.ResultDefaultDenyEgressDrop, check.ResultNone + }) } diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/manifests/client-egress-l7-tls-other-sni.yaml b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/manifests/client-egress-l7-tls-other-sni.yaml new file mode 100644 index 0000000000..46493a8650 --- /dev/null +++ b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/manifests/client-egress-l7-tls-other-sni.yaml @@ -0,0 +1,31 @@ +apiVersion: "cilium.io/v2" +kind: CiliumNetworkPolicy +metadata: + name: "client-egress-l7-tls-other-sni" +specs: +- description: "L7 policy with TLS" + endpointSelector: + matchLabels: + kind: client + egress: + # Allow HTTPS when X-Very-Secret-Token is set + - toPorts: + - ports: + - port: "443" + protocol: "TCP" + serverNames: + - "{{trimSuffix .ExternalOtherTarget "."}}" + terminatingTLS: + secret: + namespace: "{{.TestNamespace}}" + name: externaltarget-tls # internal certificate to terminate in cluster + originatingTLS: + secret: + namespace: "{{.ExternalTargetCANamespace}}" + name: "{{.ExternalTargetCAName}}" # public CA bundle to validate external target + rules: + http: + - method: "GET" + path: "/" + headers: + - "X-Very-Secret-Token: 42" diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/manifests/deny-ingress-source-egress-other-node.yaml b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/manifests/deny-ingress-source-egress-other-node.yaml new file mode 100644 index 0000000000..8d0114478e --- /dev/null +++ b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/manifests/deny-ingress-source-egress-other-node.yaml @@ -0,0 +1,12 @@ +apiVersion: "cilium.io/v2" +kind: CiliumNetworkPolicy +metadata: + name: "ingress-source-egress-deny-other-node" +spec: + endpointSelector: + matchLabels: + kind: client + egress: + - toEndpoints: + - matchLabels: + name: echo-same-node diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/outside_to_ingress_service.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/outside_to_ingress_service.go index a580dcb1b3..1bea3c0496 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/outside_to_ingress_service.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/outside_to_ingress_service.go @@ -4,17 +4,55 @@ package builder import ( + _ "embed" + "github.com/cilium/cilium/cilium-cli/connectivity/check" "github.com/cilium/cilium/cilium-cli/connectivity/tests" "github.com/cilium/cilium/cilium-cli/utils/features" ) +//go:embed manifests/deny-world-entity.yaml +var denyWorldIdentityPolicyYAML string + type outsideToIngressService struct{} -func (t outsideToIngressService) build(ct *check.ConnectivityTest, _ map[string]string) { +func (t outsideToIngressService) build(ct *check.ConnectivityTest, templates map[string]string) { newTest("outside-to-ingress-service", ct). WithFeatureRequirements( features.RequireEnabled(features.IngressController), features.RequireEnabled(features.NodeWithoutCilium)). WithScenarios(tests.OutsideToIngressService()) + + newTest("outside-to-ingress-service-deny-all-ingress", ct). + WithFeatureRequirements( + features.RequireEnabled(features.IngressController), + features.RequireEnabled(features.NodeWithoutCilium), + ). + WithCiliumPolicy(denyAllIngressPolicyYAML). + WithScenarios(tests.OutsideToIngressService()). + WithExpectations(func(_ *check.Action) (egress check.Result, ingress check.Result) { + return check.ResultDefaultDenyEgressDrop, check.ResultNone + }) + + newTest("outside-to-ingress-service-deny-cidr", ct). + WithFeatureRequirements( + features.RequireEnabled(features.IngressController), + features.RequireEnabled(features.NodeWithoutCilium), + ). + WithCiliumPolicy(templates["denyCIDRPolicyYAML"]). + WithScenarios(tests.OutsideToIngressService()). + WithExpectations(func(_ *check.Action) (egress check.Result, ingress check.Result) { + return check.ResultDefaultDenyEgressDrop, check.ResultNone + }) + + newTest("outside-to-ingress-service-deny-world-identity", ct). + WithFeatureRequirements( + features.RequireEnabled(features.IngressController), + features.RequireEnabled(features.NodeWithoutCilium), + ). + WithCiliumPolicy(denyWorldIdentityPolicyYAML). + WithScenarios(tests.OutsideToIngressService()). + WithExpectations(func(_ *check.Action) (egress check.Result, ingress check.Result) { + return check.ResultDefaultDenyEgressDrop, check.ResultNone + }) } diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/outside_to_ingress_service_deny_all_ingress.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/outside_to_ingress_service_deny_all_ingress.go deleted file mode 100644 index a6c8e28b6a..0000000000 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/outside_to_ingress_service_deny_all_ingress.go +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package builder - -import ( - "github.com/cilium/cilium/cilium-cli/connectivity/check" - "github.com/cilium/cilium/cilium-cli/connectivity/tests" - "github.com/cilium/cilium/cilium-cli/utils/features" -) - -type outsideToIngressServiceDenyAllIngress struct{} - -func (t outsideToIngressServiceDenyAllIngress) build(ct *check.ConnectivityTest, _ map[string]string) { - newTest("outside-to-ingress-service-deny-all-ingress", ct). - WithFeatureRequirements( - features.RequireEnabled(features.IngressController), - features.RequireEnabled(features.NodeWithoutCilium), - ). - WithCiliumPolicy(denyAllIngressPolicyYAML). - WithScenarios(tests.OutsideToIngressService()). - WithExpectations(func(_ *check.Action) (egress check.Result, ingress check.Result) { - return check.ResultDefaultDenyEgressDrop, check.ResultNone - }) -} diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/outside_to_ingress_service_deny_cidr.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/outside_to_ingress_service_deny_cidr.go deleted file mode 100644 index ac3f930d3e..0000000000 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/outside_to_ingress_service_deny_cidr.go +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package builder - -import ( - "github.com/cilium/cilium/cilium-cli/connectivity/check" - "github.com/cilium/cilium/cilium-cli/connectivity/tests" - "github.com/cilium/cilium/cilium-cli/utils/features" -) - -type outsideToIngressServiceDenyCidr struct{} - -func (t outsideToIngressServiceDenyCidr) build(ct *check.ConnectivityTest, templates map[string]string) { - newTest("outside-to-ingress-service-deny-cidr", ct). - WithFeatureRequirements( - features.RequireEnabled(features.IngressController), - features.RequireEnabled(features.NodeWithoutCilium), - ). - WithCiliumPolicy(templates["denyCIDRPolicyYAML"]). - WithScenarios(tests.OutsideToIngressService()). - WithExpectations(func(_ *check.Action) (egress check.Result, ingress check.Result) { - return check.ResultDefaultDenyEgressDrop, check.ResultNone - }) -} diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/outside_to_ingress_service_deny_world_identity.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/outside_to_ingress_service_deny_world_identity.go deleted file mode 100644 index 8fe39ee45c..0000000000 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/outside_to_ingress_service_deny_world_identity.go +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package builder - -import ( - _ "embed" - - "github.com/cilium/cilium/cilium-cli/connectivity/check" - "github.com/cilium/cilium/cilium-cli/connectivity/tests" - "github.com/cilium/cilium/cilium-cli/utils/features" -) - -//go:embed manifests/deny-world-entity.yaml -var denyWorldIdentityPolicyYAML string - -type outsideToIngressServiceDenyWorldIdentity struct{} - -func (t outsideToIngressServiceDenyWorldIdentity) build(ct *check.ConnectivityTest, _ map[string]string) { - newTest("outside-to-ingress-service-deny-world-identity", ct). - WithFeatureRequirements( - features.RequireEnabled(features.IngressController), - features.RequireEnabled(features.NodeWithoutCilium), - ). - WithCiliumPolicy(denyWorldIdentityPolicyYAML). - WithScenarios(tests.OutsideToIngressService()). - WithExpectations(func(_ *check.Action) (egress check.Result, ingress check.Result) { - return check.ResultDefaultDenyEgressDrop, check.ResultNone - }) -} diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/pod_to_ingress_service.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/pod_to_ingress_service.go index 36d2857942..219c3589d1 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/pod_to_ingress_service.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/pod_to_ingress_service.go @@ -4,16 +4,76 @@ package builder import ( + _ "embed" + "strings" + "github.com/cilium/cilium/cilium-cli/connectivity/check" "github.com/cilium/cilium/cilium-cli/connectivity/tests" "github.com/cilium/cilium/cilium-cli/utils/features" ) +var ( + //go:embed manifests/allow-ingress-identity.yaml + allowIngressIdentityPolicyYAML string + + //go:embed manifests/deny-ingress-backend.yaml + denyIngressBackendPolicyYAML string + + //go:embed manifests/deny-ingress-entity.yaml + denyIngressIdentityPolicyYAML string + + //go:embed manifests/deny-ingress-source-egress-other-node.yaml + denyIngressSourceEgressOtherNodePolicyYML string +) + type podToIngressService struct{} -func (t podToIngressService) build(ct *check.ConnectivityTest, _ map[string]string) { +func (t podToIngressService) build(ct *check.ConnectivityTest, templates map[string]string) { // Test Ingress controller newTest("pod-to-ingress-service", ct). WithFeatureRequirements(features.RequireEnabled(features.IngressController)). WithScenarios(tests.PodToIngress()) + + newTest("pod-to-ingress-service-allow-ingress-identity", ct). + WithFeatureRequirements(features.RequireEnabled(features.IngressController)). + WithCiliumPolicy(denyAllIngressPolicyYAML). + WithCiliumPolicy(allowIngressIdentityPolicyYAML). + WithScenarios(tests.PodToIngress()) + + newTest("pod-to-ingress-service-deny-all", ct). + WithFeatureRequirements(features.RequireEnabled(features.IngressController)). + WithCiliumPolicy(denyAllIngressPolicyYAML). + WithScenarios(tests.PodToIngress()). + WithExpectations(func(_ *check.Action) (egress check.Result, ingress check.Result) { + return check.ResultDefaultDenyEgressDrop, check.ResultNone + }) + + newTest("pod-to-ingress-service-deny-backend-service", ct). + WithFeatureRequirements(features.RequireEnabled(features.IngressController)). + WithCiliumPolicy(denyIngressBackendPolicyYAML). + WithScenarios(tests.PodToIngress()). + WithExpectations(func(_ *check.Action) (egress check.Result, ingress check.Result) { + return check.ResultDefaultDenyEgressDrop, check.ResultNone + }) + + newTest("pod-to-ingress-service-deny-ingress-identity", ct). + WithFeatureRequirements(features.RequireEnabled(features.IngressController)). + WithCiliumPolicy(denyIngressIdentityPolicyYAML). + WithScenarios(tests.PodToIngress()). + WithExpectations(func(_ *check.Action) (egress check.Result, ingress check.Result) { + return check.ResultDefaultDenyEgressDrop, check.ResultNone + }) + + newTest("pod-to-ingress-service-deny-source-egress-other-node", ct). + WithCiliumVersion(">1.17.1 >1.16.7 >1.15.14"). + WithFeatureRequirements(features.RequireEnabled(features.IngressController)). + WithCiliumPolicy(denyIngressSourceEgressOtherNodePolicyYML). + WithCiliumPolicy(templates["clientEgressOnlyDNSPolicyYAML"]). // DNS resolution only + WithScenarios(tests.PodToIngress()). + WithExpectations(func(a *check.Action) (egress check.Result, ingress check.Result) { + if strings.Contains(a.Destination().Name(), "cilium-ingress-same-node") { + return check.ResultOK, check.ResultOK + } + return check.ResultDefaultDenyEgressDrop, check.ResultNone + }) } diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/pod_to_ingress_service_allow_ingress_identity.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/pod_to_ingress_service_allow_ingress_identity.go deleted file mode 100644 index 6ebc506b1c..0000000000 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/pod_to_ingress_service_allow_ingress_identity.go +++ /dev/null @@ -1,25 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package builder - -import ( - _ "embed" - - "github.com/cilium/cilium/cilium-cli/connectivity/check" - "github.com/cilium/cilium/cilium-cli/connectivity/tests" - "github.com/cilium/cilium/cilium-cli/utils/features" -) - -//go:embed manifests/allow-ingress-identity.yaml -var allowIngressIdentityPolicyYAML string - -type podToIngressServiceAllowIngressIdentity struct{} - -func (t podToIngressServiceAllowIngressIdentity) build(ct *check.ConnectivityTest, _ map[string]string) { - newTest("pod-to-ingress-service-allow-ingress-identity", ct). - WithFeatureRequirements(features.RequireEnabled(features.IngressController)). - WithCiliumPolicy(denyAllIngressPolicyYAML). - WithCiliumPolicy(allowIngressIdentityPolicyYAML). - WithScenarios(tests.PodToIngress()) -} diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/pod_to_ingress_service_deny_all.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/pod_to_ingress_service_deny_all.go deleted file mode 100644 index dcc27df22f..0000000000 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/pod_to_ingress_service_deny_all.go +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package builder - -import ( - "github.com/cilium/cilium/cilium-cli/connectivity/check" - "github.com/cilium/cilium/cilium-cli/connectivity/tests" - "github.com/cilium/cilium/cilium-cli/utils/features" -) - -type podToIngressServiceDenyAll struct{} - -func (t podToIngressServiceDenyAll) build(ct *check.ConnectivityTest, _ map[string]string) { - newTest("pod-to-ingress-service-deny-all", ct). - WithFeatureRequirements(features.RequireEnabled(features.IngressController)). - WithCiliumPolicy(denyAllIngressPolicyYAML). - WithScenarios(tests.PodToIngress()). - WithExpectations(func(_ *check.Action) (egress check.Result, ingress check.Result) { - return check.ResultDefaultDenyEgressDrop, check.ResultNone - }) -} diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/pod_to_ingress_service_deny_backend_service.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/pod_to_ingress_service_deny_backend_service.go deleted file mode 100644 index 42ee591c9a..0000000000 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/pod_to_ingress_service_deny_backend_service.go +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package builder - -import ( - _ "embed" - - "github.com/cilium/cilium/cilium-cli/connectivity/check" - "github.com/cilium/cilium/cilium-cli/connectivity/tests" - "github.com/cilium/cilium/cilium-cli/utils/features" -) - -//go:embed manifests/deny-ingress-backend.yaml -var denyIngressBackendPolicyYAML string - -type podToIngressServiceDenyBackendService struct{} - -func (t podToIngressServiceDenyBackendService) build(ct *check.ConnectivityTest, _ map[string]string) { - newTest("pod-to-ingress-service-deny-backend-service", ct). - WithFeatureRequirements(features.RequireEnabled(features.IngressController)). - WithCiliumPolicy(denyIngressBackendPolicyYAML). - WithScenarios(tests.PodToIngress()). - WithExpectations(func(_ *check.Action) (egress check.Result, ingress check.Result) { - return check.ResultDefaultDenyEgressDrop, check.ResultNone - }) -} diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/pod_to_ingress_service_deny_ingress_identity.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/pod_to_ingress_service_deny_ingress_identity.go deleted file mode 100644 index 8ecad45034..0000000000 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/builder/pod_to_ingress_service_deny_ingress_identity.go +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package builder - -import ( - _ "embed" - - "github.com/cilium/cilium/cilium-cli/connectivity/check" - "github.com/cilium/cilium/cilium-cli/connectivity/tests" - "github.com/cilium/cilium/cilium-cli/utils/features" -) - -//go:embed manifests/deny-ingress-entity.yaml -var denyIngressIdentityPolicyYAML string - -type podToIngressServiceDenyIngressIdentity struct{} - -func (t podToIngressServiceDenyIngressIdentity) build(ct *check.ConnectivityTest, _ map[string]string) { - newTest("pod-to-ingress-service-deny-ingress-identity", ct). - WithFeatureRequirements(features.RequireEnabled(features.IngressController)). - WithCiliumPolicy(denyIngressIdentityPolicyYAML). - WithScenarios(tests.PodToIngress()). - WithExpectations(func(_ *check.Action) (egress check.Result, ingress check.Result) { - return check.ResultDefaultDenyEgressDrop, check.ResultNone - }) -} diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/check.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/check.go index a366ba0e45..c676c3f0e0 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/check.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/check.go @@ -7,6 +7,7 @@ import ( "encoding/json" "fmt" "io" + "maps" "regexp" "strings" "time" @@ -21,23 +22,25 @@ import ( ) type PerfParameters struct { - ReportDir string - Duration time.Duration - SetupDelay time.Duration - HostNet bool - PodNet bool - PodToHost bool - HostToPod bool - SameNode bool - OtherNode bool - Samples int - MessageSize int - Throughput bool - CRR bool - RR bool - UDP bool - Image string - NetQos bool + ReportDir string + Duration time.Duration + SetupDelay time.Duration + HostNet bool + PodNet bool + PodToHost bool + HostToPod bool + SameNode bool + OtherNode bool + Samples int + MessageSize int + Streams uint + Throughput bool + ThroughputMulti bool + CRR bool + RR bool + UDP bool + Image string + NetQos bool NodeSelectorServer map[string]string NodeSelectorClient map[string]string @@ -91,7 +94,8 @@ type Parameters struct { CiliumPodSelector string NodeSelector map[string]string DeploymentAnnotations annotationsMap - NamespaceAnnotations annotations + NamespaceLabels map[string]string + NamespaceAnnotations map[string]string ExternalTarget string ExternalOtherTarget string ExternalCIDR string @@ -111,16 +115,18 @@ type Parameters struct { ImpersonateGroups []string IPFamilies []string - IncludeConnDisruptTest bool - IncludeConnDisruptTestNSTraffic bool - ConnDisruptTestSetup bool - ConnDisruptTestRestartsPath string - ConnDisruptTestXfrmErrorsPath string - ConnDisruptDispatchInterval time.Duration + IncludeConnDisruptTest bool + IncludeConnDisruptTestNSTraffic bool + IncludeConnDisruptTestEgressGateway bool + ConnDisruptTestSetup bool + ConnDisruptTestRestartsPath string + ConnDisruptTestXfrmErrorsPath string + ConnDisruptDispatchInterval time.Duration ExpectedDropReasons []string ExpectedXFRMErrors []string + CodeOwners []string LogCodeOwners bool ExcludeCodeOwners []string LogCheckLevels []string @@ -308,9 +314,7 @@ func (r *FlowRequirementResults) Merge(from *FlowRequirementResults) { if r.Matched == nil { r.Matched = from.Matched } else { - for k, v := range from.Matched { - r.Matched[k] = v - } + maps.Copy(r.Matched, from.Matched) } r.Failures += from.Failures r.NeedMoreFlows = r.NeedMoreFlows || from.NeedMoreFlows diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/context.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/context.go index 367b2b2086..1219b3a512 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/context.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/context.go @@ -303,6 +303,21 @@ func (ct *ConnectivityTest) SetupAndValidate(ctx context.Context, extra SetupHoo if err := ctx.Err(); err != nil { return err } + + setupAndValidate := ct.setupAndValidate + if ct.Params().Perf { + setupAndValidate = ct.setupAndValidatePerf + } + + if err := setupAndValidate(ctx, extra); err != nil { + return err + } + + // Setup and validate all the extras coming from extended functionalities. + return extra.SetupAndValidate(ctx, ct) +} + +func (ct *ConnectivityTest) setupAndValidate(ctx context.Context, extra SetupHooks) error { if err := ct.detectSingleNode(ctx); err != nil { return err } @@ -375,8 +390,23 @@ func (ct *ConnectivityTest) SetupAndValidate(ctx context.Context, extra SetupHoo } } - // Setup and validate all the extras coming from extended functionalities. - return extra.SetupAndValidate(ctx, ct) + return nil +} + +func (ct *ConnectivityTest) setupAndValidatePerf(ctx context.Context, _ SetupHooks) error { + if err := ct.initClients(ctx); err != nil { + return err + } + + if err := ct.deployPerf(ctx); err != nil { + return err + } + + if err := ct.validateDeploymentPerf(ctx); err != nil { + return err + } + + return nil } // PrintTestInfo prints connectivity test names and count. @@ -561,12 +591,12 @@ func (ct *ConnectivityTest) report() error { } } ct.Logf("%s", strings.Repeat("-", 200)) - ct.Logf("%s", strings.Repeat("-", 85)) - ct.Logf("📋 %-15s | %-10s | %-15s | %-15s | %-15s ", "Scenario", "Node", "Test", "Duration", "Throughput Mb/s") - ct.Logf("%s", strings.Repeat("-", 85)) + ct.Logf("%s", strings.Repeat("-", 88)) + ct.Logf("📋 %-15s | %-10s | %-18s | %-15s | %-15s ", "Scenario", "Node", "Test", "Duration", "Throughput Mb/s") + ct.Logf("%s", strings.Repeat("-", 88)) for _, result := range ct.PerfResults { if result.Result.ThroughputMetric != nil { - ct.Logf("📋 %-15s | %-10s | %-15s | %-15s | %-12.2f ", + ct.Logf("📋 %-15s | %-10s | %-18s | %-15s | %-12.2f ", result.PerfTest.Scenario, nodeString(result.PerfTest.SameNode), result.PerfTest.Test, @@ -575,7 +605,7 @@ func (ct *ConnectivityTest) report() error { ) } } - ct.Logf("%s", strings.Repeat("-", 85)) + ct.Logf("%s", strings.Repeat("-", 88)) if ct.Params().PerfParameters.ReportDir != "" { common.ExportPerfSummaries(ct.PerfResults, ct.Params().PerfParameters.ReportDir) } @@ -1285,3 +1315,12 @@ func (ct *ConnectivityTest) ShouldRunConnDisruptNSTraffic() bool { !ct.Features[features.KPRNodePortAcceleration].Enabled && (!ct.Features[features.IPsecEnabled].Enabled || !ct.Features[features.KPRNodePort].Enabled) } + +func (ct *ConnectivityTest) ShouldRunConnDisruptEgressGateway() bool { + return ct.params.IncludeUnsafeTests && + ct.params.IncludeConnDisruptTestEgressGateway && + ct.Features[features.EgressGateway].Enabled && + ct.Features[features.NodeWithoutCilium].Enabled && + !ct.Features[features.KPRNodePortAcceleration].Enabled && + ct.params.MultiCluster == "" +} diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/deployment.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/deployment.go index 2039f3df74..0664026688 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/deployment.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/deployment.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "maps" + "net" "net/netip" "slices" "sort" @@ -26,6 +27,7 @@ import ( "github.com/cilium/cilium/cilium-cli/defaults" "github.com/cilium/cilium/cilium-cli/k8s" "github.com/cilium/cilium/cilium-cli/utils/features" + k8sconst "github.com/cilium/cilium/pkg/k8s/apis/cilium.io" ciliumv2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2" slimmetav1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1" policyapi "github.com/cilium/cilium/pkg/policy/api" @@ -71,17 +73,27 @@ const ( hostNetNSDeploymentNameNonCilium = "host-netns-non-cilium" // runs on non-Cilium test nodes kindHostNetNS = "host-netns" - testConnDisruptClientDeploymentName = "test-conn-disrupt-client" - testConnDisruptClientNSTrafficDeploymentName = "test-conn-disrupt-client" - testConnDisruptServerDeploymentName = "test-conn-disrupt-server" - testConnDisruptServerNSTrafficDeploymentName = "test-conn-disrupt-server-ns-traffic" - testConnDisruptServiceName = "test-conn-disrupt" - testConnDisruptNSTrafficServiceName = "test-conn-disrupt-ns-traffic" - testConnDisruptCNPName = "test-conn-disrupt" - testConnDisruptNSTrafficCNPName = "test-conn-disrupt-ns-traffic" - testConnDisruptServerNSTrafficAppLabel = "test-conn-disrupt-server-ns-traffic" - KindTestConnDisrupt = "test-conn-disrupt" - KindTestConnDisruptNSTraffic = "test-conn-disrupt-ns-traffic" + testConnDisruptClientDeploymentName = "test-conn-disrupt-client" + testConnDisruptClientNSTrafficDeploymentName = "test-conn-disrupt-client" + testConnDisruptClientEgressGatewayOnGatewayNodeDeploymentName = "test-conn-disrupt-client-egw-gw-node" + testConnDisruptClientEgressGatewayOnNonGatewayNodeDeploymentName = "test-conn-disrupt-client-egw-non-gw-node" + testConnDisruptServerDeploymentName = "test-conn-disrupt-server" + testConnDisruptServerNSTrafficDeploymentName = "test-conn-disrupt-server-ns-traffic" + testConnDisruptServerEgressGatewayDeploymentName = "test-conn-disrupt-server-egw" + testConnDisruptServiceName = "test-conn-disrupt" + testConnDisruptNSTrafficServiceName = "test-conn-disrupt-ns-traffic" + testConnDisruptEgressGatewayServiceName = "test-conn-disrupt-egw" + testConnDisruptCNPName = "test-conn-disrupt" + testConnDisruptNSTrafficCNPName = "test-conn-disrupt-ns-traffic" + testConnDisruptEgressGatewayCNPName = "test-conn-disrupt-egw" + testConnDisruptCEGPName = "test-conn-disrupt" + testConnDisruptServerNSTrafficAppLabel = "test-conn-disrupt-server-ns-traffic" + testConnDisruptServerEgressGatewayAppLabel = "test-conn-disrupt-server-egw" + testConnDisruptClientEgressGatewayOnGatewayNodeAppLabel = "test-conn-disrupt-client-egw-gw-node" + testConnDisruptClientEgressGatewayOnNonGatewayNodeAppLabel = "test-conn-disrupt-client-egw-non-gw-node" + KindTestConnDisrupt = "test-conn-disrupt" + KindTestConnDisruptNSTraffic = "test-conn-disrupt-ns-traffic" + KindTestConnDisruptEgressGateway = "test-conn-disrupt-egw" bwPrioAnnotationString = "bandwidth.cilium.io/priority" ) @@ -198,9 +210,7 @@ func newDeployment(p deploymentParameters) *appsv1.Deployment { }, } - for k, v := range p.Labels { - dep.Spec.Template.ObjectMeta.Labels[k] = v - } + maps.Copy(dep.Spec.Template.ObjectMeta.Labels, p.Labels) return dep } @@ -313,9 +323,7 @@ func newDaemonSet(p daemonSetParameters) *appsv1.DaemonSet { }, } - for k, v := range p.Labels { - ds.Spec.Template.ObjectMeta.Labels[k] = v - } + maps.Copy(ds.Spec.Template.ObjectMeta.Labels, p.Labels) if p.NodeSelector != nil { ds.Spec.Template.Spec.NodeSelector = p.NodeSelector @@ -490,6 +498,81 @@ func newConnDisruptCNPForNSTraffic(ns string) *ciliumv2.CiliumNetworkPolicy { } } +func newConnDisruptCNPForEgressGateway(ns string) *ciliumv2.CiliumNetworkPolicy { + selector := policyapi.EndpointSelector{ + LabelSelector: &slimmetav1.LabelSelector{ + MatchLabels: map[string]string{"kind": KindTestConnDisruptEgressGateway}, + }, + } + + ports := []policyapi.PortRule{{ + Ports: []policyapi.PortProtocol{{ + Protocol: policyapi.ProtoTCP, + Port: "8000", + }}, + }} + + return &ciliumv2.CiliumNetworkPolicy{ + TypeMeta: metav1.TypeMeta{ + Kind: ciliumv2.CNPKindDefinition, + APIVersion: ciliumv2.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{Name: testConnDisruptEgressGatewayCNPName, Namespace: ns}, + Spec: &policyapi.Rule{ + EndpointSelector: selector, + Egress: []policyapi.EgressRule{ + { + EgressCommonRule: policyapi.EgressCommonRule{ + ToEntities: policyapi.EntitySlice{ + policyapi.EntityWorld, + }, + }, + ToPorts: ports, + }, + { + ToPorts: []policyapi.PortRule{{ + Ports: []policyapi.PortProtocol{ + {Protocol: policyapi.ProtoUDP, Port: "53"}, + {Protocol: policyapi.ProtoUDP, Port: "5353"}, + }, + }}, + }, + }, + }, + } +} + +func newConnDisruptCEGP(ns, gwNode string) *ciliumv2.CiliumEgressGatewayPolicy { + return &ciliumv2.CiliumEgressGatewayPolicy{ + TypeMeta: metav1.TypeMeta{ + Kind: ciliumv2.CEGPKindDefinition, + APIVersion: ciliumv2.SchemeGroupVersion.String(), + }, + ObjectMeta: metav1.ObjectMeta{Name: testConnDisruptCEGPName}, + Spec: ciliumv2.CiliumEgressGatewayPolicySpec{ + Selectors: []ciliumv2.EgressRule{ + { + PodSelector: &slimmetav1.LabelSelector{ + MatchLabels: map[string]slimmetav1.MatchLabelsValue{ + k8sconst.PodNamespaceLabel: ns, + "kind": KindTestConnDisruptEgressGateway, + }, + }, + }, + }, + DestinationCIDRs: []ciliumv2.IPv4CIDR{"0.0.0.0/0"}, + ExcludedCIDRs: []ciliumv2.IPv4CIDR{}, + EgressGateway: &ciliumv2.EgressGateway{ + NodeSelector: &slimmetav1.LabelSelector{ + MatchLabels: map[string]slimmetav1.MatchLabelsValue{ + "kubernetes.io/hostname": gwNode, + }, + }, + }, + }, + } +} + func (ct *ConnectivityTest) ingresses() map[string]string { ingresses := map[string]string{"same-node": echoSameNodeDeploymentName} if !ct.Params().SingleNode || ct.Params().MultiCluster != "" { @@ -522,10 +605,8 @@ func (ct *ConnectivityTest) maybeNodeToNodeEncryptionAffinity() *corev1.NodeAffi } } -// deploy ensures the test Namespace, Services and Deployments are running on the cluster. -func (ct *ConnectivityTest) deploy(ctx context.Context) error { - var err error - +// deployNamespace sets up the test namespace. +func (ct *ConnectivityTest) deployNamespace(ctx context.Context) error { for _, client := range ct.Clients() { if ct.params.ForceDeploy { if err := ct.deleteDeployments(ctx, client); err != nil { @@ -543,7 +624,7 @@ func (ct *ConnectivityTest) deploy(ctx context.Context) error { ObjectMeta: metav1.ObjectMeta{ Name: ct.params.TestNamespace, Annotations: ct.params.NamespaceAnnotations, - Labels: appLabels, + Labels: labels.Merge(ct.params.NamespaceLabels, appLabels), }, } _, err = client.CreateNamespace(ctx, namespace, metav1.CreateOptions{}) @@ -553,29 +634,32 @@ func (ct *ConnectivityTest) deploy(ctx context.Context) error { } } - // Deploy perf actors (only in the first test namespace - // in case of tests concurrent run) - if ct.params.Perf && ct.params.TestNamespaceIndex == 0 { - return ct.deployPerf(ctx) + return nil +} + +// deploy ensures the test Namespace, Services and Deployments are running on the cluster. +func (ct *ConnectivityTest) deploy(ctx context.Context) error { + if err := ct.deployNamespace(ctx); err != nil { + return err } // Deploy test-conn-disrupt actors (only in the first // test namespace in case of tests concurrent run) if ct.params.ConnDisruptTestSetup && ct.params.TestNamespaceIndex == 0 { if err := ct.createTestConnDisruptServerDeployAndSvc(ctx, testConnDisruptServerDeploymentName, KindTestConnDisrupt, 3, - testConnDisruptServiceName, "test-conn-disrupt-server", newConnDisruptCNP); err != nil { + testConnDisruptServiceName, "test-conn-disrupt-server", false, newConnDisruptCNP); err != nil { return err } if err := ct.createTestConnDisruptClientDeployment(ctx, testConnDisruptClientDeploymentName, KindTestConnDisrupt, "test-conn-disrupt-client", fmt.Sprintf("test-conn-disrupt.%s.svc.cluster.local.:8000", ct.params.TestNamespace), - 5, false); err != nil { + 5, false, nil); err != nil { return err } if ct.ShouldRunConnDisruptNSTraffic() { if err := ct.createTestConnDisruptServerDeployAndSvc(ctx, testConnDisruptServerNSTrafficDeploymentName, KindTestConnDisruptNSTraffic, 1, - testConnDisruptNSTrafficServiceName, testConnDisruptServerNSTrafficAppLabel, newConnDisruptCNPForNSTraffic); err != nil { + testConnDisruptNSTrafficServiceName, testConnDisruptServerNSTrafficAppLabel, false, newConnDisruptCNPForNSTraffic); err != nil { return err } @@ -585,9 +669,54 @@ func (ct *ConnectivityTest) deploy(ctx context.Context) error { } else { ct.Info("Skipping conn-disrupt-test for NS traffic") } + + if ct.ShouldRunConnDisruptEgressGateway() { + gatewayNode, nonGatewayNode, err := ct.getGatewayAndNonGatewayNodes() + if err != nil { + return err + } + cegp := newConnDisruptCEGP(ct.params.TestNamespace, gatewayNode) + ct.Logf("✨ [%s] Deploying %s CiliumEgressGatewayPolicy...", ct.K8sClient().ClusterName(), cegp.Name) + _, err = ct.K8sClient().ApplyGeneric(ctx, cegp) + if err != nil { + return fmt.Errorf("unable to create CiliumEgressGatewayPolicy %s: %w", cegp.Name, err) + } + + if err := ct.createTestConnDisruptServerDeployAndSvc(ctx, testConnDisruptServerEgressGatewayDeploymentName, KindTestConnDisruptEgressGateway, 1, + testConnDisruptEgressGatewayServiceName, testConnDisruptServerEgressGatewayAppLabel, true, newConnDisruptCNPForEgressGateway); err != nil { + return err + } + + if err := ct.createTestConnDisruptClientDeployment(ctx, testConnDisruptClientEgressGatewayOnGatewayNodeDeploymentName, KindTestConnDisruptEgressGateway, + testConnDisruptClientEgressGatewayOnGatewayNodeAppLabel, fmt.Sprintf("test-conn-disrupt-egw.%s.svc.cluster.local.:8000", ct.params.TestNamespace), + 1, false, map[string]string{"kubernetes.io/hostname": gatewayNode}); err != nil { + return err + } + if err := ct.createTestConnDisruptClientDeployment(ctx, testConnDisruptClientEgressGatewayOnNonGatewayNodeDeploymentName, KindTestConnDisruptEgressGateway, + testConnDisruptClientEgressGatewayOnNonGatewayNodeAppLabel, fmt.Sprintf("test-conn-disrupt-egw.%s.svc.cluster.local.:8000", ct.params.TestNamespace), + 1, false, map[string]string{"kubernetes.io/hostname": nonGatewayNode}); err != nil { + return err + } + for _, clientDeploy := range []string{testConnDisruptClientEgressGatewayOnGatewayNodeDeploymentName, testConnDisruptClientEgressGatewayOnNonGatewayNodeDeploymentName} { + err := WaitForDeployment(ctx, ct, ct.clients.dst, ct.params.TestNamespace, clientDeploy) + if err != nil { + ct.Failf("%s deployment is not ready: %s", clientDeploy, err) + } + } + if err := WaitForEgressGatewayBpfPolicyEntries(ctx, ct.CiliumPods(), + func(ciliumPod Pod) ([]BPFEgressGatewayPolicyEntry, error) { + return ct.GetConnDisruptEgressPolicyEntries(ctx, ciliumPod) + }, func(ciliumPod Pod) ([]BPFEgressGatewayPolicyEntry, error) { + return nil, nil + }); err != nil { + ct.Fail(err) + } + } else { + ct.Info("Skipping conn-disrupt-test for Egress Gateway") + } } - _, err = ct.clients.src.GetService(ctx, ct.params.TestNamespace, echoSameNodeDeploymentName, metav1.GetOptions{}) + _, err := ct.clients.src.GetService(ctx, ct.params.TestNamespace, echoSameNodeDeploymentName, metav1.GetOptions{}) if err != nil { ct.Logf("✨ [%s] Deploying %s service...", ct.clients.src.ClusterName(), echoSameNodeDeploymentName) svc := newService(echoSameNodeDeploymentName, map[string]string{"name": echoSameNodeDeploymentName}, serviceLabels, "http", 8080, ct.Params().ServiceType) @@ -1136,7 +1265,7 @@ func (ct *ConnectivityTest) deploy(ctx context.Context) error { } func (ct *ConnectivityTest) createTestConnDisruptServerDeployAndSvc(ctx context.Context, deployName, kind string, replicas int, svcName, appLabel string, - cnpFunc func(ns string) *ciliumv2.CiliumNetworkPolicy) error { + isExternal bool, cnpFunc func(ns string) *ciliumv2.CiliumNetworkPolicy) error { _, err := ct.clients.src.GetDeployment(ctx, ct.params.TestNamespace, deployName, metav1.GetOptions{}) if err != nil { ct.Logf("✨ [%s] Deploying %s deployment...", ct.clients.src.ClusterName(), deployName) @@ -1150,7 +1279,7 @@ func (ct *ConnectivityTest) createTestConnDisruptServerDeployAndSvc(ctx context. InitialDelaySeconds: int32(1), FailureThreshold: int32(20), } - testConnDisruptServerDeployment := newDeployment(deploymentParameters{ + param := deploymentParameters{ Name: deployName, Kind: kind, Image: ct.params.TestConnDisruptImage, @@ -1162,7 +1291,15 @@ func (ct *ConnectivityTest) createTestConnDisruptServerDeployAndSvc(ctx context. Resources: corev1.ResourceRequirements{ Requests: corev1.ResourceList{corev1.ResourceCPU: *resource.NewMilliQuantity(100, resource.DecimalSI)}, }, - }) + } + if isExternal { + param.NodeSelector = map[string]string{defaults.CiliumNoScheduleLabel: "true"} + param.HostNetwork = true + param.Tolerations = []corev1.Toleration{ + {Operator: corev1.TolerationOpExists}, + } + } + testConnDisruptServerDeployment := newDeployment(param) _, err = ct.clients.src.CreateServiceAccount(ctx, ct.params.TestNamespace, k8s.NewServiceAccount(deployName), metav1.CreateOptions{}) if err != nil { return fmt.Errorf("unable to create service account %s: %w", deployName, err) @@ -1211,7 +1348,7 @@ func (ct *ConnectivityTest) createTestConnDisruptServerDeployAndSvc(ctx context. return err } -func (ct *ConnectivityTest) createTestConnDisruptClientDeployment(ctx context.Context, deployName, kind, appLabel, address string, replicas int, isExternal bool) error { +func (ct *ConnectivityTest) createTestConnDisruptClientDeployment(ctx context.Context, deployName, kind, appLabel, address string, replicas int, isExternal bool, nodeSelector map[string]string) error { _, err := ct.clients.dst.GetDeployment(ctx, ct.params.TestNamespace, deployName, metav1.GetOptions{}) if err != nil { ct.Logf("✨ [%s] Deploying %s deployment...", ct.clients.dst.ClusterName(), deployName) @@ -1249,6 +1386,9 @@ func (ct *ConnectivityTest) createTestConnDisruptClientDeployment(ctx context.Co {Operator: corev1.TolerationOpExists}, } } + if nodeSelector != nil { + param.NodeSelector = nodeSelector + } testConnDisruptClientDeployment := newDeployment(param) _, err = ct.clients.dst.CreateServiceAccount(ctx, ct.params.TestNamespace, k8s.NewServiceAccount(deployName), metav1.CreateOptions{}) @@ -1303,7 +1443,7 @@ func (ct *ConnectivityTest) createTestConnDisruptClientDeploymentForNSTraffic(ct KindTestConnDisruptNSTraffic, fmt.Sprintf("test-conn-disrupt-client-%s-%s-%s", n.nodeType, family, strings.ToLower(string(addr.Type))), netip.AddrPortFrom(netip.MustParseAddr(addr.Address), np).String(), - 1, true); err != nil { + 1, true, nil); err != nil { errs = errors.Join(errs, err) } ct.testConnDisruptClientNSTrafficDeploymentNames = append(ct.testConnDisruptClientNSTrafficDeploymentNames, deployName) @@ -1350,6 +1490,100 @@ func (ct *ConnectivityTest) getBackendNodeAndNonBackendNode(ctx context.Context) return nodes, err } +func (ct *ConnectivityTest) getGatewayAndNonGatewayNodes() (string, string, error) { + var workerNodes []string + for _, node := range ct.nodes { + if _, found := ct.controlPlaneNodes[node.Name]; !found { + workerNodes = append(workerNodes, node.Name) + } + } + if len(workerNodes) < 2 { + return "", "", fmt.Errorf("unable to pick gateway and non gateway nodes") + } + slices.Sort(workerNodes) + + return workerNodes[0], workerNodes[1], nil + +} + +func (ct *ConnectivityTest) GetGatewayNodeInternalIP(egressGatewayNode string) net.IP { + gatewayNode, ok := ct.Nodes()[egressGatewayNode] + if !ok { + return nil + } + + for _, addr := range gatewayNode.Status.Addresses { + if addr.Type != corev1.NodeInternalIP { + continue + } + + ip := net.ParseIP(addr.Address) + if ip == nil || ip.To4() == nil { + continue + } + + return ip + } + + return nil +} + +func (ct *ConnectivityTest) getConnDisruptClientEgressGatewayPodIPs(ctx context.Context) ([]string, error) { + var appLabels []string + appLabels = append(appLabels, fmt.Sprintf("app=%s", testConnDisruptClientEgressGatewayOnGatewayNodeAppLabel)) + appLabels = append(appLabels, fmt.Sprintf("app=%s", testConnDisruptClientEgressGatewayOnNonGatewayNodeAppLabel)) + + var podIPs []string + for _, appLabel := range appLabels { + connDisruptPods, err := ct.K8sClient().ListPods(ctx, ct.Params().TestNamespace, metav1.ListOptions{LabelSelector: appLabel}) + if err != nil { + return nil, fmt.Errorf("unable to list pods with lable %s: %w", appLabel, err) + } + + for _, connDisruptPod := range connDisruptPods.Items { + podIPs = append(podIPs, connDisruptPod.Status.PodIP) + } + } + + return podIPs, nil +} + +func (ct *ConnectivityTest) GetConnDisruptEgressPolicyEntries(ctx context.Context, ciliumPod Pod) ([]BPFEgressGatewayPolicyEntry, error) { + var targetEntries []BPFEgressGatewayPolicyEntry + + podIPs, err := ct.getConnDisruptClientEgressGatewayPodIPs(ctx) + if err != nil { + return nil, err + } + + gatewayNode, _, err := ct.getGatewayAndNonGatewayNodes() + if err != nil { + return nil, err + } + + gatewayIP := ct.GetGatewayNodeInternalIP(gatewayNode) + if gatewayIP == nil { + return nil, nil + } + + egressIP := "0.0.0.0" + if ciliumPod.Pod.Spec.NodeName == gatewayNode { + egressIP = gatewayIP.String() + } + + for _, podIP := range podIPs { + targetEntries = append(targetEntries, + BPFEgressGatewayPolicyEntry{ + SourceIP: podIP, + DestCIDR: "0.0.0.0/0", + EgressIP: egressIP, + GatewayIP: gatewayIP.String(), + }) + } + + return targetEntries, nil +} + func (ct *ConnectivityTest) hasNetworkPolicies(ctx context.Context) (bool, error) { for _, client := range ct.Clients() { cnps, err := client.ListCiliumNetworkPolicies(ctx, ct.params.TestNamespace, metav1.ListOptions{Limit: 1}) @@ -1440,7 +1674,9 @@ func (ct *ConnectivityTest) createServerPerfDeployment(ctx context.Context, name } func (ct *ConnectivityTest) deployPerf(ctx context.Context) error { - var err error + if err := ct.deployNamespace(ctx); err != nil { + return err + } nodeSelectorServer := labels.SelectorFromSet(ct.params.PerfParameters.NodeSelectorServer).String() nodeSelectorClient := labels.SelectorFromSet(ct.params.PerfParameters.NodeSelectorClient).String() @@ -1546,45 +1782,44 @@ func (ct *ConnectivityTest) deployPerf(ctx context.Context) error { return nil } -// deploymentList returns 2 lists of Deployments to be used for running tests with. -func (ct *ConnectivityTest) deploymentList() (srcList []string, dstList []string) { - if ct.params.Perf && ct.params.TestNamespaceIndex == 0 { - if ct.params.PerfParameters.NetQos { - srcList = append(srcList, perClientLowPriorityDeploymentName) - srcList = append(srcList, perClientHighPriorityDeploymentName) - srcList = append(srcList, perfServerDeploymentName) - return - } +// deploymentListPerf returns the list of deployments required by the performance tests. +func (ct *ConnectivityTest) deploymentListPerf() (srcList []string, dstList []string) { + if ct.params.PerfParameters.NetQos { + srcList = append(srcList, perClientLowPriorityDeploymentName) + srcList = append(srcList, perClientHighPriorityDeploymentName) + srcList = append(srcList, perfServerDeploymentName) + return + } - if ct.params.PerfParameters.PodNet || ct.params.PerfParameters.PodToHost { - if ct.params.PerfParameters.SameNode { - srcList = append(srcList, perfClientDeploymentName) - } - if ct.params.PerfParameters.OtherNode { - srcList = append(srcList, perfClientAcrossDeploymentName) - } + if ct.params.PerfParameters.PodNet || ct.params.PerfParameters.PodToHost { + if ct.params.PerfParameters.SameNode { + srcList = append(srcList, perfClientDeploymentName) } - if ct.params.PerfParameters.PodNet || ct.params.PerfParameters.HostToPod { - srcList = append(srcList, perfServerDeploymentName) + if ct.params.PerfParameters.OtherNode { + srcList = append(srcList, perfClientAcrossDeploymentName) } + } + if ct.params.PerfParameters.PodNet || ct.params.PerfParameters.HostToPod { + srcList = append(srcList, perfServerDeploymentName) + } - if ct.params.PerfParameters.HostNet || ct.params.PerfParameters.HostToPod { - if ct.params.PerfParameters.SameNode { - srcList = append(srcList, perfClientHostNetDeploymentName) - } - if ct.params.PerfParameters.OtherNode { - srcList = append(srcList, perfClientHostNetAcrossDeploymentName) - } + if ct.params.PerfParameters.HostNet || ct.params.PerfParameters.HostToPod { + if ct.params.PerfParameters.SameNode { + srcList = append(srcList, perfClientHostNetDeploymentName) } - if ct.params.PerfParameters.HostNet || ct.params.PerfParameters.PodToHost { - srcList = append(srcList, perfServerHostNetDeploymentName) + if ct.params.PerfParameters.OtherNode { + srcList = append(srcList, perfClientHostNetAcrossDeploymentName) } - - // Return early, we can't run regular connectivity tests - // along perf test - return } + if ct.params.PerfParameters.HostNet || ct.params.PerfParameters.PodToHost { + srcList = append(srcList, perfServerHostNetDeploymentName) + } + + return +} +// deploymentList returns 2 lists of Deployments to be used for running tests with. +func (ct *ConnectivityTest) deploymentList() (srcList []string, dstList []string) { srcList = []string{clientDeploymentName, client2DeploymentName, echoSameNodeDeploymentName} if ct.params.MultiCluster == "" && !ct.params.SingleNode { srcList = append(srcList, client3DeploymentName) @@ -1603,6 +1838,11 @@ func (ct *ConnectivityTest) deploymentList() (srcList []string, dstList []string srcList = append(srcList, testConnDisruptServerNSTrafficDeploymentName) dstList = append(dstList, ct.testConnDisruptClientNSTrafficDeploymentNames...) } + if ct.ShouldRunConnDisruptEgressGateway() { + srcList = append(srcList, testConnDisruptServerEgressGatewayDeploymentName) + dstList = append(dstList, testConnDisruptClientEgressGatewayOnGatewayNodeDeploymentName, + testConnDisruptClientEgressGatewayOnNonGatewayNodeDeploymentName) + } } if ct.params.MultiCluster != "" || !ct.params.SingleNode { @@ -1673,23 +1913,29 @@ func (ct *ConnectivityTest) DeleteConnDisruptTestDeployment(ctx context.Context, _ = client.DeleteServiceAccount(ctx, ct.params.TestNamespace, deploy.Name, metav1.DeleteOptions{}) } _ = client.DeleteDeployment(ctx, ct.params.TestNamespace, testConnDisruptServerNSTrafficDeploymentName, metav1.DeleteOptions{}) + _ = client.DeleteDeployment(ctx, ct.params.TestNamespace, testConnDisruptClientEgressGatewayOnGatewayNodeDeploymentName, metav1.DeleteOptions{}) + _ = client.DeleteDeployment(ctx, ct.params.TestNamespace, testConnDisruptClientEgressGatewayOnNonGatewayNodeDeploymentName, metav1.DeleteOptions{}) + _ = client.DeleteDeployment(ctx, ct.params.TestNamespace, testConnDisruptServerEgressGatewayDeploymentName, metav1.DeleteOptions{}) _ = client.DeleteServiceAccount(ctx, ct.params.TestNamespace, testConnDisruptClientDeploymentName, metav1.DeleteOptions{}) _ = client.DeleteServiceAccount(ctx, ct.params.TestNamespace, testConnDisruptServerDeploymentName, metav1.DeleteOptions{}) _ = client.DeleteServiceAccount(ctx, ct.params.TestNamespace, testConnDisruptServerNSTrafficDeploymentName, metav1.DeleteOptions{}) + _ = client.DeleteServiceAccount(ctx, ct.params.TestNamespace, testConnDisruptServerEgressGatewayDeploymentName, metav1.DeleteOptions{}) + _ = client.DeleteServiceAccount(ctx, ct.params.TestNamespace, testConnDisruptClientEgressGatewayOnGatewayNodeDeploymentName, metav1.DeleteOptions{}) + _ = client.DeleteServiceAccount(ctx, ct.params.TestNamespace, testConnDisruptClientEgressGatewayOnNonGatewayNodeDeploymentName, metav1.DeleteOptions{}) _ = client.DeleteService(ctx, ct.params.TestNamespace, testConnDisruptServiceName, metav1.DeleteOptions{}) _ = client.DeleteService(ctx, ct.params.TestNamespace, testConnDisruptNSTrafficServiceName, metav1.DeleteOptions{}) + _ = client.DeleteService(ctx, ct.params.TestNamespace, testConnDisruptEgressGatewayServiceName, metav1.DeleteOptions{}) _ = client.DeleteCiliumNetworkPolicy(ctx, ct.params.TestNamespace, testConnDisruptCNPName, metav1.DeleteOptions{}) _ = client.DeleteCiliumNetworkPolicy(ctx, ct.params.TestNamespace, testConnDisruptNSTrafficCNPName, metav1.DeleteOptions{}) + _ = client.DeleteCiliumNetworkPolicy(ctx, ct.params.TestNamespace, testConnDisruptEgressGatewayCNPName, metav1.DeleteOptions{}) + _ = client.DeleteCiliumEgressGatewayPolicy(ctx, testConnDisruptCEGPName, metav1.DeleteOptions{}) return nil } -// validateDeployment checks if the Deployments we created have the expected Pods in them. -func (ct *ConnectivityTest) validateDeployment(ctx context.Context) error { - +func (ct *ConnectivityTest) validateDeploymentCommon(ctx context.Context, srcDeployments, dstDeployments []string) error { ct.Debug("Validating Deployments...") - srcDeployments, dstDeployments := ct.deploymentList() for _, name := range srcDeployments { if err := WaitForDeployment(ctx, ct, ct.clients.src, ct.Params().TestNamespace, name); err != nil { return err @@ -1702,34 +1948,51 @@ func (ct *ConnectivityTest) validateDeployment(ctx context.Context) error { } } - if ct.params.Perf { - perfPods, err := ct.client.ListPods(ctx, ct.params.TestNamespace, metav1.ListOptions{LabelSelector: "kind=" + kindPerfName}) - if err != nil { - return fmt.Errorf("unable to list perf pods: %w", err) - } - for _, perfPod := range perfPods.Items { - _, hasLabel := perfPod.GetLabels()["server"] - if hasLabel { - ct.perfServerPod = append(ct.perfServerPod, Pod{ - K8sClient: ct.client, - Pod: perfPod.DeepCopy(), - port: 5201, - }) - } else { - ct.perfClientPods = append(ct.perfClientPods, Pod{ - K8sClient: ct.client, - Pod: perfPod.DeepCopy(), - }) - } + return nil +} + +func (ct *ConnectivityTest) validateDeploymentPerf(ctx context.Context) error { + srcDeployments, dstDeployments := ct.deploymentListPerf() + if err := ct.validateDeploymentCommon(ctx, srcDeployments, dstDeployments); err != nil { + return err + } + + perfPods, err := ct.client.ListPods(ctx, ct.params.TestNamespace, metav1.ListOptions{LabelSelector: "kind=" + kindPerfName}) + if err != nil { + return fmt.Errorf("unable to list perf pods: %w", err) + } + + for _, perfPod := range perfPods.Items { + _, hasLabel := perfPod.GetLabels()["server"] + if hasLabel { + ct.perfServerPod = append(ct.perfServerPod, Pod{ + K8sClient: ct.client, + Pod: perfPod.DeepCopy(), + port: 5201, + }) + } else { + ct.perfClientPods = append(ct.perfClientPods, Pod{ + K8sClient: ct.client, + Pod: perfPod.DeepCopy(), + }) } - // Sort pods so results are always displayed in the same order in console - sort.SliceStable(ct.perfServerPod, func(i, j int) bool { - return ct.perfServerPod[i].Pod.Name < ct.perfServerPod[j].Pod.Name - }) - sort.SliceStable(ct.perfClientPods, func(i, j int) bool { - return ct.perfClientPods[i].Pod.Name < ct.perfClientPods[j].Pod.Name - }) - return nil + } + + // Sort pods so results are always displayed in the same order in console + sort.SliceStable(ct.perfServerPod, func(i, j int) bool { + return ct.perfServerPod[i].Pod.Name < ct.perfServerPod[j].Pod.Name + }) + sort.SliceStable(ct.perfClientPods, func(i, j int) bool { + return ct.perfClientPods[i].Pod.Name < ct.perfClientPods[j].Pod.Name + }) + + return nil +} + +func (ct *ConnectivityTest) validateDeployment(ctx context.Context) error { + srcDeployments, dstDeployments := ct.deploymentList() + if err := ct.validateDeploymentCommon(ctx, srcDeployments, dstDeployments); err != nil { + return err } if ct.Features[features.LocalRedirectPolicy].Enabled { diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/features.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/features.go index 04c61a201d..d15f784f09 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/features.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/features.go @@ -330,7 +330,7 @@ func (ct *ConnectivityTest) detectFeatures(ctx context.Context) error { if err != nil { return err } - features.ExtractFromNodes(ct.params.Perf, ct.nodesWithoutCilium) + features.ExtractFromNodes(ct.nodesWithoutCilium) err = ct.extractFeaturesFromCiliumStatus(ctx, ciliumPod, features) if err != nil { return err diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/junit.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/junit.go index 38ded9002b..a03b93a0d0 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/junit.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/junit.go @@ -5,12 +5,15 @@ package check import ( "errors" + "fmt" "os" "strings" "github.com/cilium/cilium/cilium-cli/connectivity/internal/junit" ) +const MetadataDelimiter = ";metadata;" + // NewJUnitCollector factory function that returns JUnitCollector. func NewJUnitCollector(junitProperties map[string]string, junitFile string) *JUnitCollector { properties := []junit.Property{ @@ -68,7 +71,8 @@ func (j *JUnitCollector) Collect(ct *ConnectivityTest) { j.testSuite.Failures++ msgs := []string{} for _, a := range t.failedActions() { - msgs = append(msgs, a.String()) + owners := ct.GetOwners(a.Scenario()) + msgs = append(msgs, fmt.Sprintf("%s%sOwners: %s", a, MetadataDelimiter, strings.Join(owners, ", "))) } test.Failure.Value = strings.Join(msgs, "\n") } diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/logger.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/logger.go index 6094a527e0..bfc2b5bbc5 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/logger.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/logger.go @@ -7,78 +7,109 @@ import ( "bytes" "fmt" "io" - "sync/atomic" - "time" - - "github.com/cilium/cilium/pkg/lock" ) // NewConcurrentLogger factory function that returns ConcurrentLogger. -func NewConcurrentLogger(writer io.Writer, concurrency int) *ConcurrentLogger { +func NewConcurrentLogger(writer io.Writer) *ConcurrentLogger { return &ConcurrentLogger{ - messageCh: make(chan message), - writer: writer, - // The concurrency parameter is used for nsTestsCh buffer size calculation. - // The buffer will be able to accept 10 times more unique connectivity tests - // than concurrency value. Write to the channel implemented in a separate - // goroutine to avoid deadlock in case if buffer is full. - nsTestsCh: make(chan string, concurrency*10), - nsTestMsgs: make(map[string][]message), - nsTestMsgsLock: lock.Mutex{}, - collectorStarted: atomic.Bool{}, - printerDoneCh: make(chan bool), + writer: writer, + messages: make(chan message), + done: make(chan struct{}), } } type ConcurrentLogger struct { - messageCh chan message - writer io.Writer - nsTestsCh chan string - nsTestMsgs map[string][]message - nsTestMsgsLock lock.Mutex - collectorStarted atomic.Bool - printerDoneCh chan bool - nsTestFinishCount int + writer io.Writer + messages chan message + done chan struct{} } -// Start starts ConcurrentLogger internals in separate goroutines: -// - collector: collects incoming test messages. -// - printer: sends messages to the writer in corresponding order. +// Start starts ConcurrentLogger func (c *ConcurrentLogger) Start() { - c.collectorStarted.Store(true) - go c.collector() - go c.printer() + go func() { + // current is the test that is currently being streamed to the writer without + // buffering + var current *Test + + // buffered is a map of tests (other than current one) that have not finished yet + buffered := make(map[*Test]*bytes.Buffer) + + // finished is an ordered list of tests to be logged once the current test finishes + var finished []*bytes.Buffer + + for m := range c.messages { + // make this the current test if none + if current == nil { + current = m.test + } + + // stream the current test without buffering + if m.test == current { + mustWrite(c.writer, m.data) + if m.finish { + current = nil + } + } else { + // buffer other tests + buf, ok := buffered[m.test] + if !ok { + buf = &bytes.Buffer{} + buffered[m.test] = buf + } + mustWrite(buf, m.data) + if m.finish { + delete(buffered, m.test) + finished = append(finished, buf) + } + } + + if current == nil { + // log any finished tests after done with the current test + for _, buf := range finished { + mustWrite(c.writer, buf.Bytes()) + } + finished = finished[len(finished):] + + // pick one of the running tests as the current one, if any + for test, buf := range buffered { + delete(buffered, test) + mustWrite(c.writer, buf.Bytes()) + current = test + break + } + } + } + // No more messages, log all remaining messages + for _, buf := range finished { + mustWrite(c.writer, buf.Bytes()) + } + for _, buf := range buffered { + mustWrite(c.writer, buf.Bytes()) + } + close(c.done) + }() } // Stop closes incoming message channel and waits while all messages are printed. func (c *ConcurrentLogger) Stop() { - close(c.messageCh) - <-c.printerDoneCh - close(c.printerDoneCh) + close(c.messages) + <-c.done } type message struct { - namespace string - testName string - data string - finish bool -} - -func (m message) nsTest() string { - return fmt.Sprintf("%s:%s", m.namespace, m.testName) + test *Test + data []byte + finish bool } // Print schedules message for the test to be printed. -func (c *ConcurrentLogger) Print(test *Test, msg string) { - buf := &bytes.Buffer{} +func (c *ConcurrentLogger) Print(test *Test, msg []byte) { if test.ctx.timestamp() { - mustFprint(buf, timestamp()) + msg = append(timestampBytes(), msg...) } - mustFprint(buf, msg) - c.messageCh <- message{ - namespace: test.ctx.params.TestNamespace, - testName: test.name, - data: buf.String(), + c.messages <- message{ + test: test, + data: msg, } } @@ -86,13 +117,12 @@ func (c *ConcurrentLogger) Print(test *Test, msg string) { func (c *ConcurrentLogger) Printf(test *Test, format string, args ...interface{}) { buf := &bytes.Buffer{} if test.ctx.timestamp() { - mustFprint(buf, timestamp()) + mustWrite(buf, timestampBytes()) } mustFprintf(buf, format, args...) - c.messageCh <- message{ - namespace: test.ctx.params.TestNamespace, - testName: test.name, - data: buf.String(), + c.messages <- message{ + test: test, + data: buf.Bytes(), } } @@ -105,77 +135,15 @@ func (c *ConcurrentLogger) FinishTest(test *Test) { panic(fmt.Errorf("failed to read from test log buffer: %w", err)) } } - c.messageCh <- message{ - namespace: test.Context().Params().TestNamespace, - testName: test.Name(), - data: buf.String(), - finish: true, - } -} - -func (c *ConcurrentLogger) collector() { - defer c.collectorStarted.Store(false) - for m := range c.messageCh { - nsTest := m.nsTest() - c.nsTestMsgsLock.Lock() - nsTestMsgs, ok := c.nsTestMsgs[nsTest] - if !ok { - nsTestMsgs = make([]message, 0) - // use a separate goroutine to avoid deadlock if the channel - // buffer is full, printer goroutine will pull it eventually - go func() { c.nsTestsCh <- nsTest }() - } - c.nsTestMsgs[nsTest] = append(nsTestMsgs, m) - c.nsTestMsgsLock.Unlock() - } -} - -func (c *ConcurrentLogger) printer() { - // read messages while the collector is working - for c.collectorStarted.Load() { - // double-check if there are new messages to avoid - // deadlock reading from the `nsTestsCh` channel - if c.nsTestFinishCount < c.collectedTestCount() { - c.printTestMessages(<-c.nsTestsCh) - } - } - // collector stopped but there still might be messages to print - for c.nsTestFinishCount < c.collectedTestCount() { - c.printTestMessages(<-c.nsTestsCh) - } - c.printerDoneCh <- true - close(c.nsTestsCh) -} - -func (c *ConcurrentLogger) collectedTestCount() int { - c.nsTestMsgsLock.Lock() - testCount := len(c.nsTestMsgs) - c.nsTestMsgsLock.Unlock() - return testCount -} - -func (c *ConcurrentLogger) printTestMessages(nsTest string) { - for printedMessageIndex := 0; ; { - c.nsTestMsgsLock.Lock() - messages := c.nsTestMsgs[nsTest] - c.nsTestMsgsLock.Unlock() - if len(messages) == printedMessageIndex { - // wait for new test messages - time.Sleep(time.Millisecond * 50) - continue - } - for ; printedMessageIndex < len(messages); printedMessageIndex++ { - mustFprint(c.writer, messages[printedMessageIndex].data) - if messages[printedMessageIndex].finish { - c.nsTestFinishCount++ - return - } - } + c.messages <- message{ + test: test, + data: buf.Bytes(), + finish: true, } } -func mustFprint(writer io.Writer, msg string) { - if _, err := fmt.Fprint(writer, msg); err != nil { +func mustWrite(writer io.Writer, msg []byte) { + if _, err := writer.Write(msg); err != nil { panic(fmt.Errorf("failed to print log message: %w", err)) } } diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/logging.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/logging.go index 689485f069..5b13de7b09 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/logging.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/logging.go @@ -115,9 +115,9 @@ func (s defaultScenario) Name() string { var ghWorkflowRegexp = regexp.MustCompile("^(?:.+?)/(?:.+?)/(.+?)@.*$") -func (ct *ConnectivityTest) LogOwners(scenarios ...ownedScenario) { +func (ct *ConnectivityTest) GetOwners(scenarios ...ownedScenario) []string { if !ct.params.LogCodeOwners { - return + return nil } rules := make(map[ownedScenario]*codeowners.Rule) @@ -126,7 +126,7 @@ func (ct *ConnectivityTest) LogOwners(scenarios ...ownedScenario) { if err != nil || rule == nil || rule.Owners == nil { ct.Fatalf("Failed to find CODEOWNERS for test scenario. Developer BUG?"+ "\n\t\tname=%s path=%s err=%s", scenario.Name(), scenario.FilePath(), err) - return + return nil } rules[scenario] = rule } @@ -154,23 +154,36 @@ func (ct *ConnectivityTest) LogOwners(scenarios ...ownedScenario) { excludeOwners[owner] = struct{}{} } - ct.Log(" ⛑️ The following owners are responsible for reliability of the testsuite: ") + var owners []string for scenario, rule := range rules { for _, o := range rule.Owners { owner := o.String() if _, ok := excludeOwners[owner]; ok { continue } - ct.Log(" - " + owner + " (" + scenario.Name() + ")") + owners = append(owners, fmt.Sprintf("%s (%s)", owner, scenario.Name())) } for _, o := range workflowOwners { owner := o.String() if _, ok := excludeOwners[owner]; ok { continue } - ct.Log(" - " + owner + " (" + ghWorkflow + ")") + owners = append(owners, fmt.Sprintf("%s (%s)", owner, ghWorkflow)) } } + return owners +} + +func (ct *ConnectivityTest) LogOwners(scenarios ...ownedScenario) { + owners := ct.GetOwners(scenarios...) + if len(owners) == 0 { + return + } + + ct.Log(" ⛑️ The following owners are responsible for reliability of the testsuite: ") + for _, o := range owners { + ct.Log(" - " + o) + } } // Logf logs a formatted message. @@ -323,7 +336,7 @@ func (t *Test) flush() { if _, err := io.Copy(buf, t.logBuf); err != nil { panic(err) } - t.ctx.logger.Print(t, buf.String()) + t.ctx.logger.Print(t, buf.Bytes()) // Assign a nil buffer so future writes go to user-specified writer. t.logBuf = nil @@ -486,6 +499,14 @@ func timestamp() string { return fmt.Sprintf("[%s] ", time.Now().Format(time.RFC3339)) } +func timestampBytes() []byte { + b := make([]byte, 0, 32) // roughly enough space + b = append(b, '[') + b = time.Now().AppendFormat(b, time.RFC3339) + b = append(b, ']', ' ') + return b +} + type debugWriter struct { ct *ConnectivityTest } diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/peer.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/peer.go index 4f3a883488..9baf13a4e2 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/peer.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/peer.go @@ -5,6 +5,7 @@ package check import ( "fmt" + "maps" "net" "net/url" "strconv" @@ -129,9 +130,7 @@ func (p Pod) Port() uint32 { func (p Pod) Labels() map[string]string { newMap := make(map[string]string, len(p.Pod.Labels)) - for k, v := range p.Pod.Labels { - newMap[k] = v - } + maps.Copy(newMap, p.Pod.Labels) return newMap } @@ -239,9 +238,7 @@ func (s Service) HasLabel(name, value string) bool { // Labels returns the copy of service labels func (s Service) Labels() map[string]string { newMap := make(map[string]string, len(s.Service.Labels)) - for k, v := range s.Service.Labels { - newMap[k] = v - } + maps.Copy(newMap, s.Service.Labels) return newMap } @@ -436,9 +433,7 @@ func (he httpEndpoint) HasLabel(name, value string) bool { func (he httpEndpoint) Labels() map[string]string { newMap := make(map[string]string, len(*he.labels)) - for k, v := range *he.labels { - newMap[k] = v - } + maps.Copy(newMap, *he.labels) return newMap } diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/policy.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/policy.go index caf6518461..52fc24c108 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/policy.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/policy.go @@ -23,6 +23,7 @@ import ( "github.com/cilium/cilium/cilium-cli/defaults" "github.com/cilium/cilium/cilium-cli/k8s" "github.com/cilium/cilium/cilium-cli/utils/features" + logfilter "github.com/cilium/cilium/cilium-cli/utils/log" k8sConst "github.com/cilium/cilium/pkg/k8s/apis/cilium.io" ciliumv2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2" "github.com/cilium/cilium/pkg/k8s/client/clientset/versioned/scheme" @@ -378,7 +379,13 @@ func (t *Test) ContainerLogs(ctx context.Context) { if err != nil { t.Fatalf("Error reading Cilium logs: %s", err) } - t.Infof("Cilium agent %s/%s logs since %s:\n%s", pod.Pod.Namespace, pod.Pod.Name, t.startTime.String(), log) + t.Infof( + "Cilium agent %s/%s logs since %s:\n%s", + pod.Pod.Namespace, + pod.Pod.Name, + t.startTime.String(), + logfilter.Reduce(log, t.verbose), + ) } } diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/test.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/test.go index 23aca30ffe..95a5918d06 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/test.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/test.go @@ -11,6 +11,7 @@ import ( "fmt" "io" "net" + "slices" "time" "github.com/blang/semver/v4" @@ -68,6 +69,7 @@ func NewTest(name string, verbose bool, debug bool) *Test { clrps: make(map[string]*ciliumv2.CiliumLocalRedirectPolicy), logBuf: &bytes.Buffer{}, // maintain internal buffer by default conditionFn: nil, + verbose: verbose, } // Setting the internal buffer to nil causes the logger to // write directly to stdout in verbose or debug mode. @@ -140,8 +142,9 @@ type Test struct { // Buffer to store output until it's flushed by a failure. // Unused when run in verbose or debug mode. - logMu lock.RWMutex - logBuf io.ReadWriter + logMu lock.RWMutex + logBuf io.ReadWriter + verbose bool // conditionFn is a function that returns true if the test needs to run, // and false otherwise. By default, it's set to a function that returns @@ -299,7 +302,10 @@ func (t *Test) willRun() (bool, string) { func (t *Test) finalize() { t.Debug("Finalizing Test", t.Name()) - for _, f := range t.finalizers { + // Iterate finalizers in backward order. + // As an example, first we create secrets that are referenced in policies. + // When performing cleanup, we want to first delete policies and then secrets. + for _, f := range slices.Backward(t.finalizers) { // Use a detached context to make sure this call is not affected by // context cancellation. Usually, finalization (e.g., netpol removal) // needs to happen even when the user interrupted the program. @@ -855,10 +861,5 @@ func (t *Test) CiliumLocalRedirectPolicies() map[string]*ciliumv2.CiliumLocalRed } func (t *Test) HasNetworkPolicies() bool { - for _, obj := range t.resources { - if isPolicy(obj) { - return true - } - } - return false + return slices.ContainsFunc(t.resources, isPolicy) } diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/wait.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/wait.go index e0c80839cd..1a6c02f9ac 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/wait.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/check/wait.go @@ -343,6 +343,86 @@ func WaitForIPCache(ctx context.Context, log Logger, agent Pod, pods []Pod) erro } } +// BPFEgressGatewayPolicyEntry represents an entry in the BPF egress gateway policy map +type BPFEgressGatewayPolicyEntry struct { + SourceIP string `json:"sourceIP"` + DestCIDR string `json:"destCIDR"` + EgressIP string `json:"egressIP"` + GatewayIP string `json:"gatewayIP"` +} + +// matches is an helper used to compare the receiver bpfEgressGatewayPolicyEntry with another entry +func (e *BPFEgressGatewayPolicyEntry) matches(t BPFEgressGatewayPolicyEntry) bool { + return t.SourceIP == e.SourceIP && + t.DestCIDR == e.DestCIDR && + t.EgressIP == e.EgressIP && + t.GatewayIP == e.GatewayIP +} + +// WaitForEgressGatewayBpfPolicyEntries waits for the egress gateway policy maps on each node to WaitForEgressGatewayBpfPolicyEntries +// with the entries returned by the targetEntriesCallback +func WaitForEgressGatewayBpfPolicyEntries(ctx context.Context, ciliumPods map[string]Pod, + targetEntriesCallback func(ciliumPod Pod) ([]BPFEgressGatewayPolicyEntry, error), + excludeEntries func(ciliumPod Pod) ([]BPFEgressGatewayPolicyEntry, error), +) error { + w := wait.NewObserver(ctx, wait.Parameters{Timeout: 10 * time.Second}) + defer w.Cancel() + + ensureBpfPolicyEntries := func() error { + for _, ciliumPod := range ciliumPods { + targetEntries, err := targetEntriesCallback(ciliumPod) + if err != nil { + return fmt.Errorf("failed to get target entries: %w", err) + } + + cmd := strings.Split("cilium bpf egress list -o json", " ") + stdout, err := ciliumPod.K8sClient.ExecInPod(ctx, ciliumPod.Pod.Namespace, ciliumPod.Pod.Name, defaults.AgentContainerName, cmd) + if err != nil { + return fmt.Errorf("failed to run cilium bpf egress list command: %w", err) + } + + var entries []BPFEgressGatewayPolicyEntry + json.Unmarshal(stdout.Bytes(), &entries) + + excludes, err := excludeEntries(ciliumPod) + if err != nil { + return fmt.Errorf("failed to get exclude entries: %w", err) + } + for _, exclude := range excludes { + entries = slices.DeleteFunc(entries, func(entry BPFEgressGatewayPolicyEntry) bool { + return entry.matches(exclude) + }) + } + + for _, targetEntry := range targetEntries { + if !slices.ContainsFunc(entries, targetEntry.matches) { + return fmt.Errorf("could not find egress gateway policy entry matching %+v", targetEntry) + } + } + + for _, entry := range entries { + if !slices.ContainsFunc(targetEntries, entry.matches) { + return fmt.Errorf("untracked entry %+v in the egress gateway policy map", entry) + } + } + } + + return nil + } + + for { + if err := ensureBpfPolicyEntries(); err != nil { + if err := w.Retry(err); err != nil { + return fmt.Errorf("failed to ensure egress gateway policy map is properly populated: %w", err) + } + + continue + } + + return nil + } +} + func validateIPCache(ctx context.Context, agent Pod, pods []Pod) error { stdout, err := agent.K8sClient.ExecInPod(ctx, agent.Namespace(), agent.NameWithoutNamespace(), defaults.AgentContainerName, []string{"cilium", "bpf", "ipcache", "list", "-o", "json"}) diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/perf/benchmarks/netperf/perfpod.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/perf/benchmarks/netperf/perfpod.go index afb9588015..d860442164 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/perf/benchmarks/netperf/perfpod.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/perf/benchmarks/netperf/perfpod.go @@ -6,6 +6,7 @@ package netperf import ( "context" "fmt" + "slices" "strconv" "strings" "time" @@ -52,6 +53,13 @@ func (s *netPerf) Run(ctx context.Context, t *check.Test) { } } + if perfParameters.ThroughputMulti { + tests = append(tests, "TCP_STREAM_MULTI") + if perfParameters.UDP { + tests = append(tests, "UDP_STREAM_MULTI") + } + } + if perfParameters.CRR { tests = append(tests, "TCP_CRR") } @@ -120,6 +128,7 @@ func (s *netPerf) Run(ctx context.Context, t *check.Test) { SameNode: sameNode, Sample: sample, Duration: perfParameters.Duration, + Streams: perfParameters.Streams, Scenario: scenarioName, MsgSize: perfParameters.MessageSize, NetQos: false, @@ -140,7 +149,7 @@ func buildExecCommand(test string, sip string, duration time.Duration, args []st return exec } -func parseDuration(a *check.Action, value string) time.Duration { +func parseDuration(a action, value string) time.Duration { res, err := time.ParseDuration(value + "us") // by default latencies in netperf are reported in microseconds if err != nil { a.Fatalf("Unable to process netperf result, duration: %s", value) @@ -148,7 +157,7 @@ func parseDuration(a *check.Action, value string) time.Duration { return res } -func parseFloat(a *check.Action, value string) float64 { +func parseFloat(a action, value string) float64 { res, err := strconv.ParseFloat(value, 64) if err != nil { a.Fatalf("Unable to process netperf result, float: %s", value) @@ -156,22 +165,8 @@ func parseFloat(a *check.Action, value string) float64 { return res } -func NetperfCmd(ctx context.Context, sip string, perfTest common.PerfTests, a *check.Action) common.PerfResult { - args := []string{"-o", "MIN_LATENCY,MEAN_LATENCY,MAX_LATENCY,P50_LATENCY,P90_LATENCY,P99_LATENCY,TRANSACTION_RATE,THROUGHPUT,THROUGHPUT_UNITS"} - if perfTest.Test == "UDP_STREAM" || perfTest.NetQos { - args = append(args, "-m", fmt.Sprintf("%d", perfTest.MsgSize)) - } - exec := buildExecCommand(perfTest.Test, sip, perfTest.Duration, args) - - a.ExecInPod(ctx, exec) - output := a.CmdOutput() - a.Debugf("Netperf output: ", output) - lines := strings.Split(output, "\n") - if len(lines) < 2 { - a.Fatal("Unable to process netperf result") - } - resultsLine := lines[len(lines)-2] - values := strings.Split(resultsLine, ",") +func parseNetperfResult(a action, test, line string) common.PerfResult { + values := strings.Split(line, ",") if len(values) != 9 { a.Fatalf("Unable to process netperf result") } @@ -195,19 +190,75 @@ func NetperfCmd(ctx context.Context, sip string, perfTest common.PerfTests, a *c }, } - if strings.HasSuffix(perfTest.Test, "_STREAM") { + if strings.HasSuffix(test, "_STREAM") { // We don't want to report transaction rate or latency res.TransactionRateMetric = nil res.Latency = nil // Verify that throughput unit is 10^6bits/s if values[8] != "10^6bits/s" { - a.Fatal("Unable to process netperf result") + a.Fatalf("Unable to process netperf result") } } - if strings.HasSuffix(perfTest.Test, "_RR") || strings.HasSuffix(perfTest.Test, "_CRR") { + if strings.HasSuffix(test, "_RR") || strings.HasSuffix(test, "_CRR") { // We don't want to report throughput res.ThroughputMetric = nil } return res } + +type action interface { + ExecInPod(ctx context.Context, cmd []string) + CmdOutput() string + + Debugf(format string, args ...any) + Fatalf(format string, args ...any) +} + +func NetperfCmd(ctx context.Context, sip string, perfTest common.PerfTests, a action) common.PerfResult { + test := strings.TrimSuffix(perfTest.Test, "_MULTI") + + streams := uint(1) + if strings.HasSuffix(perfTest.Test, "_MULTI") { + streams = perfTest.Streams + + if !strings.HasSuffix(test, "_STREAM") { + a.Fatalf("Only STREAM tests support parallelism") + } + } + + args := []string{"-o", "MIN_LATENCY,MEAN_LATENCY,MAX_LATENCY,P50_LATENCY,P90_LATENCY,P99_LATENCY,TRANSACTION_RATE,THROUGHPUT,THROUGHPUT_UNITS"} + if test == "UDP_STREAM" || perfTest.NetQos { + args = append(args, "-m", fmt.Sprintf("%d", perfTest.MsgSize)) + } + exec := buildExecCommand(test, sip, perfTest.Duration, args) + + if streams >= 2 { + exec = []string{"/bin/bash", "-c", + // We write the output of each process to a separate file and cat them + // at the end to prevent the possibility of interleaved output. + fmt.Sprintf("DIR=$(mktemp -d); for i in {1..%d}; do %s > $DIR/out$i.out & done; wait; cat $DIR/*; rm -rf $DIR", + streams, strings.Join(exec, " "), + )} + } + + a.ExecInPod(ctx, exec) + output := a.CmdOutput() + a.Debugf("Netperf output: %s", output) + lines := slices.DeleteFunc( + strings.Split(output, "\n"), + // Result lines always start with a number, hence drop all the others. + func(line string) bool { return len(line) == 0 || line[0] < '0' || line[0] > '9' }, + ) + if uint(len(lines)) != streams { + a.Fatalf("Unable to process netperf result: expected %d, got %d", streams, len(lines)) + } + + res := parseNetperfResult(a, test, lines[0]) + for _, line := range lines[1:] { + parsed := parseNetperfResult(a, test, line) + res.ThroughputMetric.Throughput += parsed.ThroughputMetric.Throughput + } + + return res +} diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/perf/common/metrics.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/perf/common/metrics.go index 199e5e8372..f9da1ef086 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/perf/common/metrics.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/perf/common/metrics.go @@ -101,6 +101,7 @@ type PerfTests struct { Sample int MsgSize int Duration time.Duration + Streams uint NetQos bool } diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/tests/egressgateway.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/tests/egressgateway.go index e2dce34550..61b6e36580 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/tests/egressgateway.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/tests/egressgateway.go @@ -8,120 +8,14 @@ import ( "encoding/json" "fmt" "net" - "strings" - "time" - v1 "k8s.io/api/core/v1" k8sErrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/cilium/cilium/cilium-cli/connectivity/check" - "github.com/cilium/cilium/cilium-cli/defaults" "github.com/cilium/cilium/cilium-cli/utils/features" - "github.com/cilium/cilium/cilium-cli/utils/wait" ) -// bpfEgressGatewayPolicyEntry represents an entry in the BPF egress gateway policy map -type bpfEgressGatewayPolicyEntry struct { - SourceIP string `json:"sourceIP"` - DestCIDR string `json:"destCIDR"` - EgressIP string `json:"egressIP"` - GatewayIP string `json:"gatewayIP"` -} - -// matches is an helper used to compare the receiver bpfEgressGatewayPolicyEntry with another entry -func (e *bpfEgressGatewayPolicyEntry) matches(t bpfEgressGatewayPolicyEntry) bool { - return t.SourceIP == e.SourceIP && - t.DestCIDR == e.DestCIDR && - t.EgressIP == e.EgressIP && - t.GatewayIP == e.GatewayIP -} - -// WaitForEgressGatewayBpfPolicyEntries waits for the egress gateway policy maps on each node to WaitForEgressGatewayBpfPolicyEntries -// with the entries returned by the targetEntriesCallback -func WaitForEgressGatewayBpfPolicyEntries(ctx context.Context, t *check.Test, - targetEntriesCallback func(ciliumPod check.Pod) []bpfEgressGatewayPolicyEntry, -) { - ct := t.Context() - - w := wait.NewObserver(ctx, wait.Parameters{Timeout: 10 * time.Second}) - defer w.Cancel() - - ensureBpfPolicyEntries := func() error { - for _, ciliumPod := range ct.CiliumPods() { - targetEntries := targetEntriesCallback(ciliumPod) - - cmd := strings.Split("cilium bpf egress list -o json", " ") - stdout, err := ciliumPod.K8sClient.ExecInPod(ctx, ciliumPod.Pod.Namespace, ciliumPod.Pod.Name, defaults.AgentContainerName, cmd) - if err != nil { - t.Fatal("failed to run cilium bpf egress list command: %w", err) - } - - entries := []bpfEgressGatewayPolicyEntry{} - json.Unmarshal(stdout.Bytes(), &entries) - - nextTargetEntry: - for _, targetEntry := range targetEntries { - for _, entry := range entries { - if targetEntry.matches(entry) { - continue nextTargetEntry - } - } - - return fmt.Errorf("Could not find egress gateway policy entry matching %+v", targetEntry) - } - - nextEntry: - for _, entry := range entries { - for _, targetEntry := range targetEntries { - if targetEntry.matches(entry) { - continue nextEntry - } - } - - return fmt.Errorf("Untracked entry %+v in the egress gateway policy map", entry) - } - } - - return nil - } - - for { - if err := ensureBpfPolicyEntries(); err != nil { - if err := w.Retry(err); err != nil { - t.Fatal("Failed to ensure egress gateway policy map is properly populated:", err) - } - - continue - } - - return - } -} - -// getGatewayNodeInternalIP returns the k8s internal IP of the node acting as gateway for this test -func getGatewayNodeInternalIP(ct *check.ConnectivityTest, egressGatewayNode string) net.IP { - gatewayNode, ok := ct.Nodes()[egressGatewayNode] - if !ok { - return nil - } - - for _, addr := range gatewayNode.Status.Addresses { - if addr.Type != v1.NodeInternalIP { - continue - } - - ip := net.ParseIP(addr.Address) - if ip == nil || ip.To4() == nil { - continue - } - - return ip - } - - return nil -} - // extractClientIPFromResponse extracts the client IP from the response of the echo-external service func extractClientIPFromResponse(res string) net.IP { var clientIP struct { @@ -171,13 +65,13 @@ func (s *egressGateway) Run(ctx context.Context, t *check.Test) { t.Fatal("Cannot get egress gateway node") } - egressGatewayNodeInternalIP := getGatewayNodeInternalIP(ct, egressGatewayNode) + egressGatewayNodeInternalIP := ct.GetGatewayNodeInternalIP(egressGatewayNode) if egressGatewayNodeInternalIP == nil { t.Fatal("Cannot get egress gateway node internal IP") } - WaitForEgressGatewayBpfPolicyEntries(ctx, t, func(ciliumPod check.Pod) []bpfEgressGatewayPolicyEntry { - targetEntries := []bpfEgressGatewayPolicyEntry{} + err := check.WaitForEgressGatewayBpfPolicyEntries(ctx, ct.CiliumPods(), func(ciliumPod check.Pod) ([]check.BPFEgressGatewayPolicyEntry, error) { + var targetEntries []check.BPFEgressGatewayPolicyEntry egressIP := "0.0.0.0" if ciliumPod.Pod.Spec.NodeName == egressGatewayNode { @@ -186,7 +80,7 @@ func (s *egressGateway) Run(ctx context.Context, t *check.Test) { for _, client := range ct.ClientPods() { targetEntries = append(targetEntries, - bpfEgressGatewayPolicyEntry{ + check.BPFEgressGatewayPolicyEntry{ SourceIP: client.Pod.Status.PodIP, DestCIDR: "0.0.0.0/0", EgressIP: egressIP, @@ -196,7 +90,7 @@ func (s *egressGateway) Run(ctx context.Context, t *check.Test) { for _, echo := range ct.EchoPods() { targetEntries = append(targetEntries, - bpfEgressGatewayPolicyEntry{ + check.BPFEgressGatewayPolicyEntry{ SourceIP: echo.Pod.Status.PodIP, DestCIDR: "0.0.0.0/0", EgressIP: egressIP, @@ -204,8 +98,13 @@ func (s *egressGateway) Run(ctx context.Context, t *check.Test) { }) } - return targetEntries + return targetEntries, nil + }, func(ciliumPod check.Pod) ([]check.BPFEgressGatewayPolicyEntry, error) { + return ct.GetConnDisruptEgressPolicyEntries(ctx, ciliumPod) }) + if err != nil { + t.Fatal(err) + } // Ping hosts (pod to host connectivity). Should not get masqueraded with egress IP i := 0 @@ -336,13 +235,13 @@ func (s *egressGatewayExcludedCIDRs) Run(ctx context.Context, t *check.Test) { t.Fatal("Cannot get egress gateway node") } - egressGatewayNodeInternalIP := getGatewayNodeInternalIP(ct, egressGatewayNode) + egressGatewayNodeInternalIP := ct.GetGatewayNodeInternalIP(egressGatewayNode) if egressGatewayNodeInternalIP == nil { t.Fatal("Cannot get egress gateway node internal IP") } - WaitForEgressGatewayBpfPolicyEntries(ctx, t, func(ciliumPod check.Pod) []bpfEgressGatewayPolicyEntry { - targetEntries := []bpfEgressGatewayPolicyEntry{} + err := check.WaitForEgressGatewayBpfPolicyEntries(ctx, ct.CiliumPods(), func(ciliumPod check.Pod) ([]check.BPFEgressGatewayPolicyEntry, error) { + var targetEntries []check.BPFEgressGatewayPolicyEntry egressIP := "0.0.0.0" if ciliumPod.Pod.Spec.NodeName == egressGatewayNode { @@ -361,7 +260,7 @@ func (s *egressGatewayExcludedCIDRs) Run(ctx context.Context, t *check.Test) { } targetEntries = append(targetEntries, - bpfEgressGatewayPolicyEntry{ + check.BPFEgressGatewayPolicyEntry{ SourceIP: client.Pod.Status.PodIP, DestCIDR: "0.0.0.0/0", EgressIP: egressIP, @@ -369,7 +268,7 @@ func (s *egressGatewayExcludedCIDRs) Run(ctx context.Context, t *check.Test) { }) targetEntries = append(targetEntries, - bpfEgressGatewayPolicyEntry{ + check.BPFEgressGatewayPolicyEntry{ SourceIP: client.Pod.Status.PodIP, DestCIDR: fmt.Sprintf("%s/32", nodeWithoutCilium.Status.Addresses[0].Address), EgressIP: egressIP, @@ -378,8 +277,13 @@ func (s *egressGatewayExcludedCIDRs) Run(ctx context.Context, t *check.Test) { } } - return targetEntries + return targetEntries, nil + }, func(ciliumPod check.Pod) ([]check.BPFEgressGatewayPolicyEntry, error) { + return ct.GetConnDisruptEgressPolicyEntries(ctx, ciliumPod) }) + if err != nil { + t.Fatal(err) + } // Traffic matching an egress gateway policy and an excluded CIDR should leave the cluster masqueraded with the // node IP where the pod is running rather than with the egress IP(pod to external service) diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/tests/encryption.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/tests/encryption.go index 4bf32e8eba..07f737773f 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/tests/encryption.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/tests/encryption.go @@ -258,14 +258,7 @@ func (s *podToPodEncryption) Run(ctx context.Context, t *check.Test) { t.Debug("Encapsulation before WG encryption") } - e, ok := t.Context().Feature(features.EncryptionPod) - isIPSec := ok && e.Enabled && e.Mode == "ipsec" - t.ForEachIPFamily(func(ipFam features.IPFamily) { - if isIPSec && ipFam == features.IPFamilyV6 { - t.Debugf("Inactive IPv6 test with IPSec, see https://github.com/cilium/cilium/issues/35485") - return - } testNoTrafficLeak(ctx, t, s, client, &server, &clientHost, &serverHost, requestHTTP, ipFam, assertNoLeaks, true, wgEncap) }) } diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/tests/errors.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/tests/errors.go index bab66d441f..f0751bf36f 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/tests/errors.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/tests/errors.go @@ -65,7 +65,7 @@ func NoErrorsInLogs(ciliumVersion semver.Version, checkLevels []string, external endpointMapDeleteFailed, etcdReconnection, epRestoreMissingState, mutationDetectorKlog, hubbleFailedCreatePeer, fqdnDpUpdatesTimeout, longNetpolUpdate, failedToGetEpLabels, failedCreategRPCClient, unableReallocateIngressIP, fqdnMaxIPPerHostname, failedGetMetricsAPI, envoyTLSWarning, - ciliumNodeConfigDeprecation, hubbleUIEnvVarFallback, k8sClientNetworkStatusError} + ciliumNodeConfigDeprecation, hubbleUIEnvVarFallback, k8sClientNetworkStatusError, bgpAlphaResourceDeprecation} // The list is adopted from cilium/cilium/test/helper/utils.go var errorMsgsWithExceptions = map[string][]logMatcher{ panicMessage: nil, @@ -383,4 +383,6 @@ var ( // envoyTLSWarningTemplate is the legitimate warning log for negative TLS SNI test case // This is a template string as we need to replace %s for external target flag envoyTLSWarningTemplate = "cilium.tls_wrapper: Could not get server TLS context for pod.*on destination IP.*port 443 sni.*%s.*and raw socket is not allowed" + // bgpV2alpha1ResourceDeprecation is expected when using deprecated BGP resource versions in a test, specifically when running the tests after a Cilium downgrade. + bgpAlphaResourceDeprecation = regexMatcher{regexp.MustCompile(`cilium.io/v2alpha1 CiliumBGP\w+ is deprecated`)} ) diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/tests/multicast.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/tests/multicast.go index 170a404457..5521e6e2a2 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/tests/multicast.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/tests/multicast.go @@ -8,6 +8,7 @@ import ( "errors" "fmt" "net/netip" + "slices" "strings" "sync" @@ -146,12 +147,7 @@ func (s *socatMulticast) addNodeWithoutGroup(nodeName string) { func (s *socatMulticast) isNodeWithoutGroup(nodeName string) bool { NodeWithoutGroupMu.RLock() defer NodeWithoutGroupMu.RUnlock() - for _, node := range NodeWithoutGroup { - if node == nodeName { - return true - } - } - return false + return slices.Contains(NodeWithoutGroup, nodeName) } func (s *socatMulticast) addNotSubscribePodAddress(nodeName string, podAddress v2.NodeAddress) { diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/tests/pod.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/tests/pod.go index daa5f2caff..9e5393a703 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/tests/pod.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/tests/pod.go @@ -211,11 +211,46 @@ func (s *podToPodNoFrag) Name() string { func (s *podToPodNoFrag) Run(ctx context.Context, t *check.Test) { ct := t.Context() client := ct.RandomClientPod() + + var server check.Pod + for _, pod := range ct.EchoPods() { + // Make sure that the server pod is on another node than client + if pod.Pod.Status.HostIP != client.Pod.Status.HostIP { + server = pod + break + } + } + + t.ForEachIPFamily(func(ipFam features.IPFamily) { + mtu := s.deriveMTU(ctx, t, ipFam) + t.NewAction(s, fmt.Sprintf("ping-%s", ipFam), client, server, ipFam).Run(func(a *check.Action) { + payloadSize := mtu - HdrSizeICMPEcho + switch ipFam { + case features.IPFamilyV4: + payloadSize -= HdrSizeIPv4 + case features.IPFamilyV6: + payloadSize -= HdrSizeIPv6 + } + a.ExecInPod(ctx, t.Context().PingCommand(server, ipFam, + "-M", "do", // DF + "-s", strconv.Itoa(payloadSize), // payload size + )) + }) + + }) +} + +func (s *podToPodNoFrag) deriveMTU(ctx context.Context, t *check.Test, ipFam features.IPFamily) int { + client := t.Context().RandomClientPod() var mtu int + ipFlag := "" + if ipFam == features.IPFamilyV6 { + ipFlag = " -6" + } cmd := []string{ "/bin/sh", "-c", - "ip route show default | grep -oE 'mtu [^ ]*' | cut -d' ' -f2", + fmt.Sprintf("ip%s route show default | grep -oE 'mtu [^ ]*' | cut -d' ' -f2", ipFlag), } t.Debugf("Running %s", strings.Join(cmd, " ")) mtuBytes, err := client.K8sClient.ExecInPod(ctx, client.Pod.Namespace, @@ -247,31 +282,7 @@ func (s *podToPodNoFrag) Run(ctx context.Context, t *check.Test) { } t.Debugf("Derived MTU: %d", mtu) - var server check.Pod - for _, pod := range ct.EchoPods() { - // Make sure that the server pod is on another node than client - if pod.Pod.Status.HostIP != client.Pod.Status.HostIP { - server = pod - break - } - } - - t.ForEachIPFamily(func(ipFam features.IPFamily) { - t.NewAction(s, fmt.Sprintf("ping-%s", ipFam), client, server, ipFam).Run(func(a *check.Action) { - payloadSize := mtu - HdrSizeICMPEcho - switch ipFam { - case features.IPFamilyV4: - payloadSize -= HdrSizeIPv4 - case features.IPFamilyV6: - payloadSize -= HdrSizeIPv6 - } - a.ExecInPod(ctx, t.Context().PingCommand(server, ipFam, - "-M", "do", // DF - "-s", strconv.Itoa(payloadSize), // payload size - )) - }) - - }) + return mtu } func PodToPodMissingIPCache(opts ...Option) check.Scenario { @@ -315,6 +326,10 @@ func (s *podToPodMissingIPCache) Run(ctx context.Context, t *check.Test) { continue } matches := ipcacheGetPat.FindStringSubmatch(output.String()) + if matches == nil { + ct.Warnf(`failed to find IP cache entry: "%s"`, output.String()) + continue + } identity := matches[1] encryptkey := matches[2] tunnelendpoint := matches[3] diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/tests/upgrade.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/tests/upgrade.go index a98404fcac..f1b1458ace 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/tests/upgrade.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/tests/upgrade.go @@ -6,6 +6,7 @@ package tests import ( "context" gojson "encoding/json" + "maps" "os" "strconv" @@ -76,6 +77,22 @@ func (n *noInterruptedConnections) Run(ctx context.Context, t *check.Test) { } else { ct.Info("Skipping conn-disrupt-test for NS traffic") } + + if ct.ShouldRunConnDisruptEgressGateway() { + pods, err = client.ListPods(ctx, ct.Params().TestNamespace, metav1.ListOptions{LabelSelector: "kind=" + check.KindTestConnDisruptEgressGateway}) + if err != nil { + t.Fatalf("Unable to list test-conn-disrupt-egw pods: %s", err) + } + if len(pods.Items) == 0 { + t.Fatal("No test-conn-disrupt-{client,server} for Egress Gateway pods found") + } + + for _, pod := range pods.Items { + restartCount[pod.GetObjectMeta().GetName()] = strconv.Itoa(int(pod.Status.ContainerStatuses[0].RestartCount)) + } + } else { + ct.Info("Skipping conn-disrupt-test for Egress Gateway") + } } // Only store restart counters which will be used later when running the same @@ -88,10 +105,7 @@ func (n *noInterruptedConnections) Run(ctx context.Context, t *check.Test) { } defer file.Close() - counts := make(map[string]string) - for pod, count := range restartCount { - counts[pod] = count - } + counts := maps.Clone(restartCount) j, err := gojson.Marshal(counts) if err != nil { t.Fatalf("Failed to marshal JSON: %s", err) diff --git a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/tests/world.go b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/tests/world.go index 5438f18bc9..5dd6ff1b63 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/connectivity/tests/world.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/connectivity/tests/world.go @@ -62,25 +62,34 @@ func (s *podToWorld) Run(ctx context.Context, t *check.Test) { ct := t.Context() for _, client := range ct.ClientPods() { - // With http, over port 80. - httpOpts := s.rc.CurlOptions(http, features.IPFamilyAny, client, ct.Params()) - t.NewAction(s, fmt.Sprintf("http-to-%s-%d", extTarget, i), &client, http, features.IPFamilyAny).Run(func(a *check.Action) { - a.ExecInPod(ctx, a.CurlCommand(http, httpOpts...)) - a.ValidateFlows(ctx, client, a.GetEgressRequirements(fp)) - }) + t.ForEachIPFamily(func(ipFam features.IPFamily) { + // TODO: Reenable the test once the kernel with the bugfix is released: + // https://patchwork.kernel.org/project/netdevbpf/patch/20250318161516.3791383-1-maxim@isovalent.com/ + // and when IPv6 external connectivity starts working in the CI. + if ipFam == features.IPFamilyV6 { + return + } - // With https, over port 443. - httpsOpts := s.rc.CurlOptions(https, features.IPFamilyAny, client, ct.Params()) - t.NewAction(s, fmt.Sprintf("https-to-%s-%d", extTarget, i), &client, https, features.IPFamilyAny).Run(func(a *check.Action) { - a.ExecInPod(ctx, a.CurlCommand(https, httpsOpts...)) - a.ValidateFlows(ctx, client, a.GetEgressRequirements(fp)) - }) + // With http, over port 80. + httpOpts := s.rc.CurlOptions(http, ipFam, client, ct.Params()) + t.NewAction(s, fmt.Sprintf("http-to-%s-%s-%d", extTarget, ipFam, i), &client, http, ipFam).Run(func(a *check.Action) { + a.ExecInPod(ctx, a.CurlCommand(http, httpOpts...)) + a.ValidateFlows(ctx, client, a.GetEgressRequirements(fp)) + }) - // With https, over port 443, index.html. - httpsindexOpts := s.rc.CurlOptions(httpsindex, features.IPFamilyAny, client, ct.Params()) - t.NewAction(s, fmt.Sprintf("https-to-%s-index-%d", extTarget, i), &client, httpsindex, features.IPFamilyAny).Run(func(a *check.Action) { - a.ExecInPod(ctx, a.CurlCommand(httpsindex, httpsindexOpts...)) - a.ValidateFlows(ctx, client, a.GetEgressRequirements(fp)) + // With https, over port 443. + httpsOpts := s.rc.CurlOptions(https, ipFam, client, ct.Params()) + t.NewAction(s, fmt.Sprintf("https-to-%s-%s-%d", extTarget, ipFam, i), &client, https, ipFam).Run(func(a *check.Action) { + a.ExecInPod(ctx, a.CurlCommand(https, httpsOpts...)) + a.ValidateFlows(ctx, client, a.GetEgressRequirements(fp)) + }) + + // With https, over port 443, index.html. + httpsindexOpts := s.rc.CurlOptions(httpsindex, ipFam, client, ct.Params()) + t.NewAction(s, fmt.Sprintf("https-to-%s-index-%s-%d", extTarget, ipFam, i), &client, httpsindex, ipFam).Run(func(a *check.Action) { + a.ExecInPod(ctx, a.CurlCommand(httpsindex, httpsindexOpts...)) + a.ValidateFlows(ctx, client, a.GetEgressRequirements(fp)) + }) }) i++ @@ -117,11 +126,20 @@ func (s *podToWorld2) Run(ctx context.Context, t *check.Test) { ct := t.Context() for _, client := range ct.ClientPods() { - // With https, over port 443. - t.NewAction(s, fmt.Sprintf("https-%s-%d", extTarget, i), &client, https, features.IPFamilyAny).Run(func(a *check.Action) { - a.ExecInPod(ctx, a.CurlCommand(https)) - a.ValidateFlows(ctx, client, a.GetEgressRequirements(fp)) - a.ValidateMetrics(ctx, client, a.GetEgressMetricsRequirements()) + t.ForEachIPFamily(func(ipFam features.IPFamily) { + // TODO: Reenable the test once the kernel with the bugfix is released: + // https://patchwork.kernel.org/project/netdevbpf/patch/20250318161516.3791383-1-maxim@isovalent.com/ + // and when IPv6 external connectivity starts working in the CI. + if ipFam == features.IPFamilyV6 { + return + } + + // With https, over port 443. + t.NewAction(s, fmt.Sprintf("https-%s-%s-%d", extTarget, ipFam, i), &client, https, ipFam).Run(func(a *check.Action) { + a.ExecInPod(ctx, a.CurlCommand(https)) + a.ValidateFlows(ctx, client, a.GetEgressRequirements(fp)) + a.ValidateMetrics(ctx, client, a.GetEgressMetricsRequirements()) + }) }) i++ diff --git a/vendor/github.com/cilium/cilium/cilium-cli/encrypt/status.go b/vendor/github.com/cilium/cilium/cilium-cli/encrypt/status.go index f41b496fdf..9f49d39edb 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/encrypt/status.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/encrypt/status.go @@ -8,7 +8,7 @@ import ( "encoding/json" "errors" "fmt" - "sort" + "slices" "strconv" "strings" @@ -312,9 +312,7 @@ func printClusterStatus(cs clusterStatus, format string) error { for k := range cs.IPsecKeysInUseNodeCount { keys = append(keys, k) } - sort.Slice(keys, func(i, j int) bool { - return keys[i] < keys[j] - }) + slices.Sort(keys) keyStrs := make([]string, 0, len(keys)) for _, k := range keys { keyStrs = append(keyStrs, fmt.Sprintf("%d on %d/%d", k, cs.IPsecKeysInUseNodeCount[k], cs.TotalNodeCount)) diff --git a/vendor/github.com/cilium/cilium/cilium-cli/features/summary.go b/vendor/github.com/cilium/cilium/cilium-cli/features/summary.go index efedb8b3b4..539860c2f2 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/features/summary.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/features/summary.go @@ -23,7 +23,7 @@ import ( "github.com/cilium/cilium/api/v1/models" "github.com/cilium/cilium/cilium-cli/defaults" - "github.com/google/go-github/v68/github" + "github.com/google/go-github/v70/github" "golang.org/x/oauth2" ) diff --git a/vendor/github.com/cilium/cilium/cilium-cli/internal/helm/helm.go b/vendor/github.com/cilium/cilium/cilium-cli/internal/helm/helm.go index f560f0740f..e0190cd0a5 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/internal/helm/helm.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/internal/helm/helm.go @@ -12,6 +12,7 @@ import ( "errors" "fmt" "io/fs" + "maps" "os" "path" "regexp" @@ -38,10 +39,7 @@ var settings = cli.New() // Merge maps recursively merges the values of b into a copy of a, preferring the values from b func mergeMaps(a, b map[string]interface{}) map[string]interface{} { - out := make(map[string]interface{}, len(a)) - for k, v := range a { - out[k] = v - } + out := maps.Clone(a) for k, v := range b { if v, ok := v.(map[string]interface{}); ok { if bv, ok := out[k]; ok { diff --git a/vendor/github.com/cilium/cilium/cilium-cli/status/k8s.go b/vendor/github.com/cilium/cilium/cilium-cli/status/k8s.go index 224646455b..745cadf1b1 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/status/k8s.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/status/k8s.go @@ -26,6 +26,7 @@ import ( "github.com/cilium/cilium/api/v1/models" "github.com/cilium/cilium/cilium-cli/defaults" "github.com/cilium/cilium/cilium-cli/k8s" + logfilter "github.com/cilium/cilium/cilium-cli/utils/log" "github.com/cilium/cilium/pkg/annotation" ciliumv2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2" ) @@ -451,7 +452,7 @@ func (k *K8sStatusCollector) logComponentTask(status *Status, namespace, deploym if errLogCollection != nil { status.CollectionError(fmt.Errorf("failed to gather logs from %s:%s:%s: %w", namespace, podName, containerName, err)) } else if logs != "" { - lastLog := k.processLogs(logs) + lastLog := logfilter.Reduce(logs, k.params.Verbose) err = fmt.Errorf("container %s %s:\n%s", containerName, desc, lastLog) } } @@ -470,54 +471,6 @@ func (k *K8sStatusCollector) logComponentTask(status *Status, namespace, deploym } } -func (k *K8sStatusCollector) processLogs(logs string) string { - logs = strings.TrimSpace(logs) - if k.params.Verbose { - return logs - } - - // If the log is small, just print the whole thing. - context := 5 // lines - lines := strings.Split(logs, "\n") - if len(lines) <= context*2 { - return logs - } - - // There's a few critical things in most logs: - // - A few of the oldest lines from initial startup - // - A few of the newest lines with the final error - // - Anything marked with warning level or higher severity - truncated := false - result := lines[:context] - for i := context; i < len(lines); i++ { - // Always keep the end of the log - if i >= len(lines)-context { - result = append(result, lines[i]) - continue - } - - // Keep serious-looking logs - switch { - case strings.Contains(lines[i], "level=warn"): - result = append(result, lines[i]) - truncated = false - case strings.Contains(lines[i], "level=err"): - result = append(result, lines[i]) - truncated = false - case strings.Contains(lines[i], "level=fatal"): - result = append(result, lines[i]) - truncated = false - default: - if !truncated { - result = append(result, "<...>") - truncated = true - } - } - } - - return strings.Join(result, "\n") -} - func (k *K8sStatusCollector) status(ctx context.Context, cancel context.CancelFunc) *Status { status := newStatus() tasks := []statusTask{ diff --git a/vendor/github.com/cilium/cilium/cilium-cli/utils/codeowners/codeowners.go b/vendor/github.com/cilium/cilium/cilium-cli/utils/codeowners/codeowners.go new file mode 100644 index 0000000000..b8a18af7a1 --- /dev/null +++ b/vendor/github.com/cilium/cilium/cilium-cli/utils/codeowners/codeowners.go @@ -0,0 +1,42 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Authors of Cilium + +package codeowners + +import ( + "fmt" + "os" + + "github.com/hmarr/codeowners" +) + +type Ruleset codeowners.Ruleset + +func Load(paths []string) (codeowners.Ruleset, error) { + if len(paths) == 0 { + owners, err := codeowners.LoadFileFromStandardLocation() + if err != nil { + return nil, fmt.Errorf("while loading: %w", err) + } + return owners, nil + } + + var allOwners codeowners.Ruleset + + for _, f := range paths { + coFile, err := os.Open(f) + if err != nil { + return nil, fmt.Errorf("while opening %s: %w", f, err) + } + defer coFile.Close() + + owners, err := codeowners.ParseFile(coFile) + if err != nil { + return nil, fmt.Errorf("while parsing %s: %w", f, err) + } + + allOwners = append(allOwners, owners...) + } + + return allOwners, nil +} diff --git a/vendor/github.com/cilium/cilium/cilium-cli/utils/features/features.go b/vendor/github.com/cilium/cilium/cilium-cli/utils/features/features.go index 9100fd5148..2560637f77 100644 --- a/vendor/github.com/cilium/cilium/cilium-cli/utils/features/features.go +++ b/vendor/github.com/cilium/cilium/cilium-cli/utils/features/features.go @@ -407,9 +407,9 @@ func (fs Set) ExtractFromConfigMap(cm *v1.ConfigMap) { } } -func (fs Set) ExtractFromNodes(perf bool, nodesWithoutCilium map[string]struct{}) { +func (fs Set) ExtractFromNodes(nodesWithoutCilium map[string]struct{}) { fs[NodeWithoutCilium] = Status{ - Enabled: !perf && len(nodesWithoutCilium) != 0, + Enabled: len(nodesWithoutCilium) != 0, Mode: strings.Join(slices.Collect(maps.Keys(nodesWithoutCilium)), ","), } } diff --git a/vendor/github.com/cilium/cilium/cilium-cli/utils/log/log.go b/vendor/github.com/cilium/cilium/cilium-cli/utils/log/log.go new file mode 100644 index 0000000000..e887b88b0f --- /dev/null +++ b/vendor/github.com/cilium/cilium/cilium-cli/utils/log/log.go @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Authors of Cilium + +package log + +import "strings" + +// Reduce function filters log messages +// and returns only valuable data (warn, err, fatal) +// in case of verbose all the messages will be returned +func Reduce(logs string, verbose bool) string { + logs = strings.TrimSpace(logs) + if verbose { + return logs + } + + // If the log is small, just print the whole thing. + context := 5 // lines + lines := strings.Split(logs, "\n") + if len(lines) <= context*2 { + return logs + } + + // There's a few critical things in most logs: + // - A few of the oldest lines from initial startup + // - A few of the newest lines with the final error + // - Anything marked with warning level or higher severity + truncated := false + result := lines[:context] + for i := context; i < len(lines); i++ { + // Always keep the end of the log + if i >= len(lines)-context { + result = append(result, lines[i]) + continue + } + + // Keep serious-looking logs + switch { + case strings.Contains(lines[i], "level=warn"): + result = append(result, lines[i]) + truncated = false + case strings.Contains(lines[i], "level=err"): + result = append(result, lines[i]) + truncated = false + case strings.Contains(lines[i], "level=fatal"): + result = append(result, lines[i]) + truncated = false + default: + if !truncated { + result = append(result, "<...>") + truncated = true + } + } + } + + return strings.Join(result, "\n") +} diff --git a/vendor/github.com/cilium/cilium/daemon/k8s/init.go b/vendor/github.com/cilium/cilium/daemon/k8s/init.go index 9654a44602..d0b86fadb9 100644 --- a/vendor/github.com/cilium/cilium/daemon/k8s/init.go +++ b/vendor/github.com/cilium/cilium/daemon/k8s/init.go @@ -15,6 +15,7 @@ import ( "github.com/cilium/cilium/pkg/k8s" k8sConst "github.com/cilium/cilium/pkg/k8s/constants" "github.com/cilium/cilium/pkg/k8s/resource" + "github.com/cilium/cilium/pkg/logging" "github.com/cilium/cilium/pkg/logging/logfields" "github.com/cilium/cilium/pkg/node" nodeTypes "github.com/cilium/cilium/pkg/node/types" @@ -61,7 +62,7 @@ func retrieveNodeInformation(ctx context.Context, log logrus.FieldLogger, localN break } if event.Kind == resource.Upsert { - n = k8s.ParseNode(event.Object, source.Unspec) + n = k8s.ParseNode(logging.DefaultSlogLogger, event.Object, source.Unspec) log.WithField(logfields.NodeName, n.Name).Info("Retrieved node information from kubernetes node") if err := waitForCIDR(); err != nil { log.WithError(err).Warning("Waiting for k8s node information") diff --git a/vendor/github.com/cilium/cilium/netlify.toml b/vendor/github.com/cilium/cilium/netlify.toml deleted file mode 100644 index ce6d7f8250..0000000000 --- a/vendor/github.com/cilium/cilium/netlify.toml +++ /dev/null @@ -1,4 +0,0 @@ -[build] - base = "Documentation/" - publish = "_build/html" - command = "make html-netlify" diff --git a/vendor/github.com/cilium/cilium/operator/option/config.go b/vendor/github.com/cilium/cilium/operator/option/config.go index 1ef32dda82..f2994de57f 100644 --- a/vendor/github.com/cilium/cilium/operator/option/config.go +++ b/vendor/github.com/cilium/cilium/operator/option/config.go @@ -95,11 +95,6 @@ const ( // AWS options - // AWSInstanceLimitMapping allows overwirting AWS instance limits defined in - // pkg/aws/eni/limits.go - // e.g. {"a1.medium": "2,4,4", "a2.custom2": "4,5,6"} - AWSInstanceLimitMapping = "aws-instance-limit-mapping" - // AWSReleaseExcessIPs allows releasing excess free IP addresses from ENI. // Enabling this option reduces waste of IP addresses but may increase // the number of API calls to AWS EC2 service. @@ -129,10 +124,6 @@ const ( // ParallelAllocWorkers specifies the number of parallel workers to be used for IPAM allocation ParallelAllocWorkers = "parallel-alloc-workers" - // UpdateEC2AdapterLimitViaAPI configures the operator to use the EC2 - // API to fill out the instancetype to adapter limit mapping. - UpdateEC2AdapterLimitViaAPI = "update-ec2-adapter-limit-via-api" - // EC2APIEndpoint is the custom API endpoint to use for the EC2 AWS service, // e.g. "ec2-fips.us-west-1.amazonaws.com" to use a FIPS endpoint in the us-west-1 region. EC2APIEndpoint = "ec2-api-endpoint" @@ -321,11 +312,6 @@ type OperatorConfig struct { // ParallelAllocWorkers specifies the number of parallel workers to be used for accessing cloud provider APIs . ParallelAllocWorkers int64 - // AWSInstanceLimitMapping allows overwriting AWS instance limits defined in - // pkg/aws/eni/limits.go - // e.g. {"a1.medium": "2,4,4", "a2.custom2": "4,5,6"} - AWSInstanceLimitMapping map[string]string - // AWSReleaseExcessIps allows releasing excess free IP addresses from ENI. // Enabling this option reduces waste of IP addresses but may increase // the number of API calls to AWS EC2 service. @@ -339,10 +325,6 @@ type OperatorConfig struct { // node AWSUsePrimaryAddress bool - // UpdateEC2AdapterLimitViaAPI configures the operator to use the EC2 API to fill out the - // instancetype to adapter limit mapping. - UpdateEC2AdapterLimitViaAPI bool - // ExcessIPReleaseDelay controls how long operator would wait before an IP previously marked as excess is released. // Defaults to 180 secs ExcessIPReleaseDelay int @@ -469,7 +451,6 @@ func (c *OperatorConfig) Populate(vp *viper.Viper) { c.AWSReleaseExcessIPs = vp.GetBool(AWSReleaseExcessIPs) c.AWSEnablePrefixDelegation = vp.GetBool(AWSEnablePrefixDelegation) c.AWSUsePrimaryAddress = vp.GetBool(AWSUsePrimaryAddress) - c.UpdateEC2AdapterLimitViaAPI = vp.GetBool(UpdateEC2AdapterLimitViaAPI) c.EC2APIEndpoint = vp.GetString(EC2APIEndpoint) c.ExcessIPReleaseDelay = vp.GetInt(ExcessIPReleaseDelay) c.ENIGarbageCollectionInterval = vp.GetDuration(ENIGarbageCollectionInterval) @@ -504,12 +485,6 @@ func (c *OperatorConfig) Populate(vp *viper.Viper) { c.IPAMInstanceTags = m } - if m, err := command.GetStringMapStringE(vp, AWSInstanceLimitMapping); err != nil { - log.Fatalf("unable to parse %s: %s", AWSInstanceLimitMapping, err) - } else { - c.AWSInstanceLimitMapping = m - } - if m, err := command.GetStringMapStringE(vp, ENITags); err != nil { log.Fatalf("unable to parse %s: %s", ENITags, err) } else { @@ -535,7 +510,6 @@ var Config = &OperatorConfig{ IPAMSubnetsTags: make(map[string]string), IPAMInstanceTags: make(map[string]string), IPAMAutoCreateCiliumPodIPPools: make(map[string]string), - AWSInstanceLimitMapping: make(map[string]string), ENITags: make(map[string]string), ENIGarbageCollectionTags: make(map[string]string), } diff --git a/vendor/github.com/cilium/cilium/pkg/allocator/allocator.go b/vendor/github.com/cilium/cilium/pkg/allocator/allocator.go index aab22b52bd..4f88edeffd 100644 --- a/vendor/github.com/cilium/cilium/pkg/allocator/allocator.go +++ b/vendor/github.com/cilium/cilium/pkg/allocator/allocator.go @@ -10,7 +10,6 @@ import ( "log/slog" "github.com/google/uuid" - "github.com/sirupsen/logrus" "github.com/cilium/cilium/pkg/backoff" "github.com/cilium/cilium/pkg/idpool" @@ -315,7 +314,7 @@ func NewAllocator(rootLogger *slog.Logger, typ AllocatorKey, backend Backend, op backend: backend, min: idpool.ID(1), max: idpool.ID(^uint64(0)), - localKeys: newLocalKeys(), + localKeys: newLocalKeys(rootLogger), stopGC: make(chan struct{}), suffix: uuid.New().String()[:10], remoteCaches: map[string]*remoteCache{}, @@ -527,7 +526,7 @@ type AllocatorKey interface { func (a *Allocator) lockedAllocate(ctx context.Context, key AllocatorKey) (idpool.ID, bool, bool, error) { var firstUse bool - kvstore.Trace("Allocating key in kvstore", nil, logrus.Fields{fieldKey: key}) + kvstore.Trace(a.logger, "Allocating key in kvstore", fieldKey, key) k := key.GetKey() lock, err := a.backend.Lock(ctx, key) @@ -544,7 +543,7 @@ func (a *Allocator) lockedAllocate(ctx context.Context, key AllocatorKey) (idpoo return 0, false, false, err } - kvstore.Trace("kvstore state is: ", nil, logrus.Fields{fieldID: value}) + kvstore.Trace(a.logger, "kvstore state is: ", fieldID, value) a.slaveKeysMutex.Lock() defer a.slaveKeysMutex.Unlock() @@ -595,7 +594,7 @@ func (a *Allocator) lockedAllocate(ctx context.Context, key AllocatorKey) (idpoo return 0, false, false, fmt.Errorf("no more available IDs in configured space") } - kvstore.Trace("Selected available key ID", nil, logrus.Fields{fieldID: id}) + kvstore.Trace(a.logger, "Selected available key ID", fieldID, id) releaseKeyAndID := func() { a.localKeys.release(k) @@ -695,7 +694,7 @@ func (a *Allocator) Allocate(ctx context.Context, key AllocatorKey) (idpool.ID, return id, false, false, err } - kvstore.Trace("Allocating from kvstore", nil, logrus.Fields{fieldKey: key}) + kvstore.Trace(a.logger, "Allocating from kvstore", fieldKey, key) // make a copy of the template and customize it boff := a.backoffTemplate @@ -710,7 +709,10 @@ func (a *Allocator) Allocate(ctx context.Context, key AllocatorKey) (idpool.ID, // execution thread. It does not hurt to check if localKeys contains a // reference for the key that we are attempting to allocate. if val := a.localKeys.use(key.GetKey()); val != idpool.NoID { - kvstore.Trace("Reusing local id", nil, logrus.Fields{fieldID: val, fieldKey: key}) + kvstore.Trace(a.logger, "Reusing local id", + fieldID, val, + fieldKey, key, + ) a.mainCache.insert(key, val) return val, false, false, nil } @@ -742,7 +744,10 @@ func (a *Allocator) Allocate(ctx context.Context, key AllocatorKey) (idpool.ID, ) } - kvstore.Trace("Allocation attempt failed", err, logrus.Fields{fieldKey: key, logfields.Attempt: attempt}) + kvstore.Trace(a.logger, "Allocation attempt failed", + fieldKey, key, + logfields.Attempt, attempt, + ) if waitErr := boff.Wait(ctx); waitErr != nil { return 0, false, false, waitErr diff --git a/vendor/github.com/cilium/cilium/pkg/allocator/localkeys.go b/vendor/github.com/cilium/cilium/pkg/allocator/localkeys.go index 48820d7366..54cc5e6928 100644 --- a/vendor/github.com/cilium/cilium/pkg/allocator/localkeys.go +++ b/vendor/github.com/cilium/cilium/pkg/allocator/localkeys.go @@ -5,8 +5,7 @@ package allocator import ( "fmt" - - "github.com/sirupsen/logrus" + "log/slog" "github.com/cilium/cilium/pkg/idpool" "github.com/cilium/cilium/pkg/kvstore" @@ -25,15 +24,17 @@ type localKey struct { // localKeys is a map of keys in use locally. Keys can be used multiple times. // A refcnt is managed to know when a key is no longer in use type localKeys struct { + logger *slog.Logger lock.RWMutex keys map[string]*localKey ids map[idpool.ID]*localKey } -func newLocalKeys() *localKeys { +func newLocalKeys(logger *slog.Logger) *localKeys { return &localKeys{ - keys: map[string]*localKey{}, - ids: map[idpool.ID]*localKey{}, + logger: logger, + keys: map[string]*localKey{}, + ids: map[idpool.ID]*localKey{}, } } @@ -52,7 +53,11 @@ func (lk *localKeys) allocate(keyString string, key AllocatorKey, val idpool.ID) } k.refcnt++ - kvstore.Trace("Incremented local key refcnt", nil, logrus.Fields{fieldKey: keyString, fieldID: val, fieldRefCnt: k.refcnt}) + kvstore.Trace(lk.logger, "Incremented local key refcnt", + fieldKey, keyString, + fieldID, val, + fieldRefCnt, k.refcnt, + ) return k.val, firstUse, nil } @@ -60,7 +65,11 @@ func (lk *localKeys) allocate(keyString string, key AllocatorKey, val idpool.ID) k := &localKey{key: key, val: val, refcnt: 1} lk.keys[keyString] = k lk.ids[val] = k - kvstore.Trace("New local key", nil, logrus.Fields{fieldKey: keyString, fieldID: val, fieldRefCnt: 1}) + kvstore.Trace(lk.logger, "New local key", + fieldKey, keyString, + fieldID, val, + fieldRefCnt, 1, + ) return val, firstUse, nil } @@ -70,7 +79,9 @@ func (lk *localKeys) verify(key string) error { if k, ok := lk.keys[key]; ok { k.verified = true - kvstore.Trace("Local key verified", nil, logrus.Fields{fieldKey: key}) + kvstore.Trace(lk.logger, "Local key verified", + fieldKey, key, + ) return nil } @@ -114,7 +125,11 @@ func (lk *localKeys) use(key string) idpool.ID { } k.refcnt++ - kvstore.Trace("Incremented local key refcnt", nil, logrus.Fields{fieldKey: key, fieldID: k.val, fieldRefCnt: k.refcnt}) + kvstore.Trace(lk.logger, "Incremented local key refcnt", + fieldKey, key, + fieldID, k.val, + fieldRefCnt, k.refcnt, + ) return k.val } @@ -129,7 +144,11 @@ func (lk *localKeys) release(key string) (lastUse bool, id idpool.ID, err error) defer lk.Unlock() if k, ok := lk.keys[key]; ok { k.refcnt-- - kvstore.Trace("Decremented local key refcnt", nil, logrus.Fields{fieldKey: key, fieldID: k.val, fieldRefCnt: k.refcnt}) + kvstore.Trace(lk.logger, "Decremented local key refcnt", + fieldKey, key, + fieldID, k.val, + fieldRefCnt, k.refcnt, + ) if k.refcnt == 0 { delete(lk.keys, key) delete(lk.ids, k.val) diff --git a/vendor/github.com/cilium/cilium/pkg/client/client.go b/vendor/github.com/cilium/cilium/pkg/client/client.go index fef6a84f44..e168e280eb 100644 --- a/vendor/github.com/cilium/cilium/pkg/client/client.go +++ b/vendor/github.com/cilium/cilium/pkg/client/client.go @@ -535,8 +535,6 @@ func FormatStatusResponse(w io.Writer, sr *models.StatusResponse, sd StatusDetai status = "BPF" } if sr.KubeProxyReplacement != nil { - // When BPF Masquerading is enabled we don't do any masquerading for IPv6 - // traffic so no SNAT Exclusion IPv6 CIDR is listed in status output. devStr := "" for i, dev := range sr.KubeProxyReplacement.DeviceList { devStr += dev.Name @@ -544,9 +542,12 @@ func FormatStatusResponse(w io.Writer, sr *models.StatusResponse, sd StatusDetai devStr += ", " } } - status += fmt.Sprintf("\t[%s]\t%s", + status += fmt.Sprintf( + "\t[%s]\t%s %s", devStr, - sr.Masquerading.SnatExclusionCidrV4) + sr.Masquerading.SnatExclusionCidrV4, + sr.Masquerading.SnatExclusionCidrV6, + ) } } else if sr.Masquerading.Mode == models.MasqueradingModeIptables { diff --git a/vendor/github.com/cilium/cilium/pkg/client/config.go b/vendor/github.com/cilium/cilium/pkg/client/config.go index 3775abe2ee..1734eded0b 100644 --- a/vendor/github.com/cilium/cilium/pkg/client/config.go +++ b/vendor/github.com/cilium/cilium/pkg/client/config.go @@ -4,6 +4,8 @@ package client import ( + "maps" + "github.com/cilium/cilium/api/v1/client/daemon" "github.com/cilium/cilium/api/v1/models" "github.com/cilium/cilium/pkg/api" @@ -25,9 +27,7 @@ func (c *Client) ConfigPatch(cfg models.DaemonConfigurationSpec) error { return err } - for opt, value := range cfg.Options { - fullCfg.Spec.Options[opt] = value - } + maps.Copy(fullCfg.Spec.Options, cfg.Options) if cfg.PolicyEnforcement != "" { fullCfg.Spec.PolicyEnforcement = cfg.PolicyEnforcement } diff --git a/vendor/github.com/cilium/cilium/pkg/comparator/comparator.go b/vendor/github.com/cilium/cilium/pkg/comparator/comparator.go index 49ccc8dd1e..03b4985c8c 100644 --- a/vendor/github.com/cilium/cilium/pkg/comparator/comparator.go +++ b/vendor/github.com/cilium/cilium/pkg/comparator/comparator.go @@ -3,6 +3,8 @@ package comparator +import "slices" + // MapStringEqualsIgnoreKeys returns true if both maps have the same values for // the keys that are not present in the 'ignoreKeys'. func MapStringEqualsIgnoreKeys(m1, m2 map[string]string, ignoreKeys []string) bool { @@ -16,11 +18,8 @@ func MapStringEqualsIgnoreKeys(m1, m2 map[string]string, ignoreKeys []string) bo ignoredM1 := 0 for k1, v1 := range m1 { var ignore bool - for _, ig := range ignoreKeys { - if k1 == ig { - ignore = true - break - } + if slices.Contains(ignoreKeys, k1) { + ignore = true } if ignore { ignoredM1++ diff --git a/vendor/github.com/cilium/cilium/pkg/container/versioned/value.go b/vendor/github.com/cilium/cilium/pkg/container/versioned/value.go index 79aa63e3f3..dd2940ced0 100644 --- a/vendor/github.com/cilium/cilium/pkg/container/versioned/value.go +++ b/vendor/github.com/cilium/cilium/pkg/container/versioned/value.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "iter" + "log/slog" "math" "runtime" "slices" @@ -14,7 +15,6 @@ import ( "sync/atomic" "github.com/hashicorp/go-hclog" - "github.com/sirupsen/logrus" "github.com/cilium/cilium/pkg/lock" "github.com/cilium/cilium/pkg/logging/logfields" @@ -103,9 +103,9 @@ func versionHandleFinalizer(h *VersionHandle) { if coordinator != nil && coordinator.Logger != nil { logger := coordinator.Logger if h.stacktrace != "" { - logger = logger.WithField(logfields.Stacktrace, h.stacktrace) + logger = logger.With(logfields.Stacktrace, h.stacktrace) } - logger.WithField(logfields.Version, h.version).Error("Handle for version not closed.") + logger.Error("Handle for version not closed.", logfields.Version, h.version) } h.Close() } @@ -159,7 +159,7 @@ type versionCount struct { // should be cleaned by the 'cleaner' function given to the Coordinator. type Coordinator struct { // Logger supplied to NewCoordinator. Should be set if logging is desired. - Logger *logrus.Entry + Logger *slog.Logger // Cleaner is called with the earliest version that must be kept. // Must be set to clean up resources held for old versions. @@ -270,10 +270,11 @@ func (v *Coordinator) releaseVersion(version version) error { n, found := slices.BinarySearchFunc(v.versions, version, versionHandleCmp) if !found { if v.Logger != nil { - v.Logger.WithFields(logrus.Fields{ - logfields.Version: version, - logfields.Stacktrace: hclog.Stacktrace(), - }).Error("Version not found.") + v.Logger.Error( + "Version not found.", + logfields.Version, version, + logfields.Stacktrace, hclog.Stacktrace(), + ) } return ErrVersionNotFound } @@ -303,15 +304,16 @@ func (v *Coordinator) clean() { // The cleaner is called from a goroutine without holding any locks if v.Cleaner != nil { if v.Logger != nil { - v.Logger.WithFields(logrus.Fields{ - logfields.OldVersion: v.oldestVersion, - logfields.NewVersion: keepVersion, - }).Debug("releaseVersion: calling cleaner") + v.Logger.Debug( + "releaseVersion: calling cleaner", + logfields.OldVersion, v.oldestVersion, + logfields.NewVersion, keepVersion, + ) } go v.Cleaner(KeepVersion(keepVersion)) v.oldestVersion = keepVersion } else if v.Logger != nil { - v.Logger.Warnf("VersionHandle.Close: Cleaner function not set") + v.Logger.Warn("VersionHandle.Close: Cleaner function not set") } } } @@ -333,11 +335,12 @@ func (v *Coordinator) getVersionHandleLocked(version version) *VersionHandle { oldVersion := version version = v.oldestVersion if v.Logger != nil { - v.Logger.WithFields(logrus.Fields{ - logfields.Stacktrace: hclog.Stacktrace(), - logfields.Version: version, - logfields.OldVersion: oldVersion, - }).Warn("GetVersionHandle: Handle to a stale version requested, returning oldest valid version instead") + v.Logger.Warn( + "GetVersionHandle: Handle to a stale version requested, returning oldest valid version instead", + logfields.Stacktrace, hclog.Stacktrace(), + logfields.Version, version, + logfields.OldVersion, oldVersion, + ) } } n, found := slices.BinarySearchFunc(v.versions, version, versionHandleCmp) diff --git a/vendor/github.com/cilium/cilium/pkg/controller/manager.go b/vendor/github.com/cilium/cilium/pkg/controller/manager.go index cd9a2c73ad..a34e4a79dd 100644 --- a/vendor/github.com/cilium/cilium/pkg/controller/manager.go +++ b/vendor/github.com/cilium/cilium/pkg/controller/manager.go @@ -6,6 +6,7 @@ package controller import ( "context" "fmt" + "maps" "github.com/go-openapi/strfmt" "github.com/google/uuid" @@ -70,14 +71,18 @@ func (m *Manager) updateController(name string, params ControllerParams) *manage ctrl := m.lookupLocked(name) if ctrl != nil { + ctrl.mutex.Lock() ctrl.getLogger().Debug("Updating existing controller") ctrl.updateParamsLocked(params) + ctrl.mutex.Unlock() + ctrl.mutex.RLock() // Notify the goroutine of the params update. select { case ctrl.update <- ctrl.params: default: } + ctrl.mutex.RUnlock() ctrl.getLogger().Debug("Controller update time: ", time.Since(start)) } else { @@ -229,9 +234,7 @@ func (m *Manager) GetStatusModel() models.ControllerStatuses { // manager mutex quickly again controllers := controllerMap{} m.mutex.RLock() - for key, c := range m.controllers { - controllers[key] = c - } + maps.Copy(controllers, m.controllers) m.mutex.RUnlock() statuses := models.ControllerStatuses{} diff --git a/vendor/github.com/cilium/cilium/pkg/counter/counter.go b/vendor/github.com/cilium/cilium/pkg/counter/counter.go index 7b154ffe6c..7925dfa27f 100644 --- a/vendor/github.com/cilium/cilium/pkg/counter/counter.go +++ b/vendor/github.com/cilium/cilium/pkg/counter/counter.go @@ -3,6 +3,8 @@ package counter +import "maps" + // Counter tracks references for comparable . // // No thread safety is provided within this structure, the user is expected to @@ -31,9 +33,7 @@ func (c Counter[T]) Delete(key T) bool { // DeepCopy makes a new copy of the received Counter. func (c Counter[T]) DeepCopy() Counter[T] { result := make(Counter[T], len(c)) - for k, v := range c { - result[k] = v - } + maps.Copy(result, c) return result } diff --git a/vendor/github.com/cilium/cilium/pkg/crypto/certificatemanager/certificate_manager_mock.go b/vendor/github.com/cilium/cilium/pkg/crypto/certificatemanager/certificate_manager_mock.go new file mode 100644 index 0000000000..723dc21d3b --- /dev/null +++ b/vendor/github.com/cilium/cilium/pkg/crypto/certificatemanager/certificate_manager_mock.go @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Authors of Cilium + +package certificatemanager + +import ( + "context" + "errors" + + "github.com/cilium/cilium/pkg/policy/api" +) + +func NewMockSecretManagerInline() SecretManager { + return &mockSecretManager{ + inlineValue: "somevalue", + } +} + +func NewMockSecretManagerNotFound() SecretManager { + return &mockSecretManager{ + inlineError: errors.New("not found"), + } +} + +func NewMockSecretManagerSDS() SecretManager { + return &mockSecretManager{ + isSDS: true, + } +} + +type mockSecretManager struct { + inlineValue string + inlineError error + isSDS bool +} + +func (m mockSecretManager) GetSecretString(_ context.Context, secret *api.Secret, ns string) (string, error) { + return m.inlineValue, m.inlineError +} + +func (m mockSecretManager) PolicySecretSyncEnabled() bool { + return m.isSDS +} + +func (m mockSecretManager) SecretsOnlyFromSecretsNamespace() bool { + return m.isSDS +} + +func (m mockSecretManager) GetSecretSyncNamespace() string { + return "" +} diff --git a/vendor/github.com/cilium/cilium/pkg/debug/subsystem.go b/vendor/github.com/cilium/cilium/pkg/debug/subsystem.go index 62162e3648..9013cf1487 100644 --- a/vendor/github.com/cilium/cilium/pkg/debug/subsystem.go +++ b/vendor/github.com/cilium/cilium/pkg/debug/subsystem.go @@ -5,6 +5,7 @@ package debug import ( "fmt" + "maps" "github.com/cilium/cilium/pkg/lock" ) @@ -56,13 +57,9 @@ func (s *statusFunctions) registerStatusObject(name string, obj StatusObject) er } func (s *statusFunctions) collectStatus() StatusMap { - fnCopy := functionMap{} - // Make a copy to not hold the mutex while collecting the status s.mutex.RLock() - for name, fn := range s.functions { - fnCopy[name] = fn - } + fnCopy := maps.Clone(s.functions) s.mutex.RUnlock() status := StatusMap{} diff --git a/vendor/github.com/cilium/cilium/pkg/defaults/defaults.go b/vendor/github.com/cilium/cilium/pkg/defaults/defaults.go index 03d325e6c2..203340db39 100644 --- a/vendor/github.com/cilium/cilium/pkg/defaults/defaults.go +++ b/vendor/github.com/cilium/cilium/pkg/defaults/defaults.go @@ -313,10 +313,6 @@ const ( // a kvstore path for too long. KVStoreStaleLockTimeout = 30 * time.Second - // KVstorePodNetworkSupport represents whether to enable the support for - // running the Cilium KVstore in pod network. - KVstorePodNetworkSupport = false - // KVstoreQPS is default rate limit for kv store operations KVstoreQPS = 20 @@ -414,6 +410,9 @@ const ( // per GC interval ENIGarbageCollectionMaxPerInterval = 25 + // ENIMaxResultsPerApiCall is the maximum number of ENI objects to fetch per DescribeNetworkInterfaces API call + ENIMaxResultsPerApiCall = 1000 + // ParallelAllocWorkers is the default max number of parallel workers doing allocation in the operator ParallelAllocWorkers = 50 diff --git a/vendor/github.com/cilium/cilium/pkg/envoy/policy/envoy_l7_rules_translator.go b/vendor/github.com/cilium/cilium/pkg/envoy/policy/envoy_l7_rules_translator.go new file mode 100644 index 0000000000..a8be568085 --- /dev/null +++ b/vendor/github.com/cilium/cilium/pkg/envoy/policy/envoy_l7_rules_translator.go @@ -0,0 +1,274 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Authors of Cilium + +package envoypolicy + +import ( + "context" + "fmt" + "log/slog" + "strings" + + cilium "github.com/cilium/proxy/go/cilium/api" + envoy_config_route "github.com/cilium/proxy/go/envoy/config/route/v3" + envoy_type_matcher "github.com/cilium/proxy/go/envoy/type/matcher/v3" + "k8s.io/apimachinery/pkg/types" + + "github.com/cilium/cilium/pkg/crypto/certificatemanager" + "github.com/cilium/cilium/pkg/logging/logfields" + policyapi "github.com/cilium/cilium/pkg/policy/api" +) + +type EnvoyL7RulesTranslator interface { + GetEnvoyHTTPRules(l7Rules *policyapi.L7Rules, ns string) (*cilium.HttpNetworkPolicyRules, bool) +} + +type envoyL7RulesTranslator struct { + logger *slog.Logger + secretManager certificatemanager.SecretManager +} + +func NewEnvoyL7RulesTranslator(logger *slog.Logger, secretManager certificatemanager.SecretManager) EnvoyL7RulesTranslator { + return &envoyL7RulesTranslator{ + logger: logger, + secretManager: secretManager, + } +} + +func (r *envoyL7RulesTranslator) GetEnvoyHTTPRules(l7Rules *policyapi.L7Rules, ns string) (*cilium.HttpNetworkPolicyRules, bool) { + if len(l7Rules.HTTP) > 0 { // Just cautious. This should never be false. + // Assume none of the rules have side-effects so that rule evaluation can + // be stopped as soon as the first allowing rule is found. 'canShortCircuit' + // is set to 'false' below if any rules with side effects are encountered, + // causing all the applicable rules to be evaluated instead. + canShortCircuit := true + httpRules := make([]*cilium.HttpNetworkPolicyRule, 0, len(l7Rules.HTTP)) + for _, l7 := range l7Rules.HTTP { + rule, cs := r.getHTTPRule(&l7, ns) + httpRules = append(httpRules, rule) + if !cs { + canShortCircuit = false + } + } + SortHTTPNetworkPolicyRules(httpRules) + return &cilium.HttpNetworkPolicyRules{ + HttpRules: httpRules, + }, canShortCircuit + } + + return nil, true +} + +func (r *envoyL7RulesTranslator) getHTTPRule(h *policyapi.PortRuleHTTP, ns string) (*cilium.HttpNetworkPolicyRule, bool) { + // Count the number of header matches we need + cnt := len(h.Headers) + len(h.HeaderMatches) + if h.Path != "" { + cnt++ + } + if h.Method != "" { + cnt++ + } + if h.Host != "" { + cnt++ + } + + headers := make([]*envoy_config_route.HeaderMatcher, 0, cnt) + if h.Path != "" { + headers = append(headers, &envoy_config_route.HeaderMatcher{ + Name: ":path", + HeaderMatchSpecifier: &envoy_config_route.HeaderMatcher_StringMatch{ + StringMatch: &envoy_type_matcher.StringMatcher{ + MatchPattern: &envoy_type_matcher.StringMatcher_SafeRegex{ + SafeRegex: &envoy_type_matcher.RegexMatcher{ + Regex: h.Path, + }, + }, + }, + }, + }) + } + if h.Method != "" { + headers = append(headers, &envoy_config_route.HeaderMatcher{ + Name: ":method", + HeaderMatchSpecifier: &envoy_config_route.HeaderMatcher_StringMatch{ + StringMatch: &envoy_type_matcher.StringMatcher{ + MatchPattern: &envoy_type_matcher.StringMatcher_SafeRegex{ + SafeRegex: &envoy_type_matcher.RegexMatcher{ + Regex: h.Method, + }, + }, + }, + }, + }) + } + if h.Host != "" { + headers = append(headers, &envoy_config_route.HeaderMatcher{ + Name: ":authority", + HeaderMatchSpecifier: &envoy_config_route.HeaderMatcher_StringMatch{ + StringMatch: &envoy_type_matcher.StringMatcher{ + MatchPattern: &envoy_type_matcher.StringMatcher_SafeRegex{ + SafeRegex: &envoy_type_matcher.RegexMatcher{ + Regex: h.Host, + }, + }, + }, + }, + }) + } + for _, hdr := range h.Headers { + strs := strings.SplitN(hdr, " ", 2) + if len(strs) == 2 { + // Remove ':' in "X-Key: true" + key := strings.TrimRight(strs[0], ":") + // Header presence and matching (literal) value needed. + headers = append(headers, &envoy_config_route.HeaderMatcher{ + Name: key, + HeaderMatchSpecifier: &envoy_config_route.HeaderMatcher_StringMatch{ + StringMatch: &envoy_type_matcher.StringMatcher{ + MatchPattern: &envoy_type_matcher.StringMatcher_Exact{ + Exact: strs[1], + }, + }, + }, + }) + } else { + // Only header presence needed + headers = append(headers, &envoy_config_route.HeaderMatcher{ + Name: strs[0], + HeaderMatchSpecifier: &envoy_config_route.HeaderMatcher_PresentMatch{PresentMatch: true}, + }) + } + } + + headerMatches := make([]*cilium.HeaderMatch, 0, len(h.HeaderMatches)) + for _, hdr := range h.HeaderMatches { + var mismatch_action cilium.HeaderMatch_MismatchAction + switch hdr.Mismatch { + case policyapi.MismatchActionLog: + mismatch_action = cilium.HeaderMatch_CONTINUE_ON_MISMATCH + case policyapi.MismatchActionAdd: + mismatch_action = cilium.HeaderMatch_ADD_ON_MISMATCH + case policyapi.MismatchActionDelete: + mismatch_action = cilium.HeaderMatch_DELETE_ON_MISMATCH + case policyapi.MismatchActionReplace: + mismatch_action = cilium.HeaderMatch_REPLACE_ON_MISMATCH + default: + mismatch_action = cilium.HeaderMatch_FAIL_ON_MISMATCH + } + // Fetch the secret + value, err := r.getSecretString(hdr, ns) + if err != nil { + r.logger.Warn("Failed fetching K8s Secret, header match will fail", logfields.Error, err) + // Envoy treats an empty exact match value as matching ANY value; adding + // InvertMatch: true here will cause this rule to NEVER match. + headers = append(headers, &envoy_config_route.HeaderMatcher{ + Name: hdr.Name, + HeaderMatchSpecifier: &envoy_config_route.HeaderMatcher_StringMatch{ + StringMatch: &envoy_type_matcher.StringMatcher{ + MatchPattern: &envoy_type_matcher.StringMatcher_Exact{ + Exact: "", + }, + }, + }, + InvertMatch: true, + }) + } else if value != "" { + // Inline value provided. + // Header presence and matching (literal) value needed. + if mismatch_action == cilium.HeaderMatch_FAIL_ON_MISMATCH { + // fail on mismatch gets converted for regular HeaderMatcher + headers = append(headers, &envoy_config_route.HeaderMatcher{ + Name: hdr.Name, + HeaderMatchSpecifier: &envoy_config_route.HeaderMatcher_StringMatch{ + StringMatch: &envoy_type_matcher.StringMatcher{ + MatchPattern: &envoy_type_matcher.StringMatcher_Exact{ + Exact: value, + }, + }, + }, + }) + } else { + r.logger.Debug("HeaderMatches: Adding header", logfields.Name, hdr.Name) + headerMatches = append(headerMatches, &cilium.HeaderMatch{ + MismatchAction: mismatch_action, + Name: hdr.Name, + Value: value, + }) + } + } else if hdr.Secret == nil { + // No inline value and no secret. + // Header presence for FAIL_ON_MISMSTCH or matching empty value otherwise needed. + if mismatch_action == cilium.HeaderMatch_FAIL_ON_MISMATCH { + // Only header presence needed + headers = append(headers, &envoy_config_route.HeaderMatcher{ + Name: hdr.Name, + HeaderMatchSpecifier: &envoy_config_route.HeaderMatcher_PresentMatch{PresentMatch: true}, + }) + } else { + r.logger.Debug("HeaderMatches: Adding header for an empty value", logfields.Name, hdr.Name) + headerMatches = append(headerMatches, &cilium.HeaderMatch{ + MismatchAction: mismatch_action, + Name: hdr.Name, + }) + } + } else { + // A secret is set, so we transform to an SDS value. + // cilium-envoy takes care of treating this as a presence match if the + // secret exists with an empty value. + r.logger.Debug("HeaderMatches: Adding header because SDS value is required", logfields.Name, hdr.Name) + headerMatches = append(headerMatches, &cilium.HeaderMatch{ + MismatchAction: mismatch_action, + Name: hdr.Name, + ValueSdsSecret: namespacedNametoSyncedSDSSecretName(types.NamespacedName{ + Namespace: hdr.Secret.Namespace, + Name: hdr.Secret.Name, + }, r.secretManager.GetSecretSyncNamespace()), + }) + } + } + if len(headers) == 0 { + headers = nil + } else { + SortHeaderMatchers(headers) + } + if len(headerMatches) == 0 { + headerMatches = nil + } else { + // Optimally we should sort the headerMatches to avoid + // updating the policy if only the order of the rules + // has changed. Right now, when 'headerMatches' is a + // slice (rather than a map) the order only changes if + // the order of the rules in the imported policies + // changes, so there is minimal likelihood of + // unnecessary policy updates. + + // SortHeaderMatches(headerMatches) + } + + return &cilium.HttpNetworkPolicyRule{Headers: headers, HeaderMatches: headerMatches}, len(headerMatches) == 0 +} + +func (r *envoyL7RulesTranslator) getSecretString(hdr *policyapi.HeaderMatch, ns string) (string, error) { + value := "" + var err error + if hdr.Secret != nil { + value, err = r.secretManager.GetSecretString(context.TODO(), hdr.Secret, ns) + } + // Only use Value if secret was not obtained + if value == "" && hdr.Value != "" { + value = hdr.Value + if err != nil { + r.logger.Debug("HeaderMatches: Using a default value due to k8s secret not being available", logfields.Error, err) + err = nil + } + } + + return value, err +} + +func namespacedNametoSyncedSDSSecretName(namespacedName types.NamespacedName, policySecretsNamespace string) string { + if policySecretsNamespace == "" { + return fmt.Sprintf("%s/%s", namespacedName.Namespace, namespacedName.Name) + } + return fmt.Sprintf("%s/%s-%s", policySecretsNamespace, namespacedName.Namespace, namespacedName.Name) +} diff --git a/vendor/github.com/cilium/cilium/pkg/envoy/policy/sort.go b/vendor/github.com/cilium/cilium/pkg/envoy/policy/sort.go new file mode 100644 index 0000000000..ae35fbe49d --- /dev/null +++ b/vendor/github.com/cilium/cilium/pkg/envoy/policy/sort.go @@ -0,0 +1,319 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Authors of Cilium + +package envoypolicy + +import ( + "sort" + + cilium "github.com/cilium/proxy/go/cilium/api" + envoy_config_route "github.com/cilium/proxy/go/envoy/config/route/v3" +) + +// PortNetworkPolicySlice implements sort.Interface to sort a slice of +// *cilium.PortNetworkPolicy. +type PortNetworkPolicySlice []*cilium.PortNetworkPolicy + +func (s PortNetworkPolicySlice) Len() int { + return len(s) +} + +func (s PortNetworkPolicySlice) Less(i, j int) bool { + p1, p2 := s[i], s[j] + + switch { + case p1.Protocol < p2.Protocol: + return true + case p1.Protocol > p2.Protocol: + return false + } + + switch { + case p1.Port < p2.Port: + return true + case p1.Port > p2.Port: + return false + } + + rules1, rules2 := p1.Rules, p2.Rules + switch { + case len(rules1) < len(rules2): + return true + case len(rules1) > len(rules2): + return false + } + // Assuming that the slices are sorted. + for idx := range rules1 { + r1, r2 := rules1[idx], rules2[idx] + switch { + case PortNetworkPolicyRuleLess(r1, r2): + return true + case PortNetworkPolicyRuleLess(r2, r1): + return false + } + } + + // Elements are equal. + return false +} + +func (s PortNetworkPolicySlice) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +// SortPortNetworkPolicies sorts the given slice in place and returns +// the sorted slice for convenience. +func SortPortNetworkPolicies(policies []*cilium.PortNetworkPolicy) []*cilium.PortNetworkPolicy { + sort.Sort(PortNetworkPolicySlice(policies)) + return policies +} + +// PortNetworkPolicyRuleSlice implements sort.Interface to sort a slice of +// *cilium.PortNetworkPolicyRuleSlice. +type PortNetworkPolicyRuleSlice []*cilium.PortNetworkPolicyRule + +// PortNetworkPolicyRuleLess reports whether the r1 rule should sort before +// the r2 rule. +// L3-L4-only rules are less than L7 rules. +func PortNetworkPolicyRuleLess(r1, r2 *cilium.PortNetworkPolicyRule) bool { + // TODO: Support Kafka. + + http1, http2 := r1.GetHttpRules(), r2.GetHttpRules() + switch { + case http1 == nil && http2 != nil: + return true + case http1 != nil && http2 == nil: + return false + } + + if http1 != nil && http2 != nil { + httpRules1, httpRules2 := http1.HttpRules, http2.HttpRules + switch { + case len(httpRules1) < len(httpRules2): + return true + case len(httpRules1) > len(httpRules2): + return false + } + // Assuming that the slices are sorted. + for idx := range httpRules1 { + httpRule1, httpRule2 := httpRules1[idx], httpRules2[idx] + switch { + case HTTPNetworkPolicyRuleLess(httpRule1, httpRule2): + return true + case HTTPNetworkPolicyRuleLess(httpRule2, httpRule1): + return false + } + } + } + + remotePolicies1, remotePolicies2 := r1.RemotePolicies, r2.RemotePolicies + switch { + case len(remotePolicies1) < len(remotePolicies2): + return true + case len(remotePolicies1) > len(remotePolicies2): + return false + } + // Assuming that the slices are sorted. + for idx := range remotePolicies1 { + p1, p2 := remotePolicies1[idx], remotePolicies2[idx] + switch { + case p1 < p2: + return true + case p1 > p2: + return false + } + } + + // Elements are equal. + return false +} + +func (s PortNetworkPolicyRuleSlice) Len() int { + return len(s) +} + +func (s PortNetworkPolicyRuleSlice) Less(i, j int) bool { + return PortNetworkPolicyRuleLess(s[i], s[j]) +} + +func (s PortNetworkPolicyRuleSlice) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +// SortPortNetworkPolicyRules sorts the given slice in place +// and returns the sorted slice for convenience. +func SortPortNetworkPolicyRules(rules []*cilium.PortNetworkPolicyRule) []*cilium.PortNetworkPolicyRule { + sort.Sort(PortNetworkPolicyRuleSlice(rules)) + return rules +} + +// HTTPNetworkPolicyRuleSlice implements sort.Interface to sort a slice of +// *cilium.HttpNetworkPolicyRule. +type HTTPNetworkPolicyRuleSlice []*cilium.HttpNetworkPolicyRule + +// HTTPNetworkPolicyRuleLess reports whether the r1 rule should sort before the +// r2 rule. +func HTTPNetworkPolicyRuleLess(r1, r2 *cilium.HttpNetworkPolicyRule) bool { + headers1, headers2 := r1.Headers, r2.Headers + switch { + case len(headers1) < len(headers2): + return true + case len(headers1) > len(headers2): + return false + } + // Assuming that the slices are sorted. + for idx := range headers1 { + header1, header2 := headers1[idx], headers2[idx] + switch { + case HeaderMatcherLess(header1, header2): + return true + case HeaderMatcherLess(header2, header1): + return false + } + } + + // Elements are equal. + return false +} + +func (s HTTPNetworkPolicyRuleSlice) Len() int { + return len(s) +} + +func (s HTTPNetworkPolicyRuleSlice) Less(i, j int) bool { + return HTTPNetworkPolicyRuleLess(s[i], s[j]) +} + +func (s HTTPNetworkPolicyRuleSlice) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +// SortHTTPNetworkPolicyRules sorts the given slice. +func SortHTTPNetworkPolicyRules(rules []*cilium.HttpNetworkPolicyRule) { + sort.Sort(HTTPNetworkPolicyRuleSlice(rules)) +} + +// HeaderMatcherSlice implements sort.Interface to sort a slice of +// *envoy_config_route.HeaderMatcher. +type HeaderMatcherSlice []*envoy_config_route.HeaderMatcher + +// HeaderMatcherLess reports whether the m1 matcher should sort before the m2 +// matcher. +func HeaderMatcherLess(m1, m2 *envoy_config_route.HeaderMatcher) bool { + switch { + case m1.Name < m2.Name: + return true + case m1.Name > m2.Name: + return false + } + + // Compare the header_match_specifier oneof field, by comparing each + // possible field in the oneof individually: + // - exactMatch + // - regexMatch + // - rangeMatch + // - presentMatch + // - prefixMatch + // - suffixMatch + // Use the getters to access the fields and return zero values when they + // are not set. + + s1 := m1.GetExactMatch() + s2 := m2.GetExactMatch() + switch { + case s1 < s2: + return true + case s1 > s2: + return false + } + + srm1 := m1.GetSafeRegexMatch() + srm2 := m2.GetSafeRegexMatch() + switch { + case srm1 == nil && srm2 != nil: + return true + case srm1 != nil && srm2 == nil: + return false + case srm1 != nil && srm2 != nil: + switch { + case srm1.Regex < srm2.Regex: + return true + case srm1.Regex > srm2.Regex: + return false + } + } + + rm1 := m1.GetRangeMatch() + rm2 := m2.GetRangeMatch() + switch { + case rm1 == nil && rm2 != nil: + return true + case rm1 != nil && rm2 == nil: + return false + case rm1 != nil && rm2 != nil: + switch { + case rm1.Start < rm2.Start: + return true + case rm1.Start > rm2.Start: + return false + } + switch { + case rm1.End < rm2.End: + return true + case rm1.End > rm2.End: + return false + } + } + + switch { + case !m1.GetPresentMatch() && m2.GetPresentMatch(): + return true + case m1.GetPresentMatch() && !m2.GetPresentMatch(): + return false + } + + s1 = m1.GetPrefixMatch() + s2 = m2.GetPrefixMatch() + switch { + case s1 < s2: + return true + case s1 > s2: + return false + } + + s1 = m1.GetSuffixMatch() + s2 = m2.GetSuffixMatch() + switch { + case s1 < s2: + return true + case s1 > s2: + return false + } + + switch { + case !m1.InvertMatch && m2.InvertMatch: + return true + case m1.InvertMatch && !m2.InvertMatch: + return false + } + + // Elements are equal. + return false +} + +func (s HeaderMatcherSlice) Len() int { + return len(s) +} + +func (s HeaderMatcherSlice) Less(i, j int) bool { + return HeaderMatcherLess(s[i], s[j]) +} + +func (s HeaderMatcherSlice) Swap(i, j int) { + s[i], s[j] = s[j], s[i] +} + +// SortHeaderMatchers sorts the given slice. +func SortHeaderMatchers(headers []*envoy_config_route.HeaderMatcher) { + sort.Sort(HeaderMatcherSlice(headers)) +} diff --git a/vendor/github.com/cilium/cilium/pkg/health/client/tree.go b/vendor/github.com/cilium/cilium/pkg/health/client/tree.go index febac0144a..28ab5e4315 100644 --- a/vendor/github.com/cilium/cilium/pkg/health/client/tree.go +++ b/vendor/github.com/cilium/cilium/pkg/health/client/tree.go @@ -7,6 +7,7 @@ import ( "bytes" "fmt" "io" + "slices" "sort" "strconv" "strings" @@ -177,13 +178,7 @@ func dumpVals(w io.Writer, level, maxLevel int, levelsEnded []int, edge decorati } func isEnded(levelsEnded []int, level int) bool { - for _, l := range levelsEnded { - if l == level { - return true - } - } - - return false + return slices.Contains(levelsEnded, level) } func dumpVal(level int, node *node) string { diff --git a/vendor/github.com/cilium/cilium/pkg/ip/ip.go b/vendor/github.com/cilium/cilium/pkg/ip/ip.go index 7f457f340c..23af4e44a7 100644 --- a/vendor/github.com/cilium/cilium/pkg/ip/ip.go +++ b/vendor/github.com/cilium/cilium/pkg/ip/ip.go @@ -132,7 +132,7 @@ func removeRedundantCIDRs(CIDRs []*net.IPNet) []*net.IPNet { if len(redundant) == 1 { for i := range redundant { - return append(CIDRs[:i], CIDRs[i+1:]...) + return slices.Delete(CIDRs, i, i+1) } } @@ -175,12 +175,12 @@ func RemoveCIDRs(allowCIDRs, removeCIDRs []*net.IPNet) []*net.IPNet { // Remove CIDR that we have just processed and append new CIDRs // that we computed from removing the CIDR to remove. - allowCIDRs = append(allowCIDRs[:i], allowCIDRs[i+1:]...) + allowCIDRs = slices.Delete(allowCIDRs, i, i+1) allowCIDRs = append(allowCIDRs, nets...) } else if remove.Contains(allowCIDR.IP.Mask(allowCIDR.Mask)) { // If a CIDR that we want to remove contains a CIDR in the list // that is allowed, then we can just remove the CIDR to allow. - allowCIDRs = append(allowCIDRs[:i], allowCIDRs[i+1:]...) + allowCIDRs = slices.Delete(allowCIDRs, i, i+1) } else { // Advance only if CIDR at index 'i' was not removed i++ @@ -500,7 +500,7 @@ func mergeAdjacentCIDRs(ranges []*netWithRange) []*netWithRange { // Since we have combined ranges[i] with the preceding item in the // ranges list, we can delete ranges[i] from the slice. - ranges = append(ranges[:i], ranges[i+1:]...) + ranges = slices.Delete(ranges, i, i+1) } } return ranges diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/annotate.go b/vendor/github.com/cilium/cilium/pkg/k8s/annotate.go index c6d072d225..46a18dd28b 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/annotate.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/annotate.go @@ -7,11 +7,11 @@ import ( "context" "encoding/json" "fmt" + "log/slog" "reflect" "strconv" "strings" - "github.com/sirupsen/logrus" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" k8sTypes "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes" @@ -69,20 +69,21 @@ func updateNodeAnnotation(c kubernetes.Interface, nodeName string, annotation no // AnnotateNode writes v4 and v6 CIDRs and health IPs in the given k8s node name. // In case of failure while updating the node, this function while spawn a go // routine to retry the node update indefinitely. -func AnnotateNode(cs kubernetes.Interface, nodeName string, nd nodeTypes.Node, encryptKey uint8) (nodeAnnotation, error) { - scopedLog := log.WithFields(logrus.Fields{ - logfields.NodeName: nodeName, - logfields.V4Prefix: nd.IPv4AllocCIDR, - logfields.V6Prefix: nd.IPv6AllocCIDR, - logfields.V4HealthIP: nd.IPv4HealthIP, - logfields.V6HealthIP: nd.IPv6HealthIP, - logfields.V4IngressIP: nd.IPv4IngressIP, - logfields.V6IngressIP: nd.IPv6IngressIP, - logfields.V4CiliumHostIP: nd.GetCiliumInternalIP(false), - logfields.V6CiliumHostIP: nd.GetCiliumInternalIP(true), - logfields.Key: encryptKey, - }) +func AnnotateNode(logger *slog.Logger, cs kubernetes.Interface, nodeName string, nd nodeTypes.Node, encryptKey uint8) (nodeAnnotation, error) { + scopedLog := logger.With( + logfields.NodeName, nodeName, + logfields.V4Prefix, nd.IPv4AllocCIDR, + logfields.V6Prefix, nd.IPv6AllocCIDR, + logfields.V4HealthIP, nd.IPv4HealthIP, + logfields.V6HealthIP, nd.IPv6HealthIP, + logfields.V4IngressIP, nd.IPv4IngressIP, + logfields.V6IngressIP, nd.IPv6IngressIP, + logfields.V4CiliumHostIP, nd.GetCiliumInternalIP(false), + logfields.V6CiliumHostIP, nd.GetCiliumInternalIP(true), + logfields.Key, encryptKey, + ) scopedLog.Debug("Updating node annotations with node CIDRs") + annotation := prepareNodeAnnotation(nd, encryptKey) controller.NewManager().UpdateController("update-k8s-node-annotations", controller.ControllerParams{ @@ -90,7 +91,7 @@ func AnnotateNode(cs kubernetes.Interface, nodeName string, nd nodeTypes.Node, e DoFunc: func(_ context.Context) error { err := updateNodeAnnotation(cs, nodeName, annotation) if err != nil { - scopedLog.WithFields(logrus.Fields{}).WithError(err).Warn("Unable to patch node resource with annotation") + scopedLog.Warn("Unable to patch node resource with annotation", logfields.Error, err) } return err }, diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/register.go b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/register.go index 0d015cab95..710a840001 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/register.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/register.go @@ -15,5 +15,5 @@ const ( // // Maintainers: Run ./Documentation/check-crd-compat-table.sh for each release // Developers: Bump patch for each change in the CRD schema. - CustomResourceDefinitionSchemaVersion = "1.31.3" + CustomResourceDefinitionSchemaVersion = "1.31.4" ) diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/utils/utils.go b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/utils/utils.go index 90227aa005..611eedcb73 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/utils/utils.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/utils/utils.go @@ -4,20 +4,18 @@ package utils import ( - "github.com/sirupsen/logrus" + "log/slog" + "k8s.io/apimachinery/pkg/types" k8sConst "github.com/cilium/cilium/pkg/k8s/apis/cilium.io" slim_metav1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1" "github.com/cilium/cilium/pkg/labels" - "github.com/cilium/cilium/pkg/logging" "github.com/cilium/cilium/pkg/logging/logfields" "github.com/cilium/cilium/pkg/policy/api" ) const ( - // subsysK8s is the value for logfields.LogSubsys - subsysK8s = "k8s" // podPrefixLbl is the value the prefix used in the label selector to // represent pods on the default namespace. podPrefixLbl = labels.LabelSourceK8sKeyPrefix + k8sConst.PodNamespaceLabel @@ -45,11 +43,6 @@ const ( ResourceTypeCiliumClusterwideNetworkPolicy = "CiliumClusterwideNetworkPolicy" ) -var ( - // log is the k8s package logger object. - log = logging.DefaultLogger.WithField(logfields.LogSubsys, subsysK8s) -) - // GetPolicyLabels returns a LabelArray for the given namespace and name. func GetPolicyLabels(ns, name string, uid types.UID, derivedFrom string) labels.LabelArray { // Keep labels sorted by the key. @@ -326,7 +319,7 @@ func namespacesAreValid(namespace string, userNamespaces []string) bool { // labels. If the namespace provided is empty then the rule is cluster scoped, this // might happen in case of CiliumClusterwideNetworkPolicy which enforces a policy on the cluster // instead of the particular namespace. -func ParseToCiliumRule(namespace, name string, uid types.UID, r *api.Rule) *api.Rule { +func ParseToCiliumRule(logger *slog.Logger, namespace, name string, uid types.UID, r *api.Rule) *api.Rule { retRule := &api.Rule{} if r.EndpointSelector.LabelSelector != nil { retRule.EndpointSelector = api.NewESFromK8sLabelSelector("", r.EndpointSelector.LabelSelector) @@ -343,12 +336,12 @@ func ParseToCiliumRule(namespace, name string, uid types.UID, r *api.Rule) *api. if namespace != "" { userNamespace, present := r.EndpointSelector.GetMatch(podPrefixLbl) if present && !namespacesAreValid(namespace, userNamespace) { - log.WithFields(logrus.Fields{ - logfields.K8sNamespace: namespace, - logfields.CiliumNetworkPolicyName: name, - logfields.K8sNamespace + ".illegal": userNamespace, - }).Warn("CiliumNetworkPolicy contains illegal namespace match in EndpointSelector." + - " EndpointSelector always applies in namespace of the policy resource, removing illegal namespace match'.") + logger.Warn("CiliumNetworkPolicy contains illegal namespace match in EndpointSelector."+ + " EndpointSelector always applies in namespace of the policy resource, removing illegal namespace match'.", + logfields.K8sNamespace, namespace, + logfields.CiliumNetworkPolicyName, name, + logfields.K8sNamespaceIllegal, userNamespace, + ) } retRule.EndpointSelector.AddMatch(podPrefixLbl, namespace) } diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2/bgp_advert_types.go b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2/bgp_advert_types.go index 8be0ae1209..3711b4e7b4 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2/bgp_advert_types.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2/bgp_advert_types.go @@ -127,6 +127,16 @@ type BGPAdvertisement struct { // BGPServiceOptions defines the configuration for Service advertisement type. type BGPServiceOptions struct { + // IPv4 mask to aggregate BGP route advertisements of service + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:validation:Maximum=31 + // +kubebuilder:validation:Optional + AggregationLengthIPv4 *int16 `json:"aggregationLengthIPv4,omitempty"` + // IPv6 mask to aggregate BGP route advertisements of service + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:validation:Maximum=127 + // +kubebuilder:validation:Optional + AggregationLengthIPv6 *int16 `json:"aggregationLengthIPv6,omitempty"` // Addresses is a list of service address types which needs to be advertised via BGP. // // +kubebuilder:validation:Required diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2/ccnp_types.go b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2/ccnp_types.go index dd563f079f..9059252a47 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2/ccnp_types.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2/ccnp_types.go @@ -5,6 +5,7 @@ package v2 import ( "fmt" + "log/slog" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -77,7 +78,7 @@ type CiliumClusterwideNetworkPolicyList struct { // Parse parses a CiliumClusterwideNetworkPolicy and returns a list of cilium // policy rules. -func (r *CiliumClusterwideNetworkPolicy) Parse() (api.Rules, error) { +func (r *CiliumClusterwideNetworkPolicy) Parse(logger *slog.Logger) (api.Rules, error) { if r.ObjectMeta.Name == "" { return nil, NewErrParse("CiliumClusterwideNetworkPolicy must have name") } @@ -95,7 +96,7 @@ func (r *CiliumClusterwideNetworkPolicy) Parse() (api.Rules, error) { if err := r.Spec.Sanitize(); err != nil { return nil, NewErrParse(fmt.Sprintf("Invalid CiliumClusterwideNetworkPolicy spec: %s", err)) } - cr := k8sCiliumUtils.ParseToCiliumRule("", name, uid, r.Spec) + cr := k8sCiliumUtils.ParseToCiliumRule(logger, "", name, uid, r.Spec) retRules = append(retRules, cr) } if r.Specs != nil { @@ -104,7 +105,7 @@ func (r *CiliumClusterwideNetworkPolicy) Parse() (api.Rules, error) { return nil, NewErrParse(fmt.Sprintf("Invalid CiliumClusterwideNetworkPolicy specs: %s", err)) } - cr := k8sCiliumUtils.ParseToCiliumRule("", name, uid, rule) + cr := k8sCiliumUtils.ParseToCiliumRule(logger, "", name, uid, rule) retRules = append(retRules, cr) } } diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2/cec_types.go b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2/cec_types.go index e672fbb31b..e5e1ef2c61 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2/cec_types.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2/cec_types.go @@ -16,6 +16,8 @@ import ( slim_metav1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1" "github.com/cilium/cilium/pkg/loadbalancer" + "github.com/cilium/cilium/pkg/logging" + "github.com/cilium/cilium/pkg/logging/logfields" "github.com/cilium/cilium/pkg/option" ) @@ -185,10 +187,12 @@ func (u *XDSResource) UnmarshalJSON(b []byte) (err error) { if err != nil { var buf bytes.Buffer json.Indent(&buf, b, "", "\t") - log.Warningf("Ignoring invalid CiliumEnvoyConfig JSON (%s): %s", - err, buf.String()) + logging.DefaultSlogLogger.Warn("Ignoring invalid CiliumEnvoyConfig JSON", + logfields.Error, err, + logfields.Object, buf, + ) } else if option.Config.Debug { - log.Debugf("CEC unmarshaled XDS Resource: %v", prototext.Format(u.Any)) + logging.DefaultSlogLogger.Debug("CEC unmarshaled XDS Resource", logfields.Resource, prototext.Format(u.Any)) } return nil } diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2/cnp_types.go b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2/cnp_types.go index 27c2667f36..0ad09fa280 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2/cnp_types.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2/cnp_types.go @@ -5,6 +5,7 @@ package v2 import ( "fmt" + "log/slog" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -159,7 +160,7 @@ func (r *CiliumNetworkPolicy) SetDerivedPolicyStatus(derivativePolicyName string // Parse parses a CiliumNetworkPolicy and returns a list of cilium policy // rules. -func (r *CiliumNetworkPolicy) Parse() (api.Rules, error) { +func (r *CiliumNetworkPolicy) Parse(logger *slog.Logger) (api.Rules, error) { if r.ObjectMeta.Name == "" { return nil, NewErrParse("CiliumNetworkPolicy must have name") } @@ -176,7 +177,7 @@ func (r *CiliumNetworkPolicy) Parse() (api.Rules, error) { Specs: r.Specs, Status: r.Status, } - return ccnp.Parse() + return ccnp.Parse(logger) } name := r.ObjectMeta.Name uid := r.ObjectMeta.UID @@ -194,7 +195,7 @@ func (r *CiliumNetworkPolicy) Parse() (api.Rules, error) { if r.Spec.NodeSelector.LabelSelector != nil { return nil, NewErrParse("Invalid CiliumNetworkPolicy spec: rule cannot have NodeSelector") } - cr := k8sCiliumUtils.ParseToCiliumRule(namespace, name, uid, r.Spec) + cr := k8sCiliumUtils.ParseToCiliumRule(logger, namespace, name, uid, r.Spec) retRules = append(retRules, cr) } if r.Specs != nil { @@ -203,7 +204,7 @@ func (r *CiliumNetworkPolicy) Parse() (api.Rules, error) { return nil, NewErrParse(fmt.Sprintf("Invalid CiliumNetworkPolicy specs: %s", err)) } - cr := k8sCiliumUtils.ParseToCiliumRule(namespace, name, uid, rule) + cr := k8sCiliumUtils.ParseToCiliumRule(logger, namespace, name, uid, rule) retRules = append(retRules, cr) } } diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2/logfields.go b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2/logfields.go deleted file mode 100644 index b4f5563730..0000000000 --- a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2/logfields.go +++ /dev/null @@ -1,11 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package v2 - -import ( - "github.com/cilium/cilium/pkg/logging" - "github.com/cilium/cilium/pkg/logging/logfields" -) - -var log = logging.DefaultLogger.WithField(logfields.LogSubsys, "k8s-apis-cilium.io-v2") diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2/zz_generated.deepcopy.go b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2/zz_generated.deepcopy.go index 87ccf9652c..cf67c096c1 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2/zz_generated.deepcopy.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2/zz_generated.deepcopy.go @@ -133,6 +133,16 @@ func (in *BGPFamilyRouteCount) DeepCopy() *BGPFamilyRouteCount { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BGPServiceOptions) DeepCopyInto(out *BGPServiceOptions) { *out = *in + if in.AggregationLengthIPv4 != nil { + in, out := &in.AggregationLengthIPv4, &out.AggregationLengthIPv4 + *out = new(int16) + **out = **in + } + if in.AggregationLengthIPv6 != nil { + in, out := &in.AggregationLengthIPv6, &out.AggregationLengthIPv6 + *out = new(int16) + **out = **in + } if in.Addresses != nil { in, out := &in.Addresses, &out.Addresses *out = make([]BGPServiceAddressType, len(*in)) diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2/zz_generated.deepequal.go b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2/zz_generated.deepequal.go index 17b526dc60..3459656914 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2/zz_generated.deepequal.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2/zz_generated.deepequal.go @@ -228,6 +228,22 @@ func (in *BGPServiceOptions) DeepEqual(other *BGPServiceOptions) bool { return false } + if (in.AggregationLengthIPv4 == nil) != (other.AggregationLengthIPv4 == nil) { + return false + } else if in.AggregationLengthIPv4 != nil { + if *in.AggregationLengthIPv4 != *other.AggregationLengthIPv4 { + return false + } + } + + if (in.AggregationLengthIPv6 == nil) != (other.AggregationLengthIPv6 == nil) { + return false + } else if in.AggregationLengthIPv6 != nil { + if *in.AggregationLengthIPv6 != *other.AggregationLengthIPv6 { + return false + } + } + if ((in.Addresses != nil) && (other.Addresses != nil)) || ((in.Addresses == nil) != (other.Addresses == nil)) { in, other := &in.Addresses, &other.Addresses if other == nil { diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1/bgp_advert_types.go b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1/bgp_advert_types.go index fc5e508336..822ba6f5ce 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1/bgp_advert_types.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1/bgp_advert_types.go @@ -62,6 +62,7 @@ const ( // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:resource:categories={cilium,ciliumbgp},singular="ciliumbgpadvertisement",path="ciliumbgpadvertisements",scope="Cluster",shortName={cbgpadvert} // +kubebuilder:printcolumn:JSONPath=".metadata.creationTimestamp",name="Age",type=date +// +kubebuilder:deprecatedversion // CiliumBGPAdvertisement is the Schema for the ciliumbgpadvertisements API type CiliumBGPAdvertisement struct { @@ -121,6 +122,16 @@ type BGPAdvertisement struct { // BGPServiceOptions defines the configuration for Service advertisement type. type BGPServiceOptions struct { + // IPv4 mask to aggregate BGP route advertisements of service + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:validation:Maximum=31 + // +kubebuilder:validation:Optional + AggregationLengthIPv4 *int16 `json:"aggregationLengthIPv4,omitempty"` + // IPv6 mask to aggregate BGP route advertisements of service + // +kubebuilder:validation:Minimum=0 + // +kubebuilder:validation:Maximum=127 + // +kubebuilder:validation:Optional + AggregationLengthIPv6 *int16 `json:"aggregationLengthIPv6,omitempty"` // Addresses is a list of service address types which needs to be advertised via BGP. // // +kubebuilder:validation:Required diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1/bgp_cluster_types.go b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1/bgp_cluster_types.go index fee94679fc..bc94c1ab4b 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1/bgp_cluster_types.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1/bgp_cluster_types.go @@ -15,6 +15,7 @@ import ( // +kubebuilder:resource:categories={cilium,ciliumbgp},singular="ciliumbgpclusterconfig",path="ciliumbgpclusterconfigs",scope="Cluster",shortName={cbgpcluster} // +kubebuilder:printcolumn:JSONPath=".metadata.creationTimestamp",name="Age",type=date // +kubebuilder:subresource:status +// +kubebuilder:deprecatedversion // CiliumBGPClusterConfig is the Schema for the CiliumBGPClusterConfig API type CiliumBGPClusterConfig struct { diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1/bgp_node_override_types.go b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1/bgp_node_override_types.go index 6ba6b00f3d..e7f208c07b 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1/bgp_node_override_types.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1/bgp_node_override_types.go @@ -12,6 +12,7 @@ import ( // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object // +kubebuilder:resource:categories={cilium,ciliumbgp},singular="ciliumbgpnodeconfigoverride",path="ciliumbgpnodeconfigoverrides",scope="Cluster",shortName={cbgpnodeoverride} // +kubebuilder:printcolumn:JSONPath=".metadata.creationTimestamp",name="Age",type=date +// +kubebuilder:deprecatedversion // CiliumBGPNodeConfigOverride specifies configuration overrides for a CiliumBGPNodeConfig. // It allows fine-tuning of BGP behavior on a per-node basis. For the override to be effective, diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1/bgp_node_types.go b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1/bgp_node_types.go index 1b5abad121..a9e1d7e9e0 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1/bgp_node_types.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1/bgp_node_types.go @@ -13,6 +13,7 @@ import ( // +kubebuilder:resource:categories={cilium,ciliumbgp},singular="ciliumbgpnodeconfig",path="ciliumbgpnodeconfigs",scope="Cluster",shortName={cbgpnode} // +kubebuilder:printcolumn:JSONPath=".metadata.creationTimestamp",name="Age",type=date // +kubebuilder:subresource:status +// +kubebuilder:deprecatedversion // CiliumBGPNodeConfig is node local configuration for BGP agent. Name of the object should be node name. // This resource will be created by Cilium operator and is read-only for the users. diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1/bgp_peer_types.go b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1/bgp_peer_types.go index 2108722234..e37aee2596 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1/bgp_peer_types.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1/bgp_peer_types.go @@ -29,6 +29,7 @@ type CiliumBGPPeerConfigList struct { // +kubebuilder:resource:categories={cilium,ciliumbgp},singular="ciliumbgppeerconfig",path="ciliumbgppeerconfigs",scope="Cluster",shortName={cbgppeer} // +kubebuilder:printcolumn:JSONPath=".metadata.creationTimestamp",name="Age",type=date // +kubebuilder:subresource:status +// +kubebuilder:deprecatedversion type CiliumBGPPeerConfig struct { // +deepequal-gen=false diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1/zz_generated.deepcopy.go b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1/zz_generated.deepcopy.go index a940e26c2e..3f06ff505d 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1/zz_generated.deepcopy.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1/zz_generated.deepcopy.go @@ -135,6 +135,16 @@ func (in *BGPFamilyRouteCount) DeepCopy() *BGPFamilyRouteCount { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BGPServiceOptions) DeepCopyInto(out *BGPServiceOptions) { *out = *in + if in.AggregationLengthIPv4 != nil { + in, out := &in.AggregationLengthIPv4, &out.AggregationLengthIPv4 + *out = new(int16) + **out = **in + } + if in.AggregationLengthIPv6 != nil { + in, out := &in.AggregationLengthIPv6, &out.AggregationLengthIPv6 + *out = new(int16) + **out = **in + } if in.Addresses != nil { in, out := &in.Addresses, &out.Addresses *out = make([]BGPServiceAddressType, len(*in)) diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1/zz_generated.deepequal.go b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1/zz_generated.deepequal.go index 8be7566a97..226239125a 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1/zz_generated.deepequal.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1/zz_generated.deepequal.go @@ -171,6 +171,22 @@ func (in *BGPServiceOptions) DeepEqual(other *BGPServiceOptions) bool { return false } + if (in.AggregationLengthIPv4 == nil) != (other.AggregationLengthIPv4 == nil) { + return false + } else if in.AggregationLengthIPv4 != nil { + if *in.AggregationLengthIPv4 != *other.AggregationLengthIPv4 { + return false + } + } + + if (in.AggregationLengthIPv6 == nil) != (other.AggregationLengthIPv6 == nil) { + return false + } else if in.AggregationLengthIPv6 != nil { + if *in.AggregationLengthIPv6 != *other.AggregationLengthIPv6 { + return false + } + } + if ((in.Addresses != nil) && (other.Addresses != nil)) || ((in.Addresses == nil) != (other.Addresses == nil)) { in, other := &in.Addresses, &other.Addresses if other == nil { diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/client/cell.go b/vendor/github.com/cilium/cilium/pkg/k8s/client/cell.go index c0f5dd96ed..b6e3cbe366 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/client/cell.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/client/cell.go @@ -7,6 +7,7 @@ import ( "context" "errors" "fmt" + "log/slog" "net" "net/http" "os" @@ -15,7 +16,6 @@ import ( "github.com/cilium/hive/cell" "github.com/cilium/hive/job" - "github.com/sirupsen/logrus" apiext_clientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" k8sErrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -121,28 +121,28 @@ type compositeClientset struct { controller *controller.Manager slim *SlimClientset config Config - log logrus.FieldLogger + logger *slog.Logger closeAllConns func() restConfigManager restConfig } -func newClientset(lc cell.Lifecycle, log logrus.FieldLogger, cfg Config, jobs job.Group) (Clientset, error) { - return newClientsetForUserAgent(lc, log, cfg, "", jobs) +func newClientset(lc cell.Lifecycle, logger *slog.Logger, cfg Config, jobs job.Group) (Clientset, error) { + return newClientsetForUserAgent(lc, logger, cfg, "", jobs) } -func newClientsetForUserAgent(lc cell.Lifecycle, log logrus.FieldLogger, cfg Config, name string, jobs job.Group) (Clientset, error) { +func newClientsetForUserAgent(lc cell.Lifecycle, logger *slog.Logger, cfg Config, name string, jobs job.Group) (Clientset, error) { if !cfg.isEnabled() { return &compositeClientset{disabled: true}, nil } client := compositeClientset{ - log: log, + logger: logger, controller: controller.NewManager(), config: cfg, } var err error - client.restConfigManager, err = restConfigManagerInit(cfg, name, log, jobs) + client.restConfigManager, err = restConfigManagerInit(cfg, name, logger, jobs) if err != nil { return nil, fmt.Errorf("unable to create k8s client rest configuration: %w", err) } @@ -243,7 +243,7 @@ func (c *compositeClientset) onStart(startCtx cell.HookContext) error { c.startHeartbeat() // Update the global K8s clients, K8s version and the capabilities. - if err := k8sversion.Update(c, c.config.EnableK8sAPIDiscovery); err != nil { + if err := k8sversion.Update(c.logger, c, c.config.EnableK8sAPIDiscovery); err != nil { return err } @@ -295,7 +295,7 @@ func (c *compositeClientset) startHeartbeat() { Group: k8sHeartbeatControllerGroup, DoFunc: func(context.Context) error { runHeartbeat( - c.log, + c.logger, heartBeat, timeout, c.closeAllConns, @@ -314,7 +314,9 @@ func (c *compositeClientset) waitForConn(ctx context.Context) error { var err error wait.Until(func() { retry: - c.log.WithField("host", c.restConfigManager.getConfig().Host).Info("Establishing connection to apiserver") + c.logger.Info("Establishing connection to apiserver", + logfields.IPAddr, c.restConfigManager.getConfig().Host, + ) err = isConnReady(c) if err == nil { close(stop) @@ -332,11 +334,14 @@ func (c *compositeClientset) waitForConn(ctx context.Context) error { return } - c.log.WithError(err).WithField(logfields.IPAddr, c.restConfigManager.getConfig().Host).Error("Unable to contact k8s api-server") + c.logger.Error("Unable to contact k8s api-server", + logfields.IPAddr, c.restConfigManager.getConfig().Host, + logfields.Error, err, + ) close(stop) }, connRetryInterval, stop) if err == nil { - c.log.Info("Connected to apiserver") + c.logger.Info("Connected to apiserver") } return err } @@ -354,7 +359,7 @@ func setDialer(cfg Config, restConfig *rest.Config) func() { return dialer.CloseAll } -func runHeartbeat(log logrus.FieldLogger, heartBeat func(context.Context) error, timeout time.Duration, onFailure ...func()) { +func runHeartbeat(logger *slog.Logger, heartBeat func(context.Context) error, timeout time.Duration, onFailure ...func()) { expireDate := time.Now().Add(-timeout) // Don't even perform a health check if we have received a successful // k8s event in the last 'timeout' duration @@ -385,13 +390,13 @@ func runHeartbeat(log logrus.FieldLogger, heartBeat func(context.Context) error, select { case err := <-done: if err != nil { - log.WithError(err).Warn("Network status error received, restarting client connections") + logger.Warn("Network status error received, restarting client connections", logfields.Error, err) for _, fn := range onFailure { fn() } } case <-ctx.Done(): - log.Warn("Heartbeat timed out, restarting client connections") + logger.Warn("Heartbeat timed out, restarting client connections") for _, fn := range onFailure { fn() } @@ -414,9 +419,9 @@ type ClientBuilderFunc func(name string) (Clientset, error) // NewClientBuilder returns a function that creates a new Clientset with the given // name appended to the user agent, or returns an error if the Clientset cannot be // created. -func NewClientBuilder(lc cell.Lifecycle, log logrus.FieldLogger, cfg Config, jobs job.Group) ClientBuilderFunc { +func NewClientBuilder(lc cell.Lifecycle, logger *slog.Logger, cfg Config, jobs job.Group) ClientBuilderFunc { return func(name string) (Clientset, error) { - c, err := newClientsetForUserAgent(lc, log, cfg, name, jobs) + c, err := newClientsetForUserAgent(lc, logger, cfg, name, jobs) if err != nil { return nil, err } diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/client/restConfig_provider.go b/vendor/github.com/cilium/cilium/pkg/k8s/client/restConfig_provider.go index 4fc7c0825d..2dfbf4689b 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/client/restConfig_provider.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/client/restConfig_provider.go @@ -8,6 +8,7 @@ import ( "encoding/json" "errors" "fmt" + "log/slog" "math/rand/v2" "net/http" "net/url" @@ -18,7 +19,6 @@ import ( "github.com/cilium/hive/cell" "github.com/cilium/hive/job" "github.com/fsnotify/fsnotify" - "github.com/sirupsen/logrus" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" @@ -26,7 +26,6 @@ import ( "github.com/cilium/cilium/pkg/fswatcher" "github.com/cilium/cilium/pkg/lock" - "github.com/cilium/cilium/pkg/logging" "github.com/cilium/cilium/pkg/logging/logfields" "github.com/cilium/cilium/pkg/option" "github.com/cilium/cilium/pkg/time" @@ -34,8 +33,6 @@ import ( ) var ( - log = logging.DefaultLogger.WithField(logfields.LogSubsys, "k8s-client") - // K8sAPIServerFilePath is the file path for storing kube-apiserver service and // endpoints for high availability failover. K8sAPIServerFilePath = filepath.Join(option.Config.StateDir, "k8sapi_server_state.json") @@ -69,7 +66,7 @@ type restConfigManager struct { apiServerURLs []*url.URL isConnectedToService bool lock.RWMutex - log logrus.FieldLogger + log *slog.Logger rt *rotatingHttpRoundTripper jobs job.Group } @@ -82,7 +79,7 @@ type restConfig interface { // UpdateK8sAPIServerEntry writes the provided kubernetes service to endpoint mapping // to K8sAPIServerFilePath. -func UpdateK8sAPIServerEntry(mapping K8sServiceEndpointMapping) { +func UpdateK8sAPIServerEntry(logger *slog.Logger, mapping K8sServiceEndpointMapping) { f, err := os.OpenFile(K8sAPIServerFilePath, os.O_RDWR, 0644) if err != nil { return @@ -90,8 +87,11 @@ func UpdateK8sAPIServerEntry(mapping K8sServiceEndpointMapping) { defer f.Close() if err = json.NewEncoder(f).Encode(mapping); err != nil { - log.WithError(err).WithField("entry", mapping).Error("failed to write kubernetes service entry," + - "agent may not be able to fail over to an active k8sapi-server") + logger.Error("failed to write kubernetes service entry,"+ + "agent may not be able to fail over to an active k8sapi-server", + logfields.Error, err, + logfields.Entry, mapping, + ) } } @@ -113,7 +113,7 @@ func (r *restConfigManager) canRotateAPIServerURL() bool { return len(r.apiServerURLs) > 1 && !r.isConnectedToService } -func restConfigManagerInit(cfg Config, name string, log logrus.FieldLogger, jobs job.Group) (restConfig, error) { +func restConfigManagerInit(cfg Config, name string, log *slog.Logger, jobs job.Group) (restConfig, error) { var err error manager := restConfigManager{ log: log, @@ -211,7 +211,10 @@ func (r *restConfigManager) parseConfig(cfg Config) { } serverURL, err = url.Parse(s) if err != nil { - r.log.WithError(err).Errorf("Failed to parse APIServerURL %s, skipping", serverURL) + r.log.Error("Failed to parse APIServerURL, skipping", + logfields.Error, err, + logfields.URL, serverURL, + ) return } r.apiServerURLs = append(r.apiServerURLs, serverURL) @@ -228,7 +231,10 @@ func (r *restConfigManager) parseConfig(cfg Config) { serverURL, err := url.Parse(apiServerURL) if err != nil { - r.log.WithError(err).Errorf("Failed to parse APIServerURL %s, skipping", apiServerURL) + r.log.Error("Failed to parse APIServerURL, skipping", + logfields.Error, err, + logfields.URL, apiServerURL, + ) continue } @@ -265,13 +271,15 @@ func (r *restConfigManager) rotateAPIServerURL() { r.Lock() r.restConfig.Host = r.rt.apiServerURL.String() r.Unlock() - r.log.WithField("url", r.rt.apiServerURL).Info("Rotated api server") + r.log.Info("Rotated api server", + logfields.URL, r.rt.apiServerURL, + ) } // rotatingHttpRoundTripper sets the remote host in the rest configuration used to make API requests to the API server. type rotatingHttpRoundTripper struct { delegate http.RoundTripper - log logrus.FieldLogger + log *slog.Logger apiServerURL *url.URL lock.RWMutex // Synchronizes access to apiServerURL } @@ -280,7 +288,9 @@ func (rt *rotatingHttpRoundTripper) RoundTrip(req *http.Request) (*http.Response rt.RLock() defer rt.RUnlock() - rt.log.WithField("host", rt.apiServerURL).Debug("Kubernetes api server host") + rt.log.Debug("Kubernetes api server host", + logfields.URL, rt.apiServerURL, + ) req.URL.Host = rt.apiServerURL.Host return rt.delegate.RoundTrip(req) } @@ -344,11 +354,16 @@ func (r *restConfigManager) k8sAPIServerFileWatcher(ctx context.Context, watcher if !event.Op.Has(fsnotify.Write) { continue } - r.log.WithField("file", K8sAPIServerFilePath).Info("Processing write event ") + r.log.Info("Processing write event", + logfields.Path, K8sAPIServerFilePath, + ) r.updateK8sAPIServerURL() case err := <-watcher.Errors: health.Degraded(fmt.Sprintf("Failed to load %q", K8sAPIServerFilePath), err) - r.log.WithField("file", K8sAPIServerFilePath).WithError(err).Error("Unexpected error while watching") + r.log.Error("Unexpected error while watching", + logfields.Path, K8sAPIServerFilePath, + logfields.Error, err, + ) } } @@ -357,23 +372,29 @@ func (r *restConfigManager) k8sAPIServerFileWatcher(ctx context.Context, watcher func (r *restConfigManager) updateK8sAPIServerURL() { f, err := os.Open(K8sAPIServerFilePath) if err != nil { - r.log.WithError(err).WithField(logfields.Path, K8sAPIServerFilePath).Error("unable " + - "to open file, agent may not be able to fail over to an active kube-apiserver") + r.log.Error("unable "+ + "to open file, agent may not be able to fail over to an active kube-apiserver", + logfields.Path, K8sAPIServerFilePath, + logfields.Error, err, + ) } defer f.Close() var mapping K8sServiceEndpointMapping if err = json.NewDecoder(f).Decode(&mapping); err != nil { - r.log.WithError(err).WithFields(logrus.Fields{ - logfields.Path: K8sAPIServerFilePath, - "entry": mapping, - }).Error("failed to " + - "decode file entry, agent may not be able to fail over to an active kube-apiserver") + r.log.Error("failed to "+ + "decode file entry, agent may not be able to fail over to an active kube-apiserver", + logfields.Error, err, + logfields.Path, K8sAPIServerFilePath, + logfields.Entry, mapping, + ) } if err = r.checkConnToService(mapping.Service); err != nil { return } - r.log.WithField("host", mapping.Service).Info("Updated kubeapi server url host") + r.log.Info("Updated kubeapi server url host", + logfields.URL, mapping.Service, + ) // Set in tests mapping.Service = strings.TrimPrefix(mapping.Service, "http://") r.rt.Lock() @@ -388,7 +409,10 @@ func (r *restConfigManager) updateK8sAPIServerURL() { endpoint = fmt.Sprintf("https://%s", endpoint) serverURL, err := url.Parse(endpoint) if err != nil { - r.log.WithError(err).Errorf("Failed to parse endpoint %s, skipping", endpoint) + r.log.Info("Failed to parse endpoint, skipping", + logfields.Endpoint, endpoint, + logfields.Error, err, + ) continue } updatedServerURLs = append(updatedServerURLs, serverURL) @@ -415,13 +439,17 @@ func (r *restConfigManager) checkConnToService(host string) error { hostURL := fmt.Sprintf("https://%s", host) config, err = rest.InClusterConfig() if err != nil { - log.WithError(err).Error("unable to read cluster config") + r.log.Error("unable to read cluster config", + logfields.Error, err, + ) return err } config.Host = hostURL } wait.Until(func() { - r.log.WithField(logfields.Address, config.Host).Info("Checking connection to kubeapi service") + r.log.Info("Checking connection to kubeapi service", + logfields.Address, config.Host, + ) httpClient, _ := rest.HTTPClientFor(config) cs, _ := kubernetes.NewForConfigAndClient(config, httpClient) @@ -436,11 +464,16 @@ func (r *restConfigManager) checkConnToService(host string) error { return } - r.log.WithError(err).WithField(logfields.Address, config.Host).Error("kubeapi service not ready yet") + r.log.Error("kubeapi service not ready yet", + logfields.Address, config.Host, + logfields.Error, err, + ) close(stop) }, connRetryInterval, stop) if err == nil { - r.log.WithField(logfields.Address, config.Host).Info("Connected to kubeapi service") + r.log.Info("Connected to kubeapi service", + logfields.Address, config.Host, + ) } return err } diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/endpoints.go b/vendor/github.com/cilium/cilium/pkg/k8s/endpoints.go index 9d25add5b8..f6d24ce150 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/endpoints.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/endpoints.go @@ -5,6 +5,7 @@ package k8s import ( "fmt" + "log/slog" "net" "net/netip" "slices" @@ -20,6 +21,7 @@ import ( slim_metav1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1" "github.com/cilium/cilium/pkg/k8s/types" "github.com/cilium/cilium/pkg/loadbalancer" + "github.com/cilium/cilium/pkg/logging/logfields" "github.com/cilium/cilium/pkg/metrics" "github.com/cilium/cilium/pkg/option" serviceStore "github.com/cilium/cilium/pkg/service/store" @@ -311,7 +313,7 @@ func parseEndpointPortV1Beta1(port slim_discovery_v1beta1.EndpointPort) (string, // ParseEndpointSliceV1 parses a Kubernetes EndpointSlice resource. // It reads ready and terminating state of endpoints in the EndpointSlice to // return an EndpointSlice ID and a filtered list of Endpoints for service load-balancing. -func ParseEndpointSliceV1(ep *slim_discovery_v1.EndpointSlice) *Endpoints { +func ParseEndpointSliceV1(logger *slog.Logger, ep *slim_discovery_v1.EndpointSlice) *Endpoints { endpoints := newEndpoints() endpoints.ObjectMeta = ep.ObjectMeta endpoints.EndpointSliceID = ParseEndpointSliceID(ep) @@ -322,7 +324,10 @@ func ParseEndpointSliceV1(ep *slim_discovery_v1.EndpointSlice) *Endpoints { return endpoints } - log.Debugf("Processing %d endpoints for EndpointSlice %s", len(ep.Endpoints), ep.Name) + logger.Debug("Processing endpoints for EndpointSlice", + logfields.LenEndpoints, len(ep.Endpoints), + logfields.Name, ep.Name, + ) for _, sub := range ep.Endpoints { // ready indicates that this endpoint is prepared to receive traffic, // according to whatever system is managing the endpoint. A nil value @@ -347,12 +352,20 @@ func ParseEndpointSliceV1(ep *slim_discovery_v1.EndpointSlice) *Endpoints { // allow endpoints that are Serving and Terminating if !isReady { if !option.Config.EnableK8sTerminatingEndpoint { - log.Debugf("discarding Endpoint on EndpointSlice %s: not Ready and EnableK8sTerminatingEndpoint %v", ep.Name, option.Config.EnableK8sTerminatingEndpoint) + logger.Debug( + "discarding Endpoint on EndpointSlice: not Ready", + logfields.Name, ep.Name, + logfields.EnableK8sTerminatingEndpoint, option.Config.EnableK8sTerminatingEndpoint, + ) continue } // filter not Serving endpoints since those can not receive traffic if !isServing { - log.Debugf("discarding Endpoint on EndpointSlice %s: not Serving and EnableK8sTerminatingEndpoint %v", ep.Name, option.Config.EnableK8sTerminatingEndpoint) + logger.Debug( + "discarding Endpoint on EndpointSlice: not Serving", + logfields.Name, ep.Name, + logfields.EnableK8sTerminatingEndpoint, option.Config.EnableK8sTerminatingEndpoint, + ) continue } } @@ -360,7 +373,12 @@ func ParseEndpointSliceV1(ep *slim_discovery_v1.EndpointSlice) *Endpoints { for _, addr := range sub.Addresses { addrCluster, err := cmtypes.ParseAddrCluster(addr) if err != nil { - log.WithError(err).Infof("Unable to parse address %s for EndpointSlices %s", addr, ep.Name) + logger.Info( + "Unable to parse address for EndpointSlices", + logfields.Error, err, + logfields.Address, addr, + logfields.Name, ep.Name, + ) continue } @@ -386,7 +404,11 @@ func ParseEndpointSliceV1(ep *slim_discovery_v1.EndpointSlice) *Endpoints { // If is not ready check if is serving and terminating if !isReady && option.Config.EnableK8sTerminatingEndpoint && isServing && isTerminating { - log.Debugf("Endpoint address %s on EndpointSlice %s is Terminating", addr, ep.Name) + logger.Debug( + "Endpoint address on EndpointSlice is Terminating", + logfields.Address, addr, + logfields.Name, ep.Name, + ) backend.Terminating = true metrics.TerminatingEndpointsEvents.Inc() } @@ -408,7 +430,11 @@ func ParseEndpointSliceV1(ep *slim_discovery_v1.EndpointSlice) *Endpoints { } } - log.Debugf("EndpointSlice %s has %d backends", ep.Name, len(endpoints.Backends)) + logger.Debug( + "EndpointSlice has backends", + logfields.LenBackends, len(endpoints.Backends), + logfields.Name, ep.Name, + ) return endpoints } diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/error_helpers.go b/vendor/github.com/cilium/cilium/pkg/k8s/error_helpers.go index afc217257a..cabaf799be 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/error_helpers.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/error_helpers.go @@ -9,6 +9,8 @@ import ( "strings" "github.com/cilium/cilium/pkg/lock" + "github.com/cilium/cilium/pkg/logging" + "github.com/cilium/cilium/pkg/logging/logfields" "github.com/cilium/cilium/pkg/time" ) @@ -48,6 +50,8 @@ func K8sErrorHandler(_ context.Context, e error, _ string, _ ...interface{}) { return } + logger := logging.DefaultSlogLogger + // We rate-limit certain categories of error message. These are matched // below, with a default behaviour to print everything else without // rate-limiting. @@ -59,7 +63,7 @@ func K8sErrorHandler(_ context.Context, e error, _ string, _ ...interface{}) { // trying to connect. case strings.Contains(errstr, "connection refused"): if k8sErrorUpdateCheckUnmuteTime(errstr, now) { - log.WithError(e).Error("k8sError") + logger.Error("k8sError", logfields.Error, e) } // k8s does not allow us to watch both ThirdPartyResource and @@ -68,7 +72,7 @@ func K8sErrorHandler(_ context.Context, e error, _ string, _ ...interface{}) { // that used ThirdPartyResource to define CiliumNetworkPolicy. case strings.Contains(errstr, "Failed to list *v2.CiliumNetworkPolicy: the server could not find the requested resource"): if k8sErrorUpdateCheckUnmuteTime(errstr, now) { - log.WithError(e).Error("No Cilium Network Policy CRD defined in the cluster, please set `--skip-crd-creation=false` to avoid seeing this error.") + logger.Error("No Cilium Network Policy CRD defined in the cluster, please set `--skip-crd-creation=false` to avoid seeing this error.", logfields.Error, e) } // fromCIDR and toCIDR used to expect an "ip" subfield (so, they were a YAML @@ -81,14 +85,16 @@ func K8sErrorHandler(_ context.Context, e error, _ string, _ ...interface{}) { strings.Contains(errstr, "Failed to list *v2.CiliumNetworkPolicy: only encoded map or array can be decoded into a struct"), strings.Contains(errstr, "Failed to list *v2.CiliumNetworkPolicy: v2.CiliumNetworkPolicyList:"): if k8sErrorUpdateCheckUnmuteTime(errstr, now) { - log.WithError(e).Error("Unable to decode k8s watch event") + logger.Error("Unable to decode k8s watch event", logfields.Error, e) } case k8sObjDecodeErrRe.MatchString(errstr): - log.WithError(e).Error("K8s client-go error indicates failure to decode k8s objs from apiserver." + - " This likely indicate issues with k8s apiserver sending malformed data or errors." + - " Such issues may be related to corrupted k8s etcd state.") + logger.Error("K8s client-go error indicates failure to decode k8s objs from apiserver."+ + " This likely indicate issues with k8s apiserver sending malformed data or errors."+ + " Such issues may be related to corrupted k8s etcd state.", + logfields.Error, e, + ) default: - log.WithError(e).Error("k8sError") + logger.Error("k8sError", logfields.Error, e) } } diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/informer/cast.go b/vendor/github.com/cilium/cilium/pkg/k8s/informer/cast.go index 16b672acf2..677e258b83 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/informer/cast.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/informer/cast.go @@ -4,6 +4,9 @@ package informer import ( + "fmt" + "log/slog" + "k8s.io/client-go/tools/cache" "github.com/cilium/cilium/pkg/logging/logfields" @@ -12,7 +15,7 @@ import ( // CastInformerEvent tries to cast obj to type typ, directly // or by DeletedFinalStateUnknown type. It returns nil and logs // an error if obj doesn't contain type typ. -func CastInformerEvent[typ any](obj interface{}) *typ { +func CastInformerEvent[typ any](logger *slog.Logger, obj interface{}) *typ { k8sObj, ok := obj.(*typ) if ok { return k8sObj @@ -27,7 +30,9 @@ func CastInformerEvent[typ any](obj interface{}) *typ { return k8sObj } } - log.WithField(logfields.Object, logfields.Repr(obj)). - Warnf("Ignoring invalid type, expected: %T", new(typ)) + logger.Warn( + fmt.Sprintf("Ignoring invalid type, expected: %T", new(typ)), + logfields.Object, obj, + ) return nil } diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/informer/informer.go b/vendor/github.com/cilium/cilium/pkg/k8s/informer/informer.go index 274aa61700..25b248620f 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/informer/informer.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/informer/informer.go @@ -15,12 +15,9 @@ import ( "github.com/cilium/cilium/pkg/k8s/watchers/resources" "github.com/cilium/cilium/pkg/logging" - "github.com/cilium/cilium/pkg/logging/logfields" "github.com/cilium/cilium/pkg/time" ) -var log = logging.DefaultLogger.WithField(logfields.LogSubsys, "k8s") - func init() { utilRuntime.PanicHandlers = append( utilRuntime.PanicHandlers, @@ -33,7 +30,7 @@ func init() { // panicking with ErrAbortHandler also suppresses logging of a stack trace to the server's error log. return } - log.Fatal("Panic in Kubernetes runtime handler") + logging.Fatal(logging.DefaultSlogLogger, "Panic in Kubernetes runtime handler") }, ) } diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/labels.go b/vendor/github.com/cilium/cilium/pkg/k8s/labels.go index 349ffbf767..825c4746d9 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/labels.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/labels.go @@ -4,7 +4,7 @@ package k8s import ( - "github.com/sirupsen/logrus" + "log/slog" slim_corev1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/api/core/v1" k8sUtils "github.com/cilium/cilium/pkg/k8s/utils" @@ -19,13 +19,13 @@ const UseOriginalSourceAddressLabel = "cilium.io/use-original-source-address" // GetPodMetadata returns the labels and annotations of the pod with the given // namespace / name. -func GetPodMetadata(k8sNs *slim_corev1.Namespace, pod *slim_corev1.Pod) (containerPorts []slim_corev1.ContainerPort, lbls map[string]string) { +func GetPodMetadata(logger *slog.Logger, k8sNs *slim_corev1.Namespace, pod *slim_corev1.Pod) (containerPorts []slim_corev1.ContainerPort, lbls map[string]string) { namespace := pod.Namespace - scopedLog := log.WithFields(logrus.Fields{ - logfields.K8sNamespace: namespace, - logfields.K8sPodName: pod.Name, - }) - scopedLog.Debug("Connecting to k8s local stores to retrieve labels for pod") + logger.Debug( + "Connecting to k8s local stores to retrieve labels for pod", + logfields.K8sNamespace, namespace, + logfields.K8sPodName, pod.Name, + ) objMetaCpy := pod.ObjectMeta.DeepCopy() labels := k8sUtils.SanitizePodLabels(objMetaCpy.Labels, k8sNs, pod.Spec.ServiceAccountName, option.Config.ClusterName) diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/logfields.go b/vendor/github.com/cilium/cilium/pkg/k8s/logfields.go deleted file mode 100644 index bf6b46bfa9..0000000000 --- a/vendor/github.com/cilium/cilium/pkg/k8s/logfields.go +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package k8s - -import ( - "github.com/cilium/cilium/pkg/logging" - "github.com/cilium/cilium/pkg/logging/logfields" -) - -// logging field definitions -const ( - // subsysK8s is the value for logfields.LogSubsys - subsysK8s = "k8s" -) - -var ( - // log is the k8s package logger object. - log = logging.DefaultLogger.WithField(logfields.LogSubsys, subsysK8s) -) diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/network_policy.go b/vendor/github.com/cilium/cilium/pkg/k8s/network_policy.go index 309abc2bd1..397911c4b7 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/network_policy.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/network_policy.go @@ -5,6 +5,9 @@ package k8s import ( "fmt" + "log/slog" + "maps" + "slices" "github.com/cilium/cilium/pkg/annotation" k8sConst "github.com/cilium/cilium/pkg/k8s/apis/cilium.io" @@ -32,9 +35,9 @@ var ( // GetPolicyLabelsv1 extracts the name of np. It uses the name from the Cilium // annotation if present. If the policy's annotations do not contain // the Cilium annotation, the policy's name field is used instead. -func GetPolicyLabelsv1(np *slim_networkingv1.NetworkPolicy) labels.LabelArray { +func GetPolicyLabelsv1(logger *slog.Logger, np *slim_networkingv1.NetworkPolicy) labels.LabelArray { if np == nil { - log.Warningf("unable to extract policy labels because provided NetworkPolicy is nil") + logger.Warn("unable to extract policy labels because provided NetworkPolicy is nil") return nil } @@ -103,18 +106,13 @@ func parseNetworkPolicyPeer(namespace string, peer *slim_networkingv1.NetworkPol } func hasV1PolicyType(pTypes []slim_networkingv1.PolicyType, typ slim_networkingv1.PolicyType) bool { - for _, pType := range pTypes { - if pType == typ { - return true - } - } - return false + return slices.Contains(pTypes, typ) } // ParseNetworkPolicy parses a k8s NetworkPolicy. Returns a list of // Cilium policy rules that can be added, along with an error if there was an // error sanitizing the rules. -func ParseNetworkPolicy(np *slim_networkingv1.NetworkPolicy) (api.Rules, error) { +func ParseNetworkPolicy(logger *slog.Logger, np *slim_networkingv1.NetworkPolicy) (api.Rules, error) { if np == nil { return nil, fmt.Errorf("cannot parse NetworkPolicy because it is nil") @@ -138,7 +136,7 @@ func ParseNetworkPolicy(np *slim_networkingv1.NetworkPolicy) (api.Rules, error) ingress.FromEndpoints = append(ingress.FromEndpoints, *endpointSelector) } else { // No label-based selectors were in NetworkPolicyPeer. - log.WithField(logfields.K8sNetworkPolicyName, np.Name).Debug("NetworkPolicyPeer does not have PodSelector or NamespaceSelector") + logger.Debug("NetworkPolicyPeer does not have PodSelector or NamespaceSelector", logfields.K8sNetworkPolicyName, np.Name) } // Parse CIDR-based parts of rule. @@ -182,7 +180,7 @@ func ParseNetworkPolicy(np *slim_networkingv1.NetworkPolicy) (api.Rules, error) if endpointSelector != nil { egress.ToEndpoints = append(egress.ToEndpoints, *endpointSelector) } else { - log.WithField(logfields.K8sNetworkPolicyName, np.Name).Debug("NetworkPolicyPeer does not have PodSelector or NamespaceSelector") + logger.Debug("NetworkPolicyPeer does not have PodSelector or NamespaceSelector", logfields.K8sNetworkPolicyName, np.Name) } } if rule.IPBlock != nil { @@ -214,7 +212,7 @@ func ParseNetworkPolicy(np *slim_networkingv1.NetworkPolicy) (api.Rules, error) } // Convert the k8s default-deny model to the Cilium default-deny model - //spec: + // spec: // podSelector: {} // policyTypes: // - Ingress @@ -227,7 +225,7 @@ func ParseNetworkPolicy(np *slim_networkingv1.NetworkPolicy) (api.Rules, error) } // Convert the k8s default-deny model to the Cilium default-deny model - //spec: + // spec: // podSelector: {} // policyTypes: // - Egress @@ -240,7 +238,7 @@ func ParseNetworkPolicy(np *slim_networkingv1.NetworkPolicy) (api.Rules, error) // The next patch will pass the UID. rule := api.NewRule(). WithEndpointSelector(api.NewESFromK8sLabelSelector(labels.LabelSourceK8sKeyPrefix, podSelector)). - WithLabels(GetPolicyLabelsv1(np)). + WithLabels(GetPolicyLabelsv1(logger, np)). WithIngressRules(ingresses). WithEgressRules(egresses) @@ -255,9 +253,7 @@ func parsePodSelector(podSelectorIn *slim_metav1.LabelSelector, namespace string podSelector := &slim_metav1.LabelSelector{ MatchLabels: make(map[string]slim_metav1.MatchLabelsValue, len(podSelectorIn.MatchLabels)), } - for k, v := range podSelectorIn.MatchLabels { - podSelector.MatchLabels[k] = v - } + maps.Copy(podSelector.MatchLabels, podSelectorIn.MatchLabels) // The PodSelector should only reflect to the same namespace // the policy is being stored, thus we add the namespace to // the MatchLabels map. diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/node.go b/vendor/github.com/cilium/cilium/pkg/k8s/node.go index 63c185e37e..530c31ba51 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/node.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/node.go @@ -5,11 +5,10 @@ package k8s import ( "fmt" + "log/slog" "net" "strconv" - "github.com/sirupsen/logrus" - "github.com/cilium/cilium/pkg/annotation" "github.com/cilium/cilium/pkg/cidr" slim_corev1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/api/core/v1" @@ -43,12 +42,12 @@ type nodeAddressGroup struct { } // ParseNode parses a kubernetes node to a cilium node -func ParseNode(k8sNode *slim_corev1.Node, source source.Source) *nodeTypes.Node { +func ParseNode(logger *slog.Logger, k8sNode *slim_corev1.Node, source source.Source) *nodeTypes.Node { addrGroups := make(map[nodeAddressGroup]struct{}) - scopedLog := log.WithFields(logrus.Fields{ - logfields.NodeName: k8sNode.Name, - logfields.K8sNodeID: k8sNode.UID, - }) + scopedLog := logger.With( + logfields.NodeName, k8sNode.Name, + logfields.K8sNodeID, k8sNode.UID, + ) addrs := []nodeTypes.Address{} for _, addr := range k8sNode.Status.Addresses { // We only care about this address types, @@ -73,25 +72,29 @@ func ParseNode(k8sNode *slim_corev1.Node, source source.Source) *nodeTypes.Node case ip != nil && ip.To16() != nil: addrGroup.family = slim_corev1.IPv6Protocol default: - scopedLog.WithFields(logrus.Fields{ - logfields.IPAddr: addr.Address, - logfields.Type: addr.Type, - }).Warn("Ignoring invalid node IP") + scopedLog.Warn( + "Ignoring invalid node IP", + logfields.IPAddr, addr.Address, + logfields.Type, addr.Type, + ) continue } _, groupFound := addrGroups[addrGroup] if groupFound { - scopedLog.WithFields(logrus.Fields{ - logfields.Node: k8sNode.Name, - logfields.Type: addr.Type, - }).Warn("Detected multiple IPs of the same address type and family, Cilium will only consider the first IP in the Node resource") + scopedLog.Warn( + "Detected multiple IPs of the same address type and family, Cilium will only consider the first IP in the Node resource", + logfields.Type, addr.Type, + ) continue } addrGroups[addrGroup] = struct{}{} addressType, err := ParseNodeAddressType(addr.Type) if err != nil { - scopedLog.WithError(err).Warn("invalid address type for node") + scopedLog.Warn( + "invalid address type for node", + logfields.Error, err, + ) } na := nodeTypes.Address{ @@ -109,11 +112,19 @@ func ParseNode(k8sNode *slim_corev1.Node, source source.Source) *nodeTypes.Node if len(k8sNode.Spec.PodCIDRs) != 0 { if len(k8sNode.Spec.PodCIDRs) > 2 { - scopedLog.WithField("podCIDR", k8sNode.Spec.PodCIDRs).Errorf("Invalid PodCIDRs expected 1 or 2 PodCIDRs, received %d", len(k8sNode.Spec.PodCIDRs)) + scopedLog.Error( + "Invalid PodCIDRs expected 1 or 2 PodCIDRs", + logfields.PodCIDRs, k8sNode.Spec.PodCIDRs, + logfields.LenIPs, len(k8sNode.Spec.PodCIDRs), + ) } else { for _, podCIDR := range k8sNode.Spec.PodCIDRs { if allocCIDR, err := cidr.ParseCIDR(podCIDR); err != nil { - scopedLog.WithError(err).WithField("podCIDR", k8sNode.Spec.PodCIDR).Warn("Invalid PodCIDR value for node") + scopedLog.Warn( + "Invalid PodCIDR value for node", + logfields.Error, err, + logfields.PodCIDRs, k8sNode.Spec.PodCIDRs, + ) } else { if allocCIDR.IP.To4() != nil { newNode.IPv4AllocCIDR = allocCIDR @@ -125,7 +136,11 @@ func ParseNode(k8sNode *slim_corev1.Node, source source.Source) *nodeTypes.Node } } else if len(k8sNode.Spec.PodCIDR) != 0 { if allocCIDR, err := cidr.ParseCIDR(k8sNode.Spec.PodCIDR); err != nil { - scopedLog.WithError(err).WithField(logfields.V4Prefix, k8sNode.Spec.PodCIDR).Warn("Invalid PodCIDR value for node") + scopedLog.Warn( + "Invalid PodCIDR value for node", + logfields.Error, err, + logfields.V4Prefix, k8sNode.Spec.PodCIDR, + ) } else { if allocCIDR.IP.To4() != nil { newNode.IPv4AllocCIDR = allocCIDR @@ -153,16 +168,26 @@ func ParseNode(k8sNode *slim_corev1.Node, source source.Source) *nodeTypes.Node k8sNodeAddHostIP := func(key string, alias string) { if ciliumInternalIP, ok := annotation.Get(k8sNode, key, alias); !ok || ciliumInternalIP == "" { - scopedLog.Debugf("Missing %s (or %s). Annotation required when IPSec Enabled", key, alias) + scopedLog.Debug( + "Annotation required when IPSec Enabled. Missing key or its alias.", + logfields.Key, key, + logfields.Alias, alias, + ) } else if ip := net.ParseIP(ciliumInternalIP); ip == nil { - scopedLog.Debugf("ParseIP %s error", ciliumInternalIP) + scopedLog.Debug( + "Parse IP error", + logfields.IPAddr, ciliumInternalIP, + ) } else { na := nodeTypes.Address{ Type: addressing.NodeCiliumInternalIP, IP: ip, } addrs = append(addrs, na) - scopedLog.Debugf("Add NodeCiliumInternalIP: %s", ip) + scopedLog.Debug( + "Add NodeCiliumInternalIP", + logfields.IPAddr, ip, + ) } } @@ -181,11 +206,17 @@ func ParseNode(k8sNode *slim_corev1.Node, source source.Source) *nodeTypes.Node // In case it's invalid or empty then we fall back to our annotations. if newNode.IPv4AllocCIDR == nil { if ipv4CIDR, ok := annotation.Get(k8sNode, annotation.V4CIDRName, annotation.V4CIDRNameAlias); !ok || ipv4CIDR == "" { - scopedLog.Debug("Empty IPv4 CIDR annotation in node") + scopedLog.Debug( + "Empty IPv4 CIDR annotation in node", + ) } else { allocCIDR, err := cidr.ParseCIDR(ipv4CIDR) if err != nil { - scopedLog.WithError(err).WithField(logfields.V4Prefix, ipv4CIDR).Error("BUG, invalid IPv4 annotation CIDR in node") + scopedLog.Error( + "BUG, invalid IPv4 annotation CIDR in node", + logfields.Error, err, + logfields.V4Prefix, ipv4CIDR, + ) } else { newNode.IPv4AllocCIDR = allocCIDR } @@ -194,11 +225,17 @@ func ParseNode(k8sNode *slim_corev1.Node, source source.Source) *nodeTypes.Node if newNode.IPv6AllocCIDR == nil { if ipv6CIDR, ok := annotation.Get(k8sNode, annotation.V6CIDRName, annotation.V6CIDRNameAlias); !ok || ipv6CIDR == "" { - scopedLog.Debug("Empty IPv6 CIDR annotation in node") + scopedLog.Debug( + "Empty IPv6 CIDR annotation in node", + ) } else { allocCIDR, err := cidr.ParseCIDR(ipv6CIDR) if err != nil { - scopedLog.WithError(err).WithField(logfields.V6Prefix, ipv6CIDR).Error("BUG, invalid IPv6 annotation CIDR in node") + scopedLog.Error( + "BUG, invalid IPv6 annotation CIDR in node", + logfields.Error, err, + logfields.V6Prefix, ipv6CIDR, + ) } else { newNode.IPv6AllocCIDR = allocCIDR } @@ -207,9 +244,14 @@ func ParseNode(k8sNode *slim_corev1.Node, source source.Source) *nodeTypes.Node if newNode.IPv4HealthIP == nil { if healthIP, ok := annotation.Get(k8sNode, annotation.V4HealthName, annotation.V4HealthNameAlias); !ok || healthIP == "" { - scopedLog.Debug("Empty IPv4 health endpoint annotation in node") + scopedLog.Debug( + "Empty IPv4 health endpoint annotation in node", + ) } else if ip := net.ParseIP(healthIP); ip == nil { - scopedLog.WithField(logfields.V4HealthIP, healthIP).Error("BUG, invalid IPv4 health endpoint annotation in node") + scopedLog.Error( + "BUG, invalid IPv4 health endpoint annotation in node", + logfields.V4HealthIP, healthIP, + ) } else { newNode.IPv4HealthIP = ip } @@ -217,9 +259,14 @@ func ParseNode(k8sNode *slim_corev1.Node, source source.Source) *nodeTypes.Node if newNode.IPv6HealthIP == nil { if healthIP, ok := annotation.Get(k8sNode, annotation.V6HealthName, annotation.V6HealthNameAlias); !ok || healthIP == "" { - scopedLog.Debug("Empty IPv6 health endpoint annotation in node") + scopedLog.Debug( + "Empty IPv6 health endpoint annotation in node", + ) } else if ip := net.ParseIP(healthIP); ip == nil { - scopedLog.WithField(logfields.V6HealthIP, healthIP).Error("BUG, invalid IPv6 health endpoint annotation in node") + scopedLog.Error( + "BUG, invalid IPv6 health endpoint annotation in node", + logfields.V6HealthIP, healthIP, + ) } else { newNode.IPv6HealthIP = ip } @@ -227,9 +274,14 @@ func ParseNode(k8sNode *slim_corev1.Node, source source.Source) *nodeTypes.Node if newNode.IPv4IngressIP == nil { if ingressIP, ok := annotation.Get(k8sNode, annotation.V4IngressName, annotation.V4IngressNameAlias); !ok || ingressIP == "" { - scopedLog.Debug("Empty IPv4 Ingress annotation in node") + scopedLog.Debug( + "Empty IPv4 Ingress annotation in node", + ) } else if ip := net.ParseIP(ingressIP); ip == nil { - scopedLog.WithField(logfields.V4IngressIP, ingressIP).Error("BUG, invalid IPv4 Ingress annotation in node") + scopedLog.Error( + "BUG, invalid IPv4 Ingress annotation in node", + logfields.V4IngressIP, ingressIP, + ) } else { newNode.IPv4IngressIP = ip } @@ -237,9 +289,14 @@ func ParseNode(k8sNode *slim_corev1.Node, source source.Source) *nodeTypes.Node if newNode.IPv6IngressIP == nil { if ingressIP, ok := annotation.Get(k8sNode, annotation.V6IngressName, annotation.V6IngressNameAlias); !ok || ingressIP == "" { - scopedLog.Debug("Empty IPv6 Ingress annotation in node") + scopedLog.Debug( + "Empty IPv6 Ingress annotation in node", + ) } else if ip := net.ParseIP(ingressIP); ip == nil { - scopedLog.WithField(logfields.V6IngressIP, ingressIP).Error("BUG, invalid IPv6 Ingress annotation in node") + scopedLog.Error( + "BUG, invalid IPv6 Ingress annotation in node", + logfields.V6IngressIP, ingressIP, + ) } else { newNode.IPv6IngressIP = ip } diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/resource/resource.go b/vendor/github.com/cilium/cilium/pkg/k8s/resource/resource.go index db75b9be2e..b5b6924001 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/resource/resource.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/resource/resource.go @@ -138,7 +138,9 @@ type Resource[T k8sRuntime.Object] interface { // See also pkg/k8s/resource/example/main.go for a runnable example. func New[T k8sRuntime.Object](lc cell.Lifecycle, lw cache.ListerWatcher, opts ...ResourceOption) Resource[T] { r := &resource[T]{ - lw: lw, + subscribers: make(map[uint64]*subscriber[T]), + needed: make(chan struct{}, 1), + lw: lw, } r.opts.sourceObj = func() k8sRuntime.Object { var obj T @@ -148,7 +150,7 @@ func New[T k8sRuntime.Object](lc cell.Lifecycle, lw cache.ListerWatcher, opts .. o(&r.opts) } r.ctx, r.cancel = context.WithCancel(context.Background()) - r.reset() + r.storeResolver, r.storePromise = promise.New[Store[T]]() lc.Append(r) return r } @@ -159,7 +161,6 @@ type options struct { indexers cache.Indexers // map of the optional custom indexers to be added to the underlying resource informer metricScope string // the scope label used when recording metrics for the resource name string // the name label used for the workqueue metrics - releasable bool // if true, the underlying informer will be stopped when the last subscriber cancels its subscription crdSyncPromise promise.Promise[synced.CRDSync] // optional promise to wait for } @@ -220,23 +221,6 @@ func WithCRDSync(crdSyncPromise promise.Promise[synced.CRDSync]) ResourceOption } } -// WithStoppableInformer marks the resource as releasable. A releasable resource stops -// the underlying informer if the last active subscriber cancels its subscription. -// In this case the resource is stopped and prepared again for a subsequent call to -// either Events() or Store(). -// A subscriber is a consumer who has taken a reference to the store with Store() or that -// is listening to the events stream channel with Events(). -// This option is meant to be used for very specific cases of resources with a high rate -// of updates that can potentially hinder scalability in very large clusters, like -// CiliumNode and CiliumEndpoint. -// For this cases, stopping the informer is required when switching to other data sources -// that scale better. -func WithStoppableInformer() ResourceOption { - return func(o *options) { - o.releasable = true - } -} - type resource[T k8sRuntime.Object] struct { mu lock.RWMutex ctx context.Context @@ -254,12 +238,6 @@ type resource[T k8sRuntime.Object] struct { storePromise promise.Promise[Store[T]] storeResolver promise.Resolver[Store[T]] - - // meaningful for releasable resources only - refsMu lock.Mutex - refs uint64 - resetCtx context.Context - resetCancel context.CancelFunc } var _ Resource[*corev1.Node] = &resource[*corev1.Node]{} @@ -274,14 +252,11 @@ func (r *resource[T]) Store(ctx context.Context) (Store[T], error) { defer r.mu.RUnlock() return r.synchronized } - cache.WaitForCacheSync(ctx.Done(), hasSynced) + if !cache.WaitForCacheSync(ctx.Done(), hasSynced) { + return nil, ctx.Err() + } - // use an error handler to release the resource if the store promise - // is rejected or the context is cancelled before the cache has synchronized. - return promise.MapError(r.storePromise, func(err error) error { - r.release() - return err - }).Await(ctx) + return r.storePromise.Await(ctx) } func (r *resource[T]) metricEventProcessed(eventKind EventKind, status bool) { @@ -321,26 +296,12 @@ func (r *resource[T]) metricEventReceived(action string, valid, equal bool) { } func (r *resource[T]) Start(cell.HookContext) error { - r.start() - return nil -} - -func (r *resource[T]) start() { - // Don't start the resource if it has been definitely stopped - if r.ctx.Err() != nil { - return - } r.wg.Add(1) go r.startWhenNeeded() + return nil } func (r *resource[T]) markNeeded() { - if r.opts.releasable { - r.refsMu.Lock() - r.refs++ - r.refsMu.Unlock() - } - select { case r.needed <- struct{}{}: default: @@ -368,19 +329,16 @@ func (r *resource[T]) startWhenNeeded() { } store, informer := r.newInformer() - r.storeResolver.Resolve(&typedStore[T]{ - store: store, - release: r.release, - }) + r.storeResolver.Resolve(&typedStore[T]{store}) r.wg.Add(1) go func() { defer r.wg.Done() - informer.Run(merge(r.ctx.Done(), r.resetCtx.Done())) + informer.Run(r.ctx.Done()) }() // Wait for cache to be synced before emitting the sync event. - if cache.WaitForCacheSync(merge(r.ctx.Done(), r.resetCtx.Done()), informer.HasSynced) { + if cache.WaitForCacheSync(r.ctx.Done(), informer.HasSynced) { // Emit the sync event for all subscribers. Subscribers // that subscribe afterwards will emit it by checking // r.synchronized. @@ -394,12 +352,6 @@ func (r *resource[T]) startWhenNeeded() { } func (r *resource[T]) Stop(stopCtx cell.HookContext) error { - if r.opts.releasable { - // grab the refs lock to avoid a concurrent restart for releasable resource - r.refsMu.Lock() - defer r.refsMu.Unlock() - } - r.cancel() r.wg.Wait() return nil @@ -475,7 +427,6 @@ func (r *resource[T]) Events(ctx context.Context, opts ...EventsOpt) <-chan Even // Fork a goroutine to process the queued keys and pass them to the subscriber. r.wg.Add(1) go func() { - defer r.release() defer r.wg.Done() defer close(out) @@ -522,7 +473,6 @@ func (r *resource[T]) Events(ctx context.Context, opts ...EventsOpt) <-chan Even defer r.wg.Done() select { case <-r.ctx.Done(): - case <-r.resetCtx.Done(): case <-ctx.Done(): } subCancel() @@ -532,39 +482,6 @@ func (r *resource[T]) Events(ctx context.Context, opts ...EventsOpt) <-chan Even return out } -func (r *resource[T]) release() { - if !r.opts.releasable { - return - } - - // in case of a releasable resource, stop the underlying informer when the last - // reference to it is released. The resource is restarted to be - // ready again in case of a subsequent call to either Events() or Store(). - - r.refsMu.Lock() - defer r.refsMu.Unlock() - - r.refs-- - if r.refs > 0 { - return - } - - r.resetCancel() - r.wg.Wait() - close(r.needed) - - r.reset() - r.start() -} - -func (r *resource[T]) reset() { - r.subscribers = make(map[uint64]*subscriber[T]) - r.needed = make(chan struct{}, 1) - r.synchronized = false - r.storeResolver, r.storePromise = promise.New[Store[T]]() - r.resetCtx, r.resetCancel = context.WithCancel(context.Background()) -} - func (r *resource[T]) resourceName() string { if r.opts.name != "" { return r.opts.name @@ -913,15 +830,3 @@ func getUID(obj k8sRuntime.Object) types.UID { } return meta.GetUID() } - -func merge[T any](c1, c2 <-chan T) <-chan T { - m := make(chan T) - go func() { - select { - case <-c1: - case <-c2: - } - close(m) - }() - return m -} diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/resource/store.go b/vendor/github.com/cilium/cilium/pkg/k8s/resource/store.go index 9dec4cbad0..4dc0b892d5 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/resource/store.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/resource/store.go @@ -34,17 +34,11 @@ type Store[T k8sRuntime.Object] interface { // CacheStore returns the underlying cache.Store instance. Use for temporary // compatibility purposes only! CacheStore() cache.Store - - // Release the store and allows the associated resource to stop its informer if - // this is the last reference to it. - // This is a no-op if the resource is not releasable. - Release() } // typedStore implements Store on top of an untyped cache.Indexer. type typedStore[T k8sRuntime.Object] struct { - store cache.Indexer - release func() + store cache.Indexer } var _ Store[*corev1.Node] = &typedStore[*corev1.Node]{} @@ -95,10 +89,6 @@ func (s *typedStore[T]) CacheStore() cache.Store { return s.store } -func (s *typedStore[T]) Release() { - s.release() -} - type KeyIter interface { // Next returns true if there is a key, false if iteration has finished. Next() bool diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/resource_ctors.go b/vendor/github.com/cilium/cilium/pkg/k8s/resource_ctors.go index 4a19ea5262..6e8ef0e106 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/resource_ctors.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/resource_ctors.go @@ -5,18 +5,19 @@ package k8s import ( "fmt" + "log/slog" "sync" - "github.com/cilium/cilium/pkg/allocator" - "github.com/cilium/cilium/pkg/identity/key" - "github.com/cilium/hive/cell" + "github.com/cloudflare/cfssl/log" "github.com/spf13/pflag" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" k8sRuntime "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/tools/cache" + "github.com/cilium/cilium/pkg/allocator" + "github.com/cilium/cilium/pkg/identity/key" cilium_api_v2 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2" cilium_api_v2alpha1 "github.com/cilium/cilium/pkg/k8s/apis/cilium.io/v2alpha1" "github.com/cilium/cilium/pkg/k8s/client" @@ -138,7 +139,6 @@ func CiliumNodeResource(params CiliumResourceParams, opts ...func(*metav1.ListOp ) return resource.New[*cilium_api_v2.CiliumNode](params.Lifecycle, lw, resource.WithMetric("CiliumNode"), - resource.WithStoppableInformer(), resource.WithCRDSync(params.CRDSyncPromise), // optional, can be nil ), nil } @@ -295,7 +295,7 @@ func CiliumBGPPeerConfigResource(params CiliumResourceParams, opts ...func(*meta return resource.New[*cilium_api_v2.CiliumBGPPeerConfig](params.Lifecycle, lw, resource.WithMetric("CiliumBGPPeerConfig"), resource.WithCRDSync(params.CRDSyncPromise)), nil } -func EndpointsResource(lc cell.Lifecycle, cfg Config, cs client.Clientset, opts ...func(*metav1.ListOptions)) (resource.Resource[*Endpoints], error) { +func EndpointsResource(logger *slog.Logger, lc cell.Lifecycle, cfg Config, cs client.Clientset, opts ...func(*metav1.ListOptions)) (resource.Resource[*Endpoints], error) { if !cs.IsEnabled() { return nil, nil } @@ -318,7 +318,9 @@ func EndpointsResource(lc cell.Lifecycle, cfg Config, cs client.Clientset, opts return resource.New[*Endpoints]( lc, lw, - resource.WithLazyTransform(lw.getSourceObj, transformEndpoint), + resource.WithLazyTransform(lw.getSourceObj, func(i any) (any, error) { + return transformEndpoint(logger, i) + }), resource.WithMetric("Endpoint"), resource.WithName("endpoints"), ), nil @@ -380,12 +382,12 @@ func (lw *endpointsListerWatcher) Watch(opts metav1.ListOptions) (watch.Interfac return lw.getListerWatcher().Watch(opts) } -func transformEndpoint(obj any) (any, error) { +func transformEndpoint(logger *slog.Logger, obj any) (any, error) { switch obj := obj.(type) { case *slim_corev1.Endpoints: return ParseEndpoints(obj), nil case *slim_discoveryv1.EndpointSlice: - return ParseEndpointSliceV1(obj), nil + return ParseEndpointSliceV1(logger, obj), nil case *slim_discoveryv1beta1.EndpointSlice: return ParseEndpointSliceV1Beta1(obj), nil default: @@ -415,7 +417,6 @@ func CiliumSlimEndpointResource(params CiliumResourceParams, _ *node.LocalNodeSt }, TransformToCiliumEndpoint), resource.WithMetric("CiliumEndpoint"), resource.WithIndexers(indexers), - resource.WithStoppableInformer(), resource.WithCRDSync(params.CRDSyncPromise), ), nil } @@ -429,8 +430,10 @@ func ciliumEndpointLocalPodIndexFunc(obj any) ([]string, error) { } indices := []string{} if cep.Networking == nil { - log.WithField("ciliumendpoint", cep.GetNamespace()+"/"+cep.GetName()). - Debug("cannot index CiliumEndpoint by node without network status") + log.Debug( + "cannot index CiliumEndpoint by node without network status", + slog.Any("ciliumendpoint", cep.GetNamespace()+"/"+cep.GetName()), + ) return nil, nil } if cep.Networking.NodeIP == node.GetCiliumEndpointNodeIP() { @@ -458,7 +461,6 @@ func CiliumEndpointSliceResource(params CiliumResourceParams, _ *node.LocalNodeS return resource.New[*cilium_api_v2alpha1.CiliumEndpointSlice](params.Lifecycle, lw, resource.WithMetric("CiliumEndpointSlice"), resource.WithIndexers(indexers), - resource.WithStoppableInformer(), resource.WithCRDSync(params.CRDSyncPromise), ), nil } diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/service.go b/vendor/github.com/cilium/cilium/pkg/k8s/service.go index 108632133a..f137d7846a 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/service.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/service.go @@ -5,12 +5,12 @@ package k8s import ( "fmt" + "log/slog" "maps" "net" "net/netip" "strings" - "github.com/sirupsen/logrus" v1 "k8s.io/api/core/v1" "github.com/cilium/cilium/pkg/annotation" @@ -150,7 +150,7 @@ func isValidServiceFrontendIP(netIP net.IP) bool { // latter two, one can set the annotation with the value "LoadBalancer". type exposeSvcType slim_corev1.ServiceType -func newSvcExposureType(svc *slim_corev1.Service) (*exposeSvcType, error) { +func NewSvcExposureType(svc *slim_corev1.Service) (*exposeSvcType, error) { typ, isSet := svc.Annotations[annotation.ServiceTypeExposure] if !isSet { return nil, nil @@ -171,8 +171,8 @@ func newSvcExposureType(svc *slim_corev1.Service) (*exposeSvcType, error) { return &expType, nil } -// canExpose checks whether a given service type can be provisioned. -func (e *exposeSvcType) canExpose(t slim_corev1.ServiceType) bool { +// CanExpose checks whether a given service type can be provisioned. +func (e *exposeSvcType) CanExpose(t slim_corev1.ServiceType) bool { if e == nil { return true } @@ -189,19 +189,23 @@ func ParseServiceID(svc *slim_corev1.Service) ServiceID { } // ParseService parses a Kubernetes service and returns a Service. -func ParseService(svc *slim_corev1.Service, nodePortAddrs []netip.Addr) (ServiceID, *Service) { - scopedLog := log.WithFields(logrus.Fields{ - logfields.K8sSvcName: svc.ObjectMeta.Name, - logfields.K8sNamespace: svc.ObjectMeta.Namespace, - logfields.K8sAPIVersion: svc.TypeMeta.APIVersion, - logfields.K8sSvcType: svc.Spec.Type, - }) +func ParseService(logger *slog.Logger, svc *slim_corev1.Service, nodePortAddrs []netip.Addr) (ServiceID, *Service) { + scopedLog := logger.With( + logfields.K8sSvcName, svc.ObjectMeta.Name, + logfields.K8sNamespace, svc.ObjectMeta.Namespace, + logfields.K8sAPIVersion, svc.TypeMeta.APIVersion, + logfields.K8sSvcType, svc.Spec.Type, + ) var loadBalancerIPs []string svcID := ParseServiceID(svc) - expType, err := newSvcExposureType(svc) + expType, err := NewSvcExposureType(svc) if err != nil { - scopedLog.WithError(err).Warnf("Ignoring %q annotation", annotation.ServiceTypeExposure) + scopedLog.Warn( + "Ignoring annotation", + logfields.Error, err, + logfields.Annotation, annotation.ServiceTypeExposure, + ) } var svcType loadbalancer.SVCType @@ -220,7 +224,9 @@ func ParseService(svc *slim_corev1.Service, nodePortAddrs []netip.Addr) (Service return ServiceID{}, nil default: - scopedLog.Warn("Ignoring k8s service: unsupported type") + scopedLog.Warn( + "Ignoring k8s service: unsupported type", + ) return ServiceID{}, nil } @@ -229,7 +235,7 @@ func ParseService(svc *slim_corev1.Service, nodePortAddrs []netip.Addr) (Service } var clusterIPs []net.IP - if expType.canExpose(slim_corev1.ServiceTypeClusterIP) { + if expType.CanExpose(slim_corev1.ServiceTypeClusterIP) { if len(svc.Spec.ClusterIPs) == 0 { if clsIP := net.ParseIP(svc.Spec.ClusterIP); clsIP != nil { clusterIPs = []net.IP{clsIP} @@ -265,7 +271,7 @@ func ParseService(svc *slim_corev1.Service, nodePortAddrs []netip.Addr) (Service intTrafficPolicy = loadbalancer.SVCTrafficPolicyCluster } - if expType.canExpose(slim_corev1.ServiceTypeLoadBalancer) { + if expType.CanExpose(slim_corev1.ServiceTypeLoadBalancer) { for _, ip := range svc.Status.LoadBalancer.Ingress { if ip.IP != "" && (ip.IPMode == nil || *ip.IPMode == slim_corev1.LoadBalancerIPModeVIP) { loadBalancerIPs = append(loadBalancerIPs, ip.IP) @@ -295,8 +301,12 @@ func ParseService(svc *slim_corev1.Service, nodePortAddrs []netip.Addr) (Service svcInfo.ForwardingMode, err = getAnnotationServiceForwardingMode(svc) if err != nil { - scopedLog.WithError(err).Warnf("Ignoring %q annotation, applying global configuration: %v", - annotation.ServiceForwardingMode, svcInfo.ForwardingMode) + scopedLog.Warn( + "Ignoring annotation, applying global configuration", + logfields.Error, err, + logfields.Annotation, annotation.ServiceForwardingMode, + logfields.GlobalConfiguration, svcInfo.ForwardingMode, + ) } } @@ -306,8 +316,12 @@ func ParseService(svc *slim_corev1.Service, nodePortAddrs []netip.Addr) (Service svcInfo.LoadBalancerAlgorithm, err = getAnnotationServiceLoadBalancingAlgorithm(svc) if err != nil { - scopedLog.WithError(err).Warnf("Ignoring %q annotation, applying global configuration: %v", - annotation.ServiceLoadBalancingAlgorithm, svcInfo.LoadBalancerAlgorithm) + scopedLog.Warn( + "Ignoring annotation, applying global configuration", + logfields.Error, err, + logfields.Annotation, annotation.ServiceLoadBalancingAlgorithm, + logfields.GlobalConfiguration, svcInfo.LoadBalancerAlgorithm, + ) } } @@ -320,8 +334,11 @@ func ParseService(svc *slim_corev1.Service, nodePortAddrs []netip.Addr) (Service svcInfo.SessionAffinityTimeoutSec = uint32(v1.DefaultClientIPServiceAffinitySeconds) } if svcInfo.SessionAffinityTimeoutSec > defaults.SessionAffinityTimeoutMaxFallback { - scopedLog.Warnf("Clamping maximum possible session affinity timeout from %d to %d seconds", - svcInfo.SessionAffinityTimeoutSec, defaults.SessionAffinityTimeoutMaxFallback) + scopedLog.Warn( + "Clamping maximum possible session affinity timeout (seconds)", + logfields.From, svcInfo.SessionAffinityTimeoutSec, + logfields.To, defaults.SessionAffinityTimeoutMaxFallback, + ) svcInfo.SessionAffinityTimeoutSec = defaults.SessionAffinityTimeoutMaxFallback } } @@ -348,7 +365,7 @@ func ParseService(svc *slim_corev1.Service, nodePortAddrs []netip.Addr) (Service svcInfo.Ports[portName] = p } - if expType.canExpose(slim_corev1.ServiceTypeNodePort) && + if expType.CanExpose(slim_corev1.ServiceTypeNodePort) && (svc.Spec.Type == slim_corev1.ServiceTypeNodePort || svc.Spec.Type == slim_corev1.ServiceTypeLoadBalancer) { if option.Config.EnableNodePort { @@ -691,13 +708,9 @@ func (s *Service) UniquePorts() map[string]bool { func NewClusterService(id ServiceID, k8sService *Service, k8sEndpoints *Endpoints) serviceStore.ClusterService { svc := serviceStore.NewClusterService(id.Name, id.Namespace) - for key, value := range k8sService.Labels { - svc.Labels[key] = value - } + maps.Copy(svc.Labels, k8sService.Labels) - for key, value := range k8sService.Selector { - svc.Selector[key] = value - } + maps.Copy(svc.Selector, k8sService.Selector) portConfig := serviceStore.PortConfiguration{} for portName, port := range k8sService.Ports { diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/service_cache.go b/vendor/github.com/cilium/cilium/pkg/k8s/service_cache.go index 1e1d2bbec7..d1208b07dd 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/service_cache.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/service_cache.go @@ -5,6 +5,7 @@ package k8s import ( "context" + "log/slog" "net" "net/netip" "slices" @@ -14,7 +15,6 @@ import ( "github.com/cilium/statedb" "github.com/cilium/stream" "github.com/davecgh/go-spew/spew" - "github.com/sirupsen/logrus" "github.com/spf13/pflag" core_v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/util/sets" @@ -243,6 +243,7 @@ type ServiceCache interface { // ServiceCacheImpl is a list of services correlated with the matching endpoints. // The Events member will receive events as services. type ServiceCacheImpl struct { + logger *slog.Logger config ServiceCacheConfig // Events may only be read by single consumer. The consumer must acknowledge @@ -278,11 +279,12 @@ type ServiceCacheImpl struct { } // NewServiceCache returns a new ServiceCache -func NewServiceCache(db *statedb.DB, nodeAddrs statedb.Table[datapathTables.NodeAddress], svcMetrics SVCMetrics) *ServiceCacheImpl { +func NewServiceCache(logger *slog.Logger, db *statedb.DB, nodeAddrs statedb.Table[datapathTables.NodeAddress], svcMetrics SVCMetrics) *ServiceCacheImpl { events := make(chan ServiceEvent, option.Config.K8sServiceCacheSize) notifications, emitNotifications, completeNotifications := stream.Multicast[ServiceNotification]() return &ServiceCacheImpl{ + logger: logger, db: db, nodeAddrs: nodeAddrs, services: map[ServiceID]*Service{}, @@ -297,8 +299,8 @@ func NewServiceCache(db *statedb.DB, nodeAddrs statedb.Table[datapathTables.Node } } -func newServiceCache(lc cell.Lifecycle, cfg ServiceCacheConfig, lns *node.LocalNodeStore, db *statedb.DB, nodeAddrs statedb.Table[datapathTables.NodeAddress], metrics SVCMetrics) ServiceCache { - sc := NewServiceCache(db, nodeAddrs, metrics) +func newServiceCache(logger *slog.Logger, lc cell.Lifecycle, cfg ServiceCacheConfig, lns *node.LocalNodeStore, db *statedb.DB, nodeAddrs statedb.Table[datapathTables.NodeAddress], metrics SVCMetrics) ServiceCache { + sc := NewServiceCache(logger, db, nodeAddrs, metrics) sc.config = cfg var wg sync.WaitGroup @@ -434,7 +436,7 @@ func (s *ServiceCacheImpl) UpdateService(k8sSvc *slim_corev1.Service, swg *lock. ) } - svcID, newService := ParseService(k8sSvc, addrs) + svcID, newService := ParseService(s.logger, k8sSvc, addrs) if newService == nil { return svcID } @@ -642,11 +644,8 @@ func (s *ServiceCacheImpl) filterEndpoints(localEndpoints *Endpoints, svc *Servi return localEndpoints } - for _, hint := range backend.HintsForZones { - if hint == s.selfNodeZoneLabel { - filteredEndpoints.Backends[key] = backend - break - } + if slices.Contains(backend.HintsForZones, s.selfNodeZoneLabel) { + filteredEndpoints.Backends[key] = backend } } @@ -697,12 +696,13 @@ func (s *ServiceCacheImpl) correlateEndpoints(id ServiceID) (*Endpoints, bool) { for clusterName, remoteClusterEndpoints := range externalEndpoints.endpoints { for ip, e := range remoteClusterEndpoints.Backends { if _, ok := endpoints.Backends[ip]; ok { - log.WithFields(logrus.Fields{ - logfields.K8sSvcName: id.Name, - logfields.K8sNamespace: id.Namespace, - logfields.IPAddr: ip, - "cluster": clusterName, - }).Warning("Conflicting service backend IP") + s.logger.Warn( + "Conflicting service backend IP", + logfields.K8sSvcName, id.Name, + logfields.K8sNamespace, id.Namespace, + logfields.IPAddr, ip, + logfields.ClusterName, clusterName, + ) } else { e.Preferred = svc.ServiceAffinity == serviceAffinityRemote endpoints.Backends[ip] = e.DeepCopy() @@ -742,7 +742,6 @@ func (s *ServiceCacheImpl) MergeExternalServiceUpdate(service *serviceStore.Clus func (s *ServiceCacheImpl) mergeServiceUpdateLocked(service *serviceStore.ClusterService, oldService *Service, swg *lock.StoppableWaitGroup, opts ...mergeExternalServiceOption) { - scopedLog := log.WithFields(logrus.Fields{logfields.ServiceName: service.String()}) id := ServiceID{Name: service.Name, Namespace: service.Namespace} if slices.Contains(opts, optClusterAware) { @@ -763,13 +762,21 @@ func (s *ServiceCacheImpl) mergeServiceUpdateLocked(service *serviceStore.Cluste if service.Cluster != option.Config.ClusterName && !service.Shared { delete(externalEndpoints.endpoints, service.Cluster) } else { - scopedLog.Debugf("Updating backends to %+v", service.Backends) + s.logger.Debug( + "Updating backends", + logfields.ServiceName, service, + logfields.Backends, service.Backends, + ) + backends := map[cmtypes.AddrCluster]*Backend{} for ipString, portConfig := range service.Backends { addr, err := cmtypes.ParseAddrCluster(ipString) if err != nil { - scopedLog.WithField(logfields.IPAddr, ipString). - Error("Skipping service backend due to invalid IP address") + s.logger.Error( + "Skipping service backend due to invalid IP address", + logfields.ServiceName, service, + logfields.IPAddr, ipString, + ) continue } @@ -821,8 +828,6 @@ func (s *ServiceCacheImpl) MergeExternalServiceDelete(service *serviceStore.Clus } func (s *ServiceCacheImpl) mergeExternalServiceDeleteLocked(service *serviceStore.ClusterService, swg *lock.StoppableWaitGroup, opts ...mergeExternalServiceOption) { - scopedLog := log.WithFields(logrus.Fields{logfields.ServiceName: service.String()}) - id := ServiceID{Name: service.Name, Namespace: service.Namespace} if slices.Contains(opts, optClusterAware) { id.Cluster = service.Cluster @@ -830,7 +835,10 @@ func (s *ServiceCacheImpl) mergeExternalServiceDeleteLocked(service *serviceStor externalEndpoints, ok := s.externalEndpoints[id] if ok { - scopedLog.Debug("Deleting external endpoints") + s.logger.Debug( + "Deleting external endpoints", + logfields.ServiceName, service, + ) oldEPs, _ := s.correlateEndpoints(id) @@ -862,7 +870,10 @@ func (s *ServiceCacheImpl) mergeExternalServiceDeleteLocked(service *serviceStor s.emitEvent(event) } } else { - scopedLog.Debug("Received delete event for non-existing endpoints") + s.logger.Debug( + "Received delete event for non-existing endpoints", + logfields.ServiceName, service, + ) } } diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/labels/labels.go b/vendor/github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/labels/labels.go index 32b2988223..eabee43cb4 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/labels/labels.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/labels/labels.go @@ -7,6 +7,7 @@ package labels import ( "fmt" + "maps" "sort" "strings" @@ -105,14 +106,8 @@ func Conflicts(labels1, labels2 Set) bool { // Merge combines given maps, and does not check for any conflicts // between the maps. In case of conflicts, second map (labels2) wins func Merge(labels1, labels2 Set) Set { - mergedMap := Set{} - - for k, v := range labels1 { - mergedMap[k] = v - } - for k, v := range labels2 { - mergedMap[k] = v - } + mergedMap := maps.Clone(labels1) + maps.Copy(mergedMap, labels2) return mergedMap } diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/labels/selector.go b/vendor/github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/labels/selector.go index d19a9e6788..b6086fc45d 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/labels/selector.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/labels/selector.go @@ -7,6 +7,7 @@ package labels import ( "fmt" + "maps" "slices" "sort" "strconv" @@ -191,12 +192,7 @@ func NewRequirement(key string, op selection.Operator, vals []string, opts ...fi } func (r *Requirement) hasValue(value string) bool { - for i := range r.strValues { - if r.strValues[i] == value { - return true - } - } - return false + return slices.Contains(r.strValues, value) } // Matches returns true if the Requirement matches the input Labels. @@ -1006,11 +1002,7 @@ func (s ValidatedSetSelector) Requirements() (requirements Requirements, selecta } func (s ValidatedSetSelector) DeepCopySelector() Selector { - res := make(ValidatedSetSelector, len(s)) - for k, v := range s { - res[k] = v - } - return res + return maps.Clone(s) } func (s ValidatedSetSelector) RequiresExactMatch(label string) (value string, found bool) { diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1/helpers.go b/vendor/github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1/helpers.go index d2d08077a0..c7d345c56a 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1/helpers.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1/helpers.go @@ -7,6 +7,8 @@ package v1 import ( "fmt" + "maps" + "slices" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/sets" @@ -47,7 +49,7 @@ func LabelSelectorAsSelector(ps *LabelSelector) (labels.Selector, error) { default: return nil, fmt.Errorf("%q is not a valid label selector operator", expr.Operator) } - r, err := labels.NewRequirement(expr.Key, op, append([]string(nil), expr.Values...)) + r, err := labels.NewRequirement(expr.Key, op, slices.Clone(expr.Values)) if err != nil { return nil, err } @@ -66,10 +68,7 @@ func LabelSelectorAsMap(ps *LabelSelector) (map[string]string, error) { if ps == nil { return nil, nil } - selector := map[string]string{} - for k, v := range ps.MatchLabels { - selector[k] = v - } + selector := maps.Clone(ps.MatchLabels) for _, expr := range ps.MatchExpressions { switch expr.Operator { case LabelSelectorOpIn: @@ -145,9 +144,7 @@ func SetAsLabelSelector(ls labels.Set) *LabelSelector { selector := &LabelSelector{ MatchLabels: make(map[string]string, len(ls)), } - for label, value := range ls { - selector.MatchLabels[label] = value - } + maps.Copy(selector.MatchLabels, ls) return selector } diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/synced/cell.go b/vendor/github.com/cilium/cilium/pkg/k8s/synced/cell.go index 7e4840a6ba..a2b2bab72b 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/synced/cell.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/synced/cell.go @@ -6,6 +6,7 @@ package synced import ( "context" "errors" + "log/slog" "github.com/cilium/hive/cell" "github.com/cilium/hive/job" @@ -20,6 +21,7 @@ import ( type syncedParams struct { cell.In + Logger *slog.Logger CacheStatus CacheStatus } @@ -33,6 +35,7 @@ var Cell = cell.Module( cell.Provide(func(params syncedParams) *Resources { return &Resources{ + logger: params.Logger, CacheStatus: params.CacheStatus, } }), @@ -81,6 +84,8 @@ var RejectedCRDSyncPromise = func() promise.Promise[CRDSync] { type syncCRDsPromiseParams struct { cell.In + Logger *slog.Logger + Lifecycle cell.Lifecycle Jobs job.Registry Health cell.Health @@ -100,7 +105,7 @@ func newCRDSyncPromise(params syncCRDsPromiseParams) promise.Promise[CRDSync] { g := params.Jobs.NewGroup(params.Health) g.Add(job.OneShot("sync-crds", func(ctx context.Context, health cell.Health) error { - err := SyncCRDs(ctx, params.Clientset, params.ResourceNames, params.Resources, params.APIGroups, params.Config) + err := SyncCRDs(ctx, params.Logger, params.Clientset, params.ResourceNames, params.Resources, params.APIGroups, params.Config) if err != nil { crdSyncResolver.Reject(err) } else { diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/synced/crd.go b/vendor/github.com/cilium/cilium/pkg/k8s/synced/crd.go index 4ae44a3179..157fbc194e 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/synced/crd.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/synced/crd.go @@ -8,7 +8,10 @@ package synced import ( "context" "errors" + "fmt" + "log/slog" + "github.com/cloudflare/cfssl/log" apiextclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/fields" @@ -24,6 +27,8 @@ import ( "github.com/cilium/cilium/pkg/k8s/informer" slim_metav1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1" "github.com/cilium/cilium/pkg/lock" + "github.com/cilium/cilium/pkg/logging" + "github.com/cilium/cilium/pkg/logging/logfields" "github.com/cilium/cilium/pkg/option" "github.com/cilium/cilium/pkg/time" ) @@ -131,8 +136,8 @@ func AllCiliumCRDResourceNames() []string { // installed inside the K8s cluster. These CRDs are added by the // Cilium Operator. This function will block until it finds all the // CRDs or if a timeout occurs. -func SyncCRDs(ctx context.Context, clientset client.Clientset, crdNames []string, rs *Resources, ag *APIGroups, cfg CRDSyncConfig) error { - crds := newCRDState(crdNames) +func SyncCRDs(ctx context.Context, logger *slog.Logger, clientset client.Clientset, crdNames []string, rs *Resources, ag *APIGroups, cfg CRDSyncConfig) error { + crds := newCRDState(logger, crdNames) listerWatcher := newListWatchFromClient( newCRDGetter(clientset), @@ -194,7 +199,7 @@ func SyncCRDs(ctx context.Context, clientset client.Clientset, crdNames []string // controller will exit after this function. defer ag.RemoveAPI(k8sAPIGroupCRD) - log.Info("Waiting until all Cilium CRDs are available") + logger.Info("Waiting until all Cilium CRDs are available") ticker := time.NewTicker(50 * time.Millisecond) count := 0 @@ -203,12 +208,15 @@ func SyncCRDs(ctx context.Context, clientset client.Clientset, crdNames []string case <-ctx.Done(): err := ctx.Err() if err != nil && !errors.Is(err, context.Canceled) { - log.WithError(err). - Fatalf("Unable to find all Cilium CRDs necessary within "+ + logging.Fatal( + logger, + fmt.Sprintf("Unable to find all Cilium CRDs necessary within "+ "%v timeout. Please ensure that Cilium Operator is "+ "running, as it's responsible for registering all "+ "the Cilium CRDs. The following CRDs were not found: %v", - cfg.CRDWaitTimeout, crds.unSynced()) + cfg.CRDWaitTimeout, crds.unSynced()), + logfields.Error, err, + ) } // If the context was canceled it means the daemon is being stopped // so we can return the context's error. @@ -222,14 +230,17 @@ func SyncCRDs(ctx context.Context, clientset client.Clientset, crdNames []string count++ if count == 20 { count = 0 - log.Infof("Still waiting for Cilium Operator to register the following CRDs: %v", crds.unSynced()) + log.Info( + "Still waiting for Cilium Operator to register CRDs", + logfields.CRDs, crds.unSynced(), + ) } } } } func (s *crdState) add(obj interface{}) { - if pom := informer.CastInformerEvent[slim_metav1.PartialObjectMetadata](obj); pom != nil { + if pom := informer.CastInformerEvent[slim_metav1.PartialObjectMetadata](s.logger, obj); pom != nil { s.Lock() s.m[CRDResourceName(pom.GetName())] = true s.Unlock() @@ -237,7 +248,7 @@ func (s *crdState) add(obj interface{}) { } func (s *crdState) remove(obj interface{}) { - if pom := informer.CastInformerEvent[slim_metav1.PartialObjectMetadata](obj); pom != nil { + if pom := informer.CastInformerEvent[slim_metav1.PartialObjectMetadata](s.logger, obj); pom != nil { s.Lock() s.m[CRDResourceName(pom.GetName())] = false s.Unlock() @@ -273,6 +284,7 @@ func (s *crdState) unSynced() []string { // crdState contains the state of the CRDs inside the cluster. type crdState struct { + logger *slog.Logger lock.Mutex // m is a map which maps the CRD name to its synced state in the cluster. @@ -280,13 +292,14 @@ type crdState struct { m map[string]bool } -func newCRDState(crds []string) crdState { +func newCRDState(logger *slog.Logger, crds []string) crdState { m := make(map[string]bool, len(crds)) for _, name := range crds { m[name] = false } return crdState{ - m: m, + logger: logger, + m: m, } } diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/synced/logfields.go b/vendor/github.com/cilium/cilium/pkg/k8s/synced/logfields.go deleted file mode 100644 index 6ed069ca74..0000000000 --- a/vendor/github.com/cilium/cilium/pkg/k8s/synced/logfields.go +++ /dev/null @@ -1,20 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 -// Copyright Authors of Cilium - -package synced - -import ( - "github.com/cilium/cilium/pkg/logging" - "github.com/cilium/cilium/pkg/logging/logfields" -) - -// logging field definitions -const ( - // subsysK8s is the value for logfields.LogSubsys - subsysK8s = "k8s" -) - -var ( - // log is the k8s package logger object. - log = logging.DefaultLogger.WithField(logfields.LogSubsys, subsysK8s) -) diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/synced/resources.go b/vendor/github.com/cilium/cilium/pkg/k8s/synced/resources.go index 4512d3e163..3662aa7164 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/synced/resources.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/synced/resources.go @@ -5,17 +5,21 @@ package synced import ( "fmt" + "log/slog" "golang.org/x/sync/errgroup" "k8s.io/client-go/tools/cache" "github.com/cilium/cilium/pkg/lock" + "github.com/cilium/cilium/pkg/logging" + "github.com/cilium/cilium/pkg/logging/logfields" "github.com/cilium/cilium/pkg/time" ) // Resources maps resource names to channels that are closed upon initial // sync with k8s. type Resources struct { + logger *slog.Logger CacheStatus CacheStatus lock.RWMutex @@ -70,7 +74,10 @@ func (r *Resources) BlockWaitGroupToSyncResources( // Log an error caches have already synchronized, as the caller is making this call too late // and the resource in question was missed in the initial cache sync. if r.CacheStatus.Synchronized() { - log.WithField("kubernetesResource", resourceName).Errorf("BlockWaitGroupToSyncResources called after Caches have already synced") + r.logger.Error( + "BlockWaitGroupToSyncResources called after Caches have already synced", + logfields.Resource, resourceName, + ) return } ch := make(chan struct{}) @@ -84,7 +91,7 @@ func (r *Resources) BlockWaitGroupToSyncResources( r.Unlock() go func() { - scopedLog := log.WithField("kubernetesResource", resourceName) + scopedLog := r.logger.With(logfields.Resource, resourceName) scopedLog.Debug("waiting for cache to synchronize") if ok := cache.WaitForCacheSync(stop, hasSyncedFunc); !ok { select { @@ -100,7 +107,10 @@ func (r *Resources) BlockWaitGroupToSyncResources( r.Unlock() default: // Fatally exit it resource fails to sync - scopedLog.Fatalf("failed to wait for cache to sync") + logging.Fatal( + scopedLog, + "failed to wait for cache to sync", + ) } } else { scopedLog.Debug("cache synced") @@ -130,7 +140,7 @@ func (r *Resources) WaitForCacheSync(resourceNames ...string) { continue } for { - scopedLog := log.WithField("kubernetesResource", resourceName) + scopedLog := r.logger.With(logfields.Resource, resourceName) <-c r.RLock() stopWait := r.stopWait[resourceName] @@ -195,9 +205,17 @@ func (r *Resources) WaitForCacheSyncWithTimeout(timeout time.Duration, resourceN // We reset the timer to wait the timeout period minus the // time since the last event. currTimeout = timeout - time.Since(lastEvent) - log.Debugf("resource %q received event %s ago, waiting for additional %s before timing out", resource, time.Since(lastEvent), currTimeout) + r.logger.Debug( + "received event for resource type, waiting before timeout", + logfields.Resource, resource, + logfields.LastEventReceived, time.Since(lastEvent), + logfields.Timeout, currTimeout, + ) case <-done: - log.Debugf("resource %q cache has synced, stopping timeout watcher", resource) + r.logger.Debug( + "cache has synced, stopping timeout watcher", + logfields.Resource, resource, + ) return nil } } diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/version/version.go b/vendor/github.com/cilium/cilium/pkg/k8s/version/version.go index db2bab9951..1331cde78f 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/version/version.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/version/version.go @@ -8,6 +8,7 @@ package version import ( "context" "fmt" + "log/slog" "github.com/blang/semver/v4" "k8s.io/apimachinery/pkg/api/errors" @@ -15,13 +16,10 @@ import ( "k8s.io/client-go/kubernetes" "github.com/cilium/cilium/pkg/lock" - "github.com/cilium/cilium/pkg/logging" "github.com/cilium/cilium/pkg/logging/logfields" "github.com/cilium/cilium/pkg/versioncheck" ) -var log = logging.DefaultLogger.WithField(logfields.LogSubsys, "k8s") - // ServerCapabilities is a list of server capabilities derived based on // version, the Kubernetes discovery API, or probing of individual API // endpoints. @@ -160,7 +158,7 @@ func Force(version string) error { return nil } -func endpointSlicesFallbackDiscovery(client kubernetes.Interface) error { +func endpointSlicesFallbackDiscovery(logger *slog.Logger, client kubernetes.Interface) error { // If a k8s version with discovery v1 is used, then do not even bother // checking for v1beta1 cached.mutex.Lock() @@ -188,7 +186,7 @@ func endpointSlicesFallbackDiscovery(client kubernetes.Interface) error { } if errors.IsNotFound(err) { - log.WithError(err).Info("Unable to retrieve EndpointSlices for default/kubernetes. Disabling EndpointSlices") + logger.Info("Unable to retrieve EndpointSlices for default/kubernetes. Disabling EndpointSlices", logfields.Error, err) // StatusNotFound is a safe error, EndpointSlices are // disabled and the agent can continue. return nil @@ -199,13 +197,13 @@ func endpointSlicesFallbackDiscovery(client kubernetes.Interface) error { return fmt.Errorf("unable to validate EndpointSlices support: %w", err) } -func leasesFallbackDiscovery(client kubernetes.Interface, apiDiscoveryEnabled bool) error { +func leasesFallbackDiscovery(logger *slog.Logger, client kubernetes.Interface, apiDiscoveryEnabled bool) error { // apiDiscoveryEnabled is used to fallback leases discovery to directly // probing the API when we cannot discover API groups. // We require to check for Leases capabilities in operator only, which uses Leases // for leader election purposes in HA mode. if !apiDiscoveryEnabled { - log.Debugf("Skipping Leases support fallback discovery") + logger.Debug("Skipping Leases support fallback discovery") return nil } @@ -221,7 +219,7 @@ func leasesFallbackDiscovery(client kubernetes.Interface, apiDiscoveryEnabled bo } if errors.IsNotFound(err) { - log.WithError(err).Info("Unable to retrieve Leases for kube-controller-manager. Disabling LeasesResourceLock") + logger.Info("Unable to retrieve Leases for kube-controller-manager. Disabling LeasesResourceLock", logfields.Error, err) // StatusNotFound is a safe error, Leases are // disabled and the agent can continue return nil @@ -268,7 +266,7 @@ func updateK8sServerVersion(client kubernetes.Interface) error { // Discovery of capabilities only works if the discovery API of the apiserver // is functional. If it is not available, a warning is logged and the discovery // falls back to probing individual API endpoints. -func Update(client kubernetes.Interface, apiDiscoveryEnabled bool) error { +func Update(logger *slog.Logger, client kubernetes.Interface, apiDiscoveryEnabled bool) error { err := updateK8sServerVersion(client) if err != nil { return err @@ -288,21 +286,21 @@ func Update(client kubernetes.Interface, apiDiscoveryEnabled bool) error { // information at a later point because the capabilities are // primiarly used while the agent is starting up. Instead, fall // back to probing API endpoints directly. - log.WithError(err).Warning("Unable to discover API groups and resources") - if err := endpointSlicesFallbackDiscovery(client); err != nil { + logger.Warn("Unable to discover API groups and resources", logfields.Error, err) + if err := endpointSlicesFallbackDiscovery(logger, client); err != nil { return err } - return leasesFallbackDiscovery(client, apiDiscoveryEnabled) + return leasesFallbackDiscovery(logger, client, apiDiscoveryEnabled) } updateServerGroupsAndResources(apiResourceLists) } else { - if err := endpointSlicesFallbackDiscovery(client); err != nil { + if err := endpointSlicesFallbackDiscovery(logger, client); err != nil { return err } - return leasesFallbackDiscovery(client, apiDiscoveryEnabled) + return leasesFallbackDiscovery(logger, client, apiDiscoveryEnabled) } return nil diff --git a/vendor/github.com/cilium/cilium/pkg/k8s/watchers/resources/resources.go b/vendor/github.com/cilium/cilium/pkg/k8s/watchers/resources/resources.go index f2895f8450..16e7b3606e 100644 --- a/vendor/github.com/cilium/cilium/pkg/k8s/watchers/resources/resources.go +++ b/vendor/github.com/cilium/cilium/pkg/k8s/watchers/resources/resources.go @@ -12,6 +12,10 @@ import ( ) const ( + // K8sAPIGroupNetworkingV1Core is the identifier for K8S resources of type networking.k8s.io/v1/NetworkPolicy + K8sAPIGroupNetworkingV1Core = "networking.k8s.io/v1::NetworkPolicy" + // K8sAPIGroupNamespaceV1Core is the identifier for K8s resources of type core/v1/Namespace. + K8sAPIGroupNamespaceV1Core = "core/v1::Namespace" // K8sAPIGroupServiceV1Core is the identifier for K8s resources of type core/v1/Service. K8sAPIGroupServiceV1Core = "core/v1::Service" // K8sAPIGroupPodV1Core is the identifier for K8s resources of type core/v1/Pod. diff --git a/vendor/github.com/cilium/cilium/pkg/kvstore/backend.go b/vendor/github.com/cilium/cilium/pkg/kvstore/backend.go index 5170abbf63..7d1341e38b 100644 --- a/vendor/github.com/cilium/cilium/pkg/kvstore/backend.go +++ b/vendor/github.com/cilium/cilium/pkg/kvstore/backend.go @@ -5,10 +5,13 @@ package kvstore import ( "context" + "log/slog" "google.golang.org/grpc" "github.com/cilium/cilium/api/v1/models" + "github.com/cilium/cilium/pkg/logging" + "github.com/cilium/cilium/pkg/logging/logfields" "github.com/cilium/cilium/pkg/time" ) @@ -85,7 +88,7 @@ type backendModule interface { // setConfig must configure the backend with the specified options. // This function is called once before newClient(). - setConfig(opts map[string]string) error + setConfig(logger *slog.Logger, opts map[string]string) error // setExtraConfig sets more options in the kvstore that are not able to // be set by strings. @@ -100,7 +103,7 @@ type backendModule interface { // newClient must initializes the backend and create a new kvstore // client which implements the BackendOperations interface - newClient(ctx context.Context, opts *ExtraOptions) (BackendOperations, chan error) + newClient(ctx context.Context, logger *slog.Logger, opts *ExtraOptions) (BackendOperations, chan error) // createInstance creates a new instance of the module createInstance() backendModule @@ -115,7 +118,7 @@ var ( // registerBackend must be called by kvstore backends to register themselves func registerBackend(name string, module backendModule) { if _, ok := registeredBackends[name]; ok { - log.Panicf("backend with name '%s' already registered", name) + logging.Panic(logging.DefaultSlogLogger, "backend already registered", logfields.Name, name) } registeredBackends[name] = module @@ -138,10 +141,6 @@ type BackendOperations interface { // is connected to the kvstore server. Connected(ctx context.Context) <-chan error - // Disconnected returns a channel which is closed whenever the kvstore - // client is not connected to the kvstore server. (Only implemented for etcd) - Disconnected() <-chan struct{} - // Status returns the status of the kvstore client Status() *models.Status diff --git a/vendor/github.com/cilium/cilium/pkg/kvstore/cell.go b/vendor/github.com/cilium/cilium/pkg/kvstore/cell.go index 50e0d1dd17..a8f9ec06f4 100644 --- a/vendor/github.com/cilium/cilium/pkg/kvstore/cell.go +++ b/vendor/github.com/cilium/cilium/pkg/kvstore/cell.go @@ -7,6 +7,7 @@ import ( "context" "errors" "fmt" + "log/slog" "sync" "github.com/cilium/hive/cell" @@ -27,10 +28,10 @@ var Cell = cell.Module( cell.Config(defaultConfig), - cell.Provide(func(lc cell.Lifecycle, shutdowner hive.Shutdowner, cfg config, opts *ExtraOptions) promise.Promise[BackendOperations] { + cell.Provide(func(logger *slog.Logger, lc cell.Lifecycle, shutdowner hive.Shutdowner, cfg config, opts *ExtraOptions) promise.Promise[BackendOperations] { resolver, promise := promise.New[BackendOperations]() if cfg.KVStore == "" { - log.Info("Skipping connection to kvstore, as not configured") + logger.Info("Skipping connection to kvstore, as not configured") resolver.Reject(errors.New("kvstore not configured")) return promise } @@ -52,18 +53,19 @@ var Cell = cell.Module( go func() { defer wg.Done() - log := log.WithField(logfields.BackendName, cfg.KVStore) - log.Info("Establishing connection to kvstore") - backend, errCh := NewClient(ctx, cfg.KVStore, cfg.KVStoreOpt, opts) + scopedLogger := logger.With(logfields.BackendName, cfg.KVStore) + + scopedLogger.Info("Establishing connection to kvstore") + backend, errCh := NewClient(ctx, scopedLogger, cfg.KVStore, cfg.KVStoreOpt, opts) if err, isErr := <-errCh; isErr { - log.WithError(err).Error("Failed to establish connection to kvstore") + scopedLogger.Error("Failed to establish connection to kvstore", logfields.Error, err) resolver.Reject(fmt.Errorf("failed connecting to kvstore: %w", err)) shutdowner.Shutdown(hive.ShutdownWithError(err)) return } - log.Info("Connection to kvstore successfully established") + scopedLogger.Info("Connection to kvstore successfully established") resolver.Resolve(backend) }() return nil diff --git a/vendor/github.com/cilium/cilium/pkg/kvstore/client.go b/vendor/github.com/cilium/cilium/pkg/kvstore/client.go index 5b2ca787df..2e444eab60 100644 --- a/vendor/github.com/cilium/cilium/pkg/kvstore/client.go +++ b/vendor/github.com/cilium/cilium/pkg/kvstore/client.go @@ -6,8 +6,10 @@ package kvstore import ( "context" "fmt" + "log/slog" - "github.com/cilium/cilium/pkg/time" + "github.com/cilium/cilium/pkg/logging" + "github.com/cilium/cilium/pkg/logging/logfields" ) var ( @@ -18,12 +20,12 @@ var ( defaultClientSet = make(chan struct{}) ) -func initClient(ctx context.Context, module backendModule, opts *ExtraOptions) error { - scopedLog := log.WithField(fieldKVStoreModule, module.getName()) - c, errChan := module.newClient(ctx, opts) +func initClient(ctx context.Context, logger *slog.Logger, module backendModule, opts *ExtraOptions) error { + scopedLog := logger.With(fieldKVStoreModule, module.getName()) + c, errChan := module.newClient(ctx, scopedLog, opts) if c == nil { err := <-errChan - scopedLog.WithError(err).Fatal("Unable to create kvstore client") + logging.Fatal(scopedLog, "Unable to create kvstore client", logfields.Error, err) } defaultClient = c @@ -37,7 +39,7 @@ func initClient(ctx context.Context, module backendModule, opts *ExtraOptions) e go func() { err, isErr := <-errChan if isErr && err != nil { - scopedLog.WithError(err).Fatal("Unable to connect to kvstore") + logging.Fatal(scopedLog, "Unable to connect to kvstore", logfields.Error, err) } }() @@ -51,7 +53,7 @@ func Client() BackendOperations { } // NewClient returns a new kvstore client based on the configuration -func NewClient(ctx context.Context, selectedBackend string, opts map[string]string, options *ExtraOptions) (BackendOperations, chan error) { +func NewClient(ctx context.Context, logger *slog.Logger, selectedBackend string, opts map[string]string, options *ExtraOptions) (BackendOperations, chan error) { // Channel used to report immediate errors, module.newClient will // create and return a different channel, caller doesn't need to know errChan := make(chan error, 1) @@ -63,7 +65,7 @@ func NewClient(ctx context.Context, selectedBackend string, opts map[string]stri return nil, errChan } - if err := module.setConfig(opts); err != nil { + if err := module.setConfig(logger, opts); err != nil { errChan <- err return nil, errChan } @@ -73,28 +75,5 @@ func NewClient(ctx context.Context, selectedBackend string, opts map[string]stri return nil, errChan } - return module.newClient(ctx, options) -} - -// Connected returns a channel which is closed when the following conditions -// are being met at the same time: -// * The kvstore client is configured -// * Connectivity to the kvstore has been established -// * The kvstore has quorum -// -// The channel will *not* be closed if the kvstore client is closed before -// connectivity or quorum has been achieved. It will wait until a new kvstore -// client is configured to again wait for connectivity and quorum. -func Connected() <-chan struct{} { - c := make(chan struct{}) - go func(c chan struct{}) { - for { - if err := <-Client().Connected(context.Background()); err == nil { - close(c) - return - } - time.Sleep(100 * time.Millisecond) - } - }(c) - return c + return module.newClient(ctx, logger, options) } diff --git a/vendor/github.com/cilium/cilium/pkg/kvstore/config.go b/vendor/github.com/cilium/cilium/pkg/kvstore/config.go index 401c0c2c99..34e3e2bb9e 100644 --- a/vendor/github.com/cilium/cilium/pkg/kvstore/config.go +++ b/vendor/github.com/cilium/cilium/pkg/kvstore/config.go @@ -6,6 +6,7 @@ package kvstore import ( "context" "fmt" + "log/slog" "sync" "github.com/cilium/cilium/pkg/logging/logfields" @@ -13,20 +14,23 @@ import ( // setOpts validates the specified options against the selected backend and // then modifies the configuration -func setOpts(opts map[string]string, supportedOpts backendOptions) error { +func setOpts(logger *slog.Logger, opts map[string]string, supportedOpts backendOptions) error { errors := 0 for key, val := range opts { opt, ok := supportedOpts[key] if !ok { errors++ - log.WithField(logfields.Key, key).Error("unknown kvstore configuration key") + logger.Error("unknown kvstore configuration key", logfields.Key, key) continue } if opt.validate != nil { if err := opt.validate(val); err != nil { - log.WithError(err).Errorf("invalid value for key %s", key) + logger.Error("invalid value for key", + logfields.Error, err, + logfields.Key, key, + ) errors++ } } @@ -36,9 +40,9 @@ func setOpts(opts map[string]string, supportedOpts backendOptions) error { // if errors have occurred, print the supported configuration keys to // the log if errors > 0 { - log.Error("Supported configuration keys:") + logger.Error("Supported configuration keys:") for key, val := range supportedOpts { - log.Errorf(" %-12s %s", key, val.description) + logger.Error(fmt.Sprintf(" %-12s %s", key, val.description)) } return fmt.Errorf("invalid kvstore configuration, see log for details") @@ -66,13 +70,13 @@ var ( setupOnce sync.Once ) -func setup(ctx context.Context, selectedBackend string, opts map[string]string, goOpts *ExtraOptions) error { +func setup(ctx context.Context, logger *slog.Logger, selectedBackend string, opts map[string]string, goOpts *ExtraOptions) error { module := getBackend(selectedBackend) if module == nil { return fmt.Errorf("unknown key-value store type %q. See cilium.link/err-kvstore for details", selectedBackend) } - if err := module.setConfig(opts); err != nil { + if err := module.setConfig(logger, opts); err != nil { return err } @@ -80,16 +84,16 @@ func setup(ctx context.Context, selectedBackend string, opts map[string]string, return err } - return initClient(ctx, module, goOpts) + return initClient(ctx, logger, module, goOpts) } // Setup sets up the key-value store specified in kvStore and configures it // with the options provided in opts -func Setup(ctx context.Context, selectedBackend string, opts map[string]string, goOpts *ExtraOptions) error { +func Setup(ctx context.Context, logger *slog.Logger, selectedBackend string, opts map[string]string, goOpts *ExtraOptions) error { var err error setupOnce.Do(func() { - err = setup(ctx, selectedBackend, opts, goOpts) + err = setup(ctx, logger, selectedBackend, opts, goOpts) }) return err diff --git a/vendor/github.com/cilium/cilium/pkg/kvstore/dummy.go b/vendor/github.com/cilium/cilium/pkg/kvstore/dummy.go index 83d6991e76..52f93d65c8 100644 --- a/vendor/github.com/cilium/cilium/pkg/kvstore/dummy.go +++ b/vendor/github.com/cilium/cilium/pkg/kvstore/dummy.go @@ -7,6 +7,7 @@ import ( "context" "testing" + "github.com/cilium/hive/hivetest" client "go.etcd.io/etcd/client/v3" "github.com/cilium/cilium/pkg/time" @@ -40,13 +41,13 @@ func SetupDummyWithConfigOpts(tb testing.TB, dummyBackend string, opts map[strin module.setConfigDummy() if opts != nil { - err := module.setConfig(opts) + err := module.setConfig(hivetest.Logger(tb), opts) if err != nil { tb.Fatalf("Unable to set config options for kvstore backend module: %v", err) } } - if err := initClient(context.Background(), module, nil); err != nil { + if err := initClient(context.Background(), hivetest.Logger(tb), module, nil); err != nil { tb.Fatalf("Unable to initialize kvstore client: %v", err) } diff --git a/vendor/github.com/cilium/cilium/pkg/kvstore/etcd.go b/vendor/github.com/cilium/cilium/pkg/kvstore/etcd.go index 2912c2f5e2..2c47e7d1b9 100644 --- a/vendor/github.com/cilium/cilium/pkg/kvstore/etcd.go +++ b/vendor/github.com/cilium/cilium/pkg/kvstore/etcd.go @@ -9,12 +9,12 @@ import ( "crypto/tls" "errors" "fmt" + "log/slog" "math/rand/v2" "os" "strconv" "strings" - "github.com/sirupsen/logrus" "go.etcd.io/etcd/api/v3/mvccpb" v3rpcErrors "go.etcd.io/etcd/api/v3/v3rpc/rpctypes" "go.etcd.io/etcd/client/pkg/v3/logutil" @@ -30,6 +30,7 @@ import ( "github.com/cilium/cilium/pkg/backoff" "github.com/cilium/cilium/pkg/defaults" "github.com/cilium/cilium/pkg/lock" + "github.com/cilium/cilium/pkg/logging" "github.com/cilium/cilium/pkg/logging/logfields" "github.com/cilium/cilium/pkg/option" ciliumrate "github.com/cilium/cilium/pkg/rate" @@ -159,8 +160,8 @@ func (e *etcdModule) getName() string { return EtcdBackendName } -func (e *etcdModule) setConfig(opts map[string]string) error { - return setOpts(opts, e.opts) +func (e *etcdModule) setConfig(logger *slog.Logger, opts map[string]string) error { + return setOpts(logger, opts, e.opts) } func (e *etcdModule) setExtraConfig(opts *ExtraOptions) error { @@ -190,7 +191,7 @@ type clientOptions struct { ListBatchSize int } -func (e *etcdModule) newClient(ctx context.Context, opts *ExtraOptions) (BackendOperations, chan error) { +func (e *etcdModule) newClient(ctx context.Context, logger *slog.Logger, opts *ExtraOptions) (BackendOperations, chan error) { errChan := make(chan error, 10) clientOptions := clientOptions{ @@ -256,22 +257,25 @@ func (e *etcdModule) newClient(ctx context.Context, opts *ExtraOptions) (Backend e.config.Endpoints = []string{endpointsOpt.value} } - log.WithFields(logrus.Fields{ - "ConfigPath": configPath, - "KeepAliveHeartbeat": clientOptions.KeepAliveHeartbeat, - "KeepAliveTimeout": clientOptions.KeepAliveTimeout, - "RateLimit": clientOptions.RateLimit, - "MaxInflight": clientOptions.MaxInflight, - "ListLimit": clientOptions.ListBatchSize, - }).Info("Creating etcd client") + logger.Info( + "Creating etcd client", + logfields.ConfigPath, configPath, + logfields.KeepAliveHeartbeat, clientOptions.KeepAliveHeartbeat, + logfields.KeepAliveTimeout, clientOptions.KeepAliveTimeout, + logfields.RateLimit, clientOptions.RateLimit, + logfields.MaxInflight, clientOptions.MaxInflight, + logfields.ListLimit, clientOptions.ListBatchSize, + ) for { // connectEtcdClient will close errChan when the connection attempt has // been successful - backend, err := connectEtcdClient(ctx, e.config, configPath, errChan, clientOptions, opts) + backend, err := connectEtcdClient(ctx, logger, e.config, configPath, errChan, clientOptions, opts) switch { case os.IsNotExist(err): - log.WithError(err).Info("Waiting for all etcd configuration files to be available") + logger.Info("Waiting for all etcd configuration files to be available", + logfields.Error, err, + ) time.Sleep(5 * time.Second) case err != nil: errChan <- err @@ -295,9 +299,11 @@ func init() { } // Initialize the etcd client logger. - l, err := logutil.CreateDefaultZapLogger(etcdClientDebugLevel()) + l, err := logutil.CreateDefaultZapLogger(etcdClientDebugLevel(logging.DefaultSlogLogger)) if err != nil { - log.WithError(err).Warning("Failed to initialize etcd client logger") + logging.DefaultSlogLogger.Warn("Failed to initialize etcd client logger", + logfields.Error, err, + ) l = zap.NewNop() } etcd3ClientLogger = l.Named("etcd-client") @@ -306,14 +312,14 @@ func init() { // etcdClientDebugLevel translates ETCD_CLIENT_DEBUG into zap log level. // This is a copy of a private etcd client function: // https://github.com/etcd-io/etcd/blob/v3.5.9/client/v3/logger.go#L47-L59 -func etcdClientDebugLevel() zapcore.Level { +func etcdClientDebugLevel(logger *slog.Logger) zapcore.Level { envLevel := os.Getenv("ETCD_CLIENT_DEBUG") if envLevel == "" || envLevel == "true" { return zapcore.InfoLevel } var l zapcore.Level if err := l.Set(envLevel); err != nil { - log.Warning("Invalid value for environment variable 'ETCD_CLIENT_DEBUG'. Using default level: 'info'") + logger.Warn("Invalid value for environment variable 'ETCD_CLIENT_DEBUG'. Using default level: 'info'") return zapcore.InfoLevel } return l @@ -373,7 +379,7 @@ type etcdClient struct { leaseExpiredObservers lock.Map[string, func(string)] // logger is the scoped logger associated with this client - logger logrus.FieldLogger + logger *slog.Logger } type etcdMutex struct { @@ -482,25 +488,7 @@ func (e *etcdClient) Connected(ctx context.Context) <-chan error { return out } -// Disconnected closes the returned channel when the etcd client is -// disconnected after being reconnected. Blocks until the etcd client is first -// connected with the kvstore. -func (e *etcdClient) Disconnected() <-chan struct{} { - <-e.firstSession - limiter := newExpBackoffRateLimiter(e, "etcd-client-disconnected") - defer limiter.Reset() - for { - session, err := e.lockLeaseManager.GetSession(context.Background(), InitLockPath) - if err == nil { - return session.Done() - } - - e.logger.WithError(err).Warning("Failed to acquire lock session") - limiter.Wait(context.TODO()) - } -} - -func connectEtcdClient(ctx context.Context, config *client.Config, cfgPath string, errChan chan error, clientOptions clientOptions, opts *ExtraOptions) (BackendOperations, error) { +func connectEtcdClient(ctx context.Context, logger *slog.Logger, config *client.Config, cfgPath string, errChan chan error, clientOptions clientOptions, opts *ExtraOptions) (BackendOperations, error) { if cfgPath != "" { cfg, err := clientyaml.NewConfig(cfgPath) if err != nil { @@ -556,10 +544,10 @@ func connectEtcdClient(ctx context.Context, config *client.Config, cfgPath strin extraOptions: opts, listBatchSize: clientOptions.ListBatchSize, statusCheckErrors: make(chan error, 128), - logger: log.WithFields(logrus.Fields{ - "endpoints": config.Endpoints, - "config": cfgPath, - }), + logger: logger.With( + logfields.Endpoints, config.Endpoints, + logfields.Config, cfgPath, + ), } initialLimit := clientOptions.RateLimit @@ -567,13 +555,19 @@ func connectEtcdClient(ctx context.Context, config *client.Config, cfgPath strin // initial rate limit to BootstrapRateLimit and apply the standard rate limit // once the caller has signaled that bootstrap is complete by closing the channel. if clientOptions.BootstrapRateLimit > 0 && opts != nil && opts.BootstrapComplete != nil { - ec.logger.WithField(logfields.EtcdQPSLimit, clientOptions.BootstrapRateLimit).Info("Setting client QPS limit for bootstrap") + ec.logger.Info( + "Setting client QPS limit for bootstrap", + logfields.EtcdQPSLimit, clientOptions.BootstrapRateLimit, + ) initialLimit = clientOptions.BootstrapRateLimit go func() { select { case <-ec.client.Ctx().Done(): case <-opts.BootstrapComplete: - ec.logger.WithField(logfields.EtcdQPSLimit, clientOptions.RateLimit).Info("Bootstrap complete, updating client QPS limit") + ec.logger.Info( + "Bootstrap complete, updating client QPS limit", + logfields.EtcdQPSLimit, clientOptions.RateLimit, + ) ec.limiter.SetRateLimit(rate.Limit(clientOptions.RateLimit)) } }() @@ -592,8 +586,8 @@ func connectEtcdClient(ctx context.Context, config *client.Config, cfgPath strin leaseTTL = defaults.KVstoreLeaseTTL } - ec.leaseManager = newEtcdLeaseManager(c, leaseTTL, etcdMaxKeysPerLease, ec.expiredLeaseObserver, ec.logger) - ec.lockLeaseManager = newEtcdLeaseManager(c, defaults.LockLeaseTTL, etcdMaxKeysPerLease, nil, ec.logger) + ec.leaseManager = newEtcdLeaseManager(ec.logger, c, leaseTTL, etcdMaxKeysPerLease, ec.expiredLeaseObserver) + ec.lockLeaseManager = newEtcdLeaseManager(ec.logger, c, defaults.LockLeaseTTL, etcdMaxKeysPerLease, nil) go ec.asyncConnectEtcdClient(errChan) @@ -748,7 +742,10 @@ func (e *etcdClient) LockPath(ctx context.Context, path string) (locker KVLocker func (e *etcdClient) DeletePrefix(ctx context.Context, path string) (err error) { defer func() { - Trace("DeletePrefix", err, logrus.Fields{fieldPrefix: path}) + Trace(e.logger, "DeletePrefix", + logfields.Error, err, + fieldPrefix, path, + ) }() lr, err := e.limiter.Wait(ctx) if err != nil { @@ -775,7 +772,7 @@ func (e *etcdClient) watch(ctx context.Context, prefix string, events emitter) { localCache := watcherCache{} listSignalSent := false - scopedLog := e.logger.WithField(fieldPrefix, prefix) + scopedLog := e.logger.With(fieldPrefix, prefix) scopedLog.Info("Starting watcher") defer func() { @@ -814,9 +811,17 @@ reList: lr.Error(err, -1) if attempt := errLimiter.Attempt(); attempt < 10 { - scopedLog.WithError(Hint(err)).WithField(logfields.Attempt, attempt).Info("Unable to list keys before starting watcher, will retry") + scopedLog.Info( + "Unable to list keys before starting watcher, will retry", + logfields.Error, Hint(err), + logfields.Attempt, attempt, + ) } else { - scopedLog.WithError(Hint(err)).WithField(logfields.Attempt, attempt).Warn("Unable to list keys before starting watcher, will retry") + scopedLog.Warn( + "Unable to list keys before starting watcher, will retry", + logfields.Error, Hint(err), + logfields.Attempt, attempt, + ) } errLimiter.Wait(ctx) @@ -825,10 +830,11 @@ reList: lr.Done() errLimiter.Reset() - scopedLog.WithFields(logrus.Fields{ - logfields.Count: len(kvs), - fieldRev: revision, - }).Info("Successfully listed keys before starting watcher") + scopedLog.Info( + "Successfully listed keys before starting watcher", + logfields.Count, len(kvs), + fieldRev, revision, + ) for _, key := range kvs { t := EventTypeCreate @@ -839,7 +845,11 @@ reList: localCache.MarkInUse(key.Key) if traceEnabled { - scopedLog.Debugf("Emitting list result as %s event for %s=%s", t, key.Key, key.Value) + scopedLog.Debug("Emitting list result", + logfields.EventType, t, + logfields.Key, key.Key, + logfields.Value, key.Value, + ) } if !events.emit(ctx, KeyValueEvent{ @@ -863,7 +873,9 @@ reList: } if traceEnabled { - scopedLog.Debugf("Emitting EventTypeDelete event for %s", k) + scopedLog.Debug("Emitting EventTypeDelete event", + logfields.Key, k, + ) } return events.emit(ctx, event) }) { @@ -879,7 +891,10 @@ reList: } recreateWatcher: - scopedLog.WithField(fieldRev, nextRev).Info("Starting to watch prefix") + scopedLog.Info( + "Starting to watch prefix", + fieldRev, nextRev, + ) lr, err = e.limiter.Wait(ctx) if err != nil { @@ -909,21 +924,28 @@ reList: goto recreateWatcher } - scopedLog := scopedLog.WithField(fieldRev, r.Header.Revision) - if err := r.Err(); err != nil { switch { case errors.Is(err, ErrOperationAbortedByInterceptor): // Aborted on purpose by a custom interceptor. - scopedLog.WithError(Hint(err)).Debug("Etcd watcher aborted") + scopedLog.Debug("Etcd watcher aborted", + logfields.Error, Hint(err), + fieldRev, r.Header.Revision, + ) case errors.Is(err, v3rpcErrors.ErrCompacted): // We tried to watch on a compacted // revision that may no longer exist, // recreate the watcher and try to // watch on the next possible revision - scopedLog.WithError(Hint(err)).Info("Tried watching on compacted revision. Triggering relist of all keys") + scopedLog.Info("Tried watching on compacted revision. Triggering relist of all keys", + logfields.Error, Hint(err), + fieldRev, r.Header.Revision, + ) default: - scopedLog.WithError(Hint(err)).Info("Etcd watcher errored. Triggering relist of all keys") + scopedLog.Info("Etcd watcher errored. Triggering relist of all keys", + logfields.Error, Hint(err), + fieldRev, r.Header.Revision, + ) } // mark all local keys in state for @@ -936,7 +958,9 @@ reList: nextRev = r.Header.Revision + 1 if traceEnabled { - scopedLog.Debugf("Received event from etcd: %+v", r) + scopedLog.Debug("Received event from etcd", + logfields.Response, r, + ) } for _, ev := range r.Events { @@ -958,7 +982,11 @@ reList: } if traceEnabled { - scopedLog.Debugf("Emitting %s event for %s=%s", event.Typ, event.Key, event.Value) + scopedLog.Debug("Emitting event", + logfields.EventType, event.Typ, + logfields.Key, event.Key, + logfields.Value, event.Value, + ) } if !events.emit(ctx, event) { return @@ -969,7 +997,7 @@ reList: } } -func (e *etcdClient) paginatedList(ctx context.Context, log *logrus.Entry, prefix string) (kvs []*mvccpb.KeyValue, revision int64, err error) { +func (e *etcdClient) paginatedList(ctx context.Context, log *slog.Logger, prefix string) (kvs []*mvccpb.KeyValue, revision int64, err error) { start, end := prefix, client.GetPrefixRangeEnd(prefix) for { @@ -982,10 +1010,11 @@ func (e *etcdClient) paginatedList(ctx context.Context, log *logrus.Entry, prefi return nil, 0, err } - log.WithFields(logrus.Fields{ - fieldNumEntries: len(res.Kvs), - fieldRemainingEntries: res.Count - int64(len(res.Kvs)), - }).Debug("Received list response from etcd") + log.Debug( + "Received list response from etcd", + fieldNumEntries, len(res.Kvs), + fieldRemainingEntries, res.Count-int64(len(res.Kvs)), + ) if kvs == nil { kvs = make([]*mvccpb.KeyValue, 0, res.Count) @@ -1013,7 +1042,9 @@ func (e *etcdClient) determineEndpointStatus(ctx context.Context, endpointAddres ctxTimeout, cancel := context.WithTimeout(ctx, statusCheckTimeout) defer cancel() - e.logger.Debugf("Checking status to etcd endpoint %s", endpointAddress) + e.logger.Debug("Checking status to etcd endpoint", + logfields.Endpoints, endpointAddress, + ) status, err := e.client.Status(ctxTimeout, endpointAddress) if err != nil { @@ -1112,8 +1143,10 @@ func (e *etcdClient) statusChecker() { case e.statusCheckErrors <- err: default: // Channel's buffer is full, skip sending errors to the channel but log warnings instead - log.WithError(err). - Warning("Status check error channel is full, dropping this error") + e.logger.Warn( + "Status check error channel is full, dropping this error", + logfields.Error, err, + ) } } @@ -1138,9 +1171,15 @@ func (e *etcdClient) Status() *models.Status { // GetIfLocked returns value of key if the client is still holding the given lock. func (e *etcdClient) GetIfLocked(ctx context.Context, key string, lock KVLocker) (bv []byte, err error) { - defer func() { - Trace("GetIfLocked", err, logrus.Fields{fieldKey: key, fieldValue: string(bv)}) - }() + if traceEnabled { + defer func() { + Trace(e.logger, "GetIfLocked", + logfields.Error, err, + fieldKey, key, + fieldValue, string(bv), + ) + }() + } lr, err := e.limiter.Wait(ctx) if err != nil { return nil, Hint(err) @@ -1172,9 +1211,15 @@ func (e *etcdClient) GetIfLocked(ctx context.Context, key string, lock KVLocker) // Get returns value of key func (e *etcdClient) Get(ctx context.Context, key string) (bv []byte, err error) { - defer func() { - Trace("Get", err, logrus.Fields{fieldKey: key, fieldValue: string(bv)}) - }() + if traceEnabled { + defer func() { + Trace(e.logger, "Get", + logfields.Error, err, + fieldKey, key, + fieldValue, string(bv), + ) + }() + } lr, err := e.limiter.Wait(ctx) if err != nil { return nil, Hint(err) @@ -1198,9 +1243,14 @@ func (e *etcdClient) Get(ctx context.Context, key string) (bv []byte, err error) // DeleteIfLocked deletes a key if the client is still holding the given lock. func (e *etcdClient) DeleteIfLocked(ctx context.Context, key string, lock KVLocker) (err error) { - defer func() { - Trace("DeleteIfLocked", err, logrus.Fields{fieldKey: key}) - }() + if traceEnabled { + defer func() { + Trace(e.logger, "DeleteIfLocked", + logfields.Error, err, + fieldKey, key, + ) + }() + } lr, err := e.limiter.Wait(ctx) if err != nil { return Hint(err) @@ -1226,9 +1276,14 @@ func (e *etcdClient) DeleteIfLocked(ctx context.Context, key string, lock KVLock // Delete deletes a key func (e *etcdClient) Delete(ctx context.Context, key string) (err error) { - defer func() { - Trace("Delete", err, logrus.Fields{fieldKey: key}) - }() + if traceEnabled { + defer func() { + Trace(e.logger, "Delete", + logfields.Error, err, + fieldKey, key, + ) + }() + } lr, err := e.limiter.Wait(ctx) if err != nil { return Hint(err) @@ -1250,9 +1305,16 @@ func (e *etcdClient) Delete(ctx context.Context, key string) (err error) { // UpdateIfLocked updates a key if the client is still holding the given lock. func (e *etcdClient) UpdateIfLocked(ctx context.Context, key string, value []byte, lease bool, lock KVLocker) (err error) { - defer func() { - Trace("UpdateIfLocked", err, logrus.Fields{fieldKey: key, fieldValue: string(value), fieldAttachLease: lease}) - }() + if traceEnabled { + defer func() { + Trace(e.logger, "UpdateIfLocked", + logfields.Error, err, + fieldKey, key, + fieldValue, string(value), + fieldAttachLease, lease, + ) + }() + } var leaseID client.LeaseID if lease { leaseID, err = e.leaseManager.GetLeaseID(ctx, key) @@ -1285,9 +1347,16 @@ func (e *etcdClient) UpdateIfLocked(ctx context.Context, key string, value []byt // Update creates or updates a key func (e *etcdClient) Update(ctx context.Context, key string, value []byte, lease bool) (err error) { - defer func() { - Trace("Update", err, logrus.Fields{fieldKey: key, fieldValue: string(value), fieldAttachLease: lease}) - }() + if traceEnabled { + defer func() { + Trace(e.logger, "Update", + logfields.Error, err, + fieldKey, key, + fieldValue, string(value), + fieldAttachLease, lease, + ) + }() + } var leaseID client.LeaseID if lease { leaseID, err = e.leaseManager.GetLeaseID(ctx, key) @@ -1313,9 +1382,17 @@ func (e *etcdClient) Update(ctx context.Context, key string, value []byte, lease // UpdateIfDifferentIfLocked updates a key if the value is different and if the client is still holding the given lock. func (e *etcdClient) UpdateIfDifferentIfLocked(ctx context.Context, key string, value []byte, lease bool, lock KVLocker) (recreated bool, err error) { - defer func() { - Trace("UpdateIfDifferentIfLocked", err, logrus.Fields{fieldKey: key, fieldValue: value, fieldAttachLease: lease, "recreated": recreated}) - }() + if traceEnabled { + defer func() { + Trace(e.logger, "UpdateIfDifferentIfLocked", + logfields.Error, err, + fieldKey, key, + fieldValue, string(value), + fieldAttachLease, lease, + fieldRecreated, recreated, + ) + }() + } lr, err := e.limiter.Wait(ctx) if err != nil { return false, Hint(err) @@ -1355,9 +1432,17 @@ func (e *etcdClient) UpdateIfDifferentIfLocked(ctx context.Context, key string, // UpdateIfDifferent updates a key if the value is different func (e *etcdClient) UpdateIfDifferent(ctx context.Context, key string, value []byte, lease bool) (recreated bool, err error) { - defer func() { - Trace("UpdateIfDifferent", err, logrus.Fields{fieldKey: key, fieldValue: value, fieldAttachLease: lease, "recreated": recreated}) - }() + if traceEnabled { + defer func() { + Trace(e.logger, "UpdateIfDifferent", + logfields.Error, err, + fieldKey, key, + fieldValue, string(value), + fieldAttachLease, lease, + fieldRecreated, recreated, + ) + }() + } lr, err := e.limiter.Wait(ctx) if err != nil { return false, Hint(err) @@ -1385,9 +1470,17 @@ func (e *etcdClient) UpdateIfDifferent(ctx context.Context, key string, value [] // CreateOnlyIfLocked atomically creates a key if the client is still holding the given lock or fails if it already exists func (e *etcdClient) CreateOnlyIfLocked(ctx context.Context, key string, value []byte, lease bool, lock KVLocker) (success bool, err error) { - defer func() { - Trace("CreateOnlyIfLocked", err, logrus.Fields{fieldKey: key, fieldValue: value, fieldAttachLease: lease, "success": success}) - }() + if traceEnabled { + defer func() { + Trace(e.logger, "CreateOnlyIfLocked", + logfields.Error, err, + fieldKey, key, + fieldValue, string(value), + fieldAttachLease, lease, + fieldSuccess, success, + ) + }() + } var leaseID client.LeaseID if lease { leaseID, err = e.leaseManager.GetLeaseID(ctx, key) @@ -1450,9 +1543,17 @@ func (e *etcdClient) CreateOnlyIfLocked(ctx context.Context, key string, value [ // CreateOnly creates a key with the value and will fail if the key already exists func (e *etcdClient) CreateOnly(ctx context.Context, key string, value []byte, lease bool) (success bool, err error) { - defer func() { - Trace("CreateOnly", err, logrus.Fields{fieldKey: key, fieldValue: value, fieldAttachLease: lease, "success": success}) - }() + if traceEnabled { + defer func() { + Trace(e.logger, "CreateOnly", + logfields.Error, err, + fieldKey, key, + fieldValue, string(value), + fieldAttachLease, lease, + fieldSuccess, success, + ) + }() + } var leaseID client.LeaseID if lease { leaseID, err = e.leaseManager.GetLeaseID(ctx, key) @@ -1485,9 +1586,15 @@ func (e *etcdClient) CreateOnly(ctx context.Context, key string, value []byte, l // ListPrefixIfLocked returns a list of keys matching the prefix only if the client is still holding the given lock. func (e *etcdClient) ListPrefixIfLocked(ctx context.Context, prefix string, lock KVLocker) (v KeyValuePairs, err error) { - defer func() { - Trace("ListPrefixIfLocked", err, logrus.Fields{fieldPrefix: prefix, fieldNumEntries: len(v)}) - }() + if traceEnabled { + defer func() { + Trace(e.logger, "ListPrefixIfLocked", + logfields.Error, err, + fieldPrefix, prefix, + fieldNumEntries, len(v), + ) + }() + } lr, err := e.limiter.Wait(ctx) if err != nil { return nil, Hint(err) @@ -1523,9 +1630,15 @@ func (e *etcdClient) ListPrefixIfLocked(ctx context.Context, prefix string, lock // ListPrefix returns a map of matching keys func (e *etcdClient) ListPrefix(ctx context.Context, prefix string) (v KeyValuePairs, err error) { - defer func() { - Trace("ListPrefix", err, logrus.Fields{fieldPrefix: prefix, fieldNumEntries: len(v)}) - }() + if traceEnabled { + defer func() { + Trace(e.logger, "ListPrefix", + logfields.Error, err, + fieldPrefix, prefix, + fieldNumEntries, len(v), + ) + }() + } lr, err := e.limiter.Wait(ctx) if err != nil { return nil, Hint(err) @@ -1559,7 +1672,10 @@ func (e *etcdClient) Close() { close(e.stopStatusChecker) if err := e.client.Close(); err != nil { - e.logger.WithError(err).Warning("Failed to close etcd client") + e.logger.Warn( + "Failed to close etcd client", + logfields.Error, err, + ) } // Wait until all child goroutines spawned by the lease managers have terminated. @@ -1598,20 +1714,21 @@ func (e *etcdClient) expiredLeaseObserver(key string) { // UserEnforcePresence creates a user in etcd if not already present, and grants the specified roles. func (e *etcdClient) UserEnforcePresence(ctx context.Context, name string, roles []string) error { - scopedLog := e.logger.WithField(FieldUser, name) - - scopedLog.Debug("Creating user") + e.logger.Debug("Creating user", FieldUser, name) _, err := e.client.Auth.UserAddWithOptions(ctx, name, "", &client.UserAddOptions{NoPassword: true}) if err != nil { if errors.Is(err, v3rpcErrors.ErrUserAlreadyExist) { - scopedLog.Debug("User already exists") + e.logger.Debug("User already exists", FieldUser, name) } else { return err } } for _, role := range roles { - scopedLog.WithField(FieldRole, role).Debug("Granting role to user") + e.logger.Debug("Granting role to user", + FieldRole, role, + FieldUser, name, + ) _, err := e.client.Auth.UserGrantRole(ctx, name, role) if err != nil { @@ -1624,13 +1741,11 @@ func (e *etcdClient) UserEnforcePresence(ctx context.Context, name string, roles // UserEnforcePresence deletes a user from etcd, if present. func (e *etcdClient) UserEnforceAbsence(ctx context.Context, name string) error { - scopedLog := e.logger.WithField(FieldUser, name) - - scopedLog.Debug("Deleting user") + e.logger.Debug("Deleting user", FieldUser, name) _, err := e.client.Auth.UserDelete(ctx, name) if err != nil { if errors.Is(err, v3rpcErrors.ErrUserNotFound) { - scopedLog.Debug("User not found") + e.logger.Debug("User not found", FieldUser, name) } else { return err } diff --git a/vendor/github.com/cilium/cilium/pkg/kvstore/etcd_lease.go b/vendor/github.com/cilium/cilium/pkg/kvstore/etcd_lease.go index 2e0a2f6d97..2e6ae9094a 100644 --- a/vendor/github.com/cilium/cilium/pkg/kvstore/etcd_lease.go +++ b/vendor/github.com/cilium/cilium/pkg/kvstore/etcd_lease.go @@ -6,15 +6,16 @@ package kvstore import ( "context" "errors" + "log/slog" "strings" "sync" - "github.com/sirupsen/logrus" v3rpcErrors "go.etcd.io/etcd/api/v3/v3rpc/rpctypes" client "go.etcd.io/etcd/client/v3" "go.etcd.io/etcd/client/v3/concurrency" "github.com/cilium/cilium/pkg/lock" + "github.com/cilium/cilium/pkg/logging/logfields" "github.com/cilium/cilium/pkg/spanstat" "github.com/cilium/cilium/pkg/time" ) @@ -28,7 +29,7 @@ type leaseInfo struct { // which lease is attached to which etcd key. type etcdLeaseManager struct { client *client.Client - log logrus.FieldLogger + log *slog.Logger ttl time.Duration limit uint32 @@ -44,10 +45,10 @@ type etcdLeaseManager struct { } // newEtcdLeaseManager builds and returns a new lease manager instance. -func newEtcdLeaseManager(cl *client.Client, ttl time.Duration, limit uint32, expired func(key string), log logrus.FieldLogger) *etcdLeaseManager { +func newEtcdLeaseManager(logger *slog.Logger, cl *client.Client, ttl time.Duration, limit uint32, expired func(key string)) *etcdLeaseManager { return &etcdLeaseManager{ client: cl, - log: log, + log: logger, ttl: ttl, limit: limit, @@ -254,10 +255,11 @@ func (elm *etcdLeaseManager) newSession(ctx context.Context) (session *concurren elm.wg.Add(1) go elm.waitForExpiration(session) - elm.log.WithFields(logrus.Fields{ - "LeaseID": leaseID, - "TTL": elm.ttl, - }).Info("New lease successfully acquired") + elm.log.Info( + "New lease successfully acquired", + logfields.LeaseID, leaseID, + logfields.TTL, elm.ttl, + ) return session, nil } @@ -275,7 +277,10 @@ func (elm *etcdLeaseManager) waitForExpiration(session *concurrency.Session) { default: } - elm.log.WithField("LeaseID", session.Lease()).Warning("Lease expired") + elm.log.Warn( + "Lease expired", + logfields.LeaseID, session.Lease(), + ) elm.mu.Lock() delete(elm.leases, session.Lease()) diff --git a/vendor/github.com/cilium/cilium/pkg/kvstore/lock.go b/vendor/github.com/cilium/cilium/pkg/kvstore/lock.go index b28c74f083..6644a4f366 100644 --- a/vendor/github.com/cilium/cilium/pkg/kvstore/lock.go +++ b/vendor/github.com/cilium/cilium/pkg/kvstore/lock.go @@ -6,14 +6,15 @@ package kvstore import ( "context" "fmt" + "log/slog" "github.com/davecgh/go-spew/spew" "github.com/google/uuid" - "github.com/sirupsen/logrus" "github.com/cilium/cilium/pkg/debug" "github.com/cilium/cilium/pkg/defaults" "github.com/cilium/cilium/pkg/lock" + "github.com/cilium/cilium/pkg/logging/logfields" "github.com/cilium/cilium/pkg/time" ) @@ -66,11 +67,11 @@ func (pl *pathLocks) DebugStatus() string { return str } -func (pl *pathLocks) runGC() { +func (pl *pathLocks) runGC(logger *slog.Logger) { pl.mutex.Lock() for path, owner := range pl.lockPaths { if time.Since(owner.created) > staleLockTimeout { - log.WithField("path", path).Error("Forcefully unlocking local kvstore lock") + logger.Error("Forcefully unlocking local kvstore lock", fieldKey, path) delete(pl.lockPaths, path) } } @@ -113,6 +114,7 @@ type Lock struct { path string id uuid.UUID kvLock KVLocker + logger *slog.Logger } // LockPath locks the specified path. The key for the lock is not the path @@ -120,7 +122,7 @@ type Lock struct { // returned also contains a patch specific local Mutex which will be held. // // It is required to call Unlock() on the returned Lock to unlock -func LockPath(ctx context.Context, backend BackendOperations, path string) (l *Lock, err error) { +func LockPath(ctx context.Context, logger *slog.Logger, backend BackendOperations, path string) (l *Lock, err error) { id, err := kvstoreLocks.lock(ctx, path) if err != nil { return nil, err @@ -129,20 +131,20 @@ func LockPath(ctx context.Context, backend BackendOperations, path string) (l *L lock, err := backend.LockPath(ctx, path) if err != nil { kvstoreLocks.unlock(path, id) - Trace("Failed to lock", err, logrus.Fields{fieldKey: path}) + Trace(logger, "Failed to lock", fieldKey, path) err = fmt.Errorf("error while locking path %s: %w", path, err) return nil, err } - Trace("Successful lock", err, logrus.Fields{fieldKey: path}) - return &Lock{kvLock: lock, path: path, id: id}, err + Trace(logger, "Successful lock", fieldKey, path) + return &Lock{kvLock: lock, path: path, id: id, logger: logger}, err } // RunLockGC inspects all local kvstore locks to determine whether they have // been held longer than the stale lock timeout, and if so, unlocks them // forceably. -func RunLockGC() { - kvstoreLocks.runGC() +func RunLockGC(logger *slog.Logger) { + kvstoreLocks.runGC(logger) } // Unlock unlocks a lock @@ -154,12 +156,15 @@ func (l *Lock) Unlock(ctx context.Context) error { // Unlock kvstore mutex first err := l.kvLock.Unlock(ctx) if err != nil { - log.WithError(err).WithField("path", l.path).Error("Unable to unlock kvstore lock") + l.logger.Error("Unable to unlock kvstore lock", + logfields.Error, err, + fieldKey, l.path, + ) } // unlock local lock even if kvstore cannot be unlocked kvstoreLocks.unlock(l.path, l.id) - Trace("Unlocked", nil, logrus.Fields{fieldKey: l.path}) + Trace(l.logger, "Unlocked", fieldKey, l.path) return err } diff --git a/vendor/github.com/cilium/cilium/pkg/kvstore/logfields.go b/vendor/github.com/cilium/cilium/pkg/kvstore/logfields.go index c272cbb33c..b8eaa15009 100644 --- a/vendor/github.com/cilium/cilium/pkg/kvstore/logfields.go +++ b/vendor/github.com/cilium/cilium/pkg/kvstore/logfields.go @@ -4,12 +4,9 @@ package kvstore import ( - "github.com/cilium/cilium/pkg/logging" "github.com/cilium/cilium/pkg/logging/logfields" ) -var log = logging.DefaultLogger.WithField(logfields.LogSubsys, "kvstore") - const ( // fieldKVStoreModule is the name of the kvstore backend (etcd) fieldKVStoreModule = "module" @@ -40,4 +37,8 @@ const ( // FieldRole identifies a role in the kvstore FieldRole = "role" + + fieldRecreated = "recreated" + + fieldSuccess = "success" ) diff --git a/vendor/github.com/cilium/cilium/pkg/kvstore/store/cell.go b/vendor/github.com/cilium/cilium/pkg/kvstore/store/cell.go index 74730f7c0c..04ca5411f3 100644 --- a/vendor/github.com/cilium/cilium/pkg/kvstore/store/cell.go +++ b/vendor/github.com/cilium/cilium/pkg/kvstore/store/cell.go @@ -4,6 +4,8 @@ package store import ( + "log/slog" + "github.com/cilium/hive/cell" "github.com/cilium/cilium/pkg/metrics" @@ -25,23 +27,25 @@ type Factory interface { } type factoryImpl struct { + logger *slog.Logger metrics *Metrics } func (w *factoryImpl) NewSyncStore(clusterName string, backend SyncStoreBackend, prefix string, opts ...WSSOpt) SyncStore { - return newWorkqueueSyncStore(clusterName, backend, prefix, w.metrics, opts...) + return newWorkqueueSyncStore(w.logger, clusterName, backend, prefix, w.metrics, opts...) } func (w *factoryImpl) NewWatchStore(clusterName string, keyCreator KeyCreator, observer Observer, opts ...RWSOpt) WatchStore { - return newRestartableWatchStore(clusterName, keyCreator, observer, w.metrics, opts...) + return newRestartableWatchStore(w.logger, clusterName, keyCreator, observer, w.metrics, opts...) } func (w *factoryImpl) NewWatchStoreManager(backend WatchStoreBackend, clusterName string) WatchStoreManager { - return newWatchStoreManagerSync(backend, clusterName, w) + return newWatchStoreManagerSync(w.logger, backend, clusterName, w) } -func NewFactory(storeMetrics *Metrics) Factory { +func NewFactory(logger *slog.Logger, storeMetrics *Metrics) Factory { return &factoryImpl{ + logger: logger, metrics: storeMetrics, } } diff --git a/vendor/github.com/cilium/cilium/pkg/kvstore/store/store.go b/vendor/github.com/cilium/cilium/pkg/kvstore/store/store.go index 382bbf49fc..9b06675067 100644 --- a/vendor/github.com/cilium/cilium/pkg/kvstore/store/store.go +++ b/vendor/github.com/cilium/cilium/pkg/kvstore/store/store.go @@ -6,16 +6,15 @@ package store import ( "context" "fmt" + "log/slog" + "maps" "path" "strings" "sync" - "github.com/sirupsen/logrus" - "github.com/cilium/cilium/pkg/controller" "github.com/cilium/cilium/pkg/kvstore" "github.com/cilium/cilium/pkg/lock" - "github.com/cilium/cilium/pkg/logging" "github.com/cilium/cilium/pkg/logging/logfields" "github.com/cilium/cilium/pkg/option" "github.com/cilium/cilium/pkg/time" @@ -30,8 +29,6 @@ const ( var ( controllers controller.Manager - log = logging.DefaultLogger.WithField(logfields.LogSubsys, "shared-store") - kvstoreSyncControllerGroup = controller.NewGroup("kvstore-sync") ) @@ -100,6 +97,7 @@ func (c *Configuration) validate() error { // SharedStore is an instance of a shared store. It is created with // JoinSharedStore() and released with the SharedStore.Close() function. type SharedStore struct { + logger *slog.Logger // conf is a copy of the store configuration. This field is never // mutated after JoinSharedStore() so it is safe to access this without // a lock. @@ -207,12 +205,13 @@ func (kv *KVPair) Unmarshal(key string, data []byte) error { // store is initialized with the contents of the kvstore. An error is returned // if the contents cannot be retrieved synchronously from the kvstore. Starts a // controller to continuously synchronize the store with the kvstore. -func JoinSharedStore(c Configuration) (*SharedStore, error) { +func JoinSharedStore(logger *slog.Logger, c Configuration) (*SharedStore, error) { if err := c.validate(); err != nil { return nil, err } s := &SharedStore{ + logger: logger, conf: c, localKeys: map[string]LocalKey{}, sharedKeys: map[string]Key{}, @@ -223,6 +222,7 @@ func JoinSharedStore(c Configuration) (*SharedStore, error) { s.conf.Context, s.stop = context.WithCancel(s.conf.Context) s.name = "store-" + s.conf.Prefix + s.logger = s.logger.With(logfields.Name, s.name) s.controllerName = "kvstore-sync-" + s.name if err := s.listAndStartWatcher(); err != nil { @@ -271,7 +271,7 @@ func (s *SharedStore) Close(ctx context.Context) { for name, key := range s.localKeys { if err := s.backend.Delete(ctx, s.keyPath(key)); err != nil { - s.getLogger().WithError(err).Warning("Unable to delete key in kvstore") + s.logger.Warn("Unable to delete key in kvstore", logfields.Error, err) } delete(s.localKeys, name) @@ -356,12 +356,7 @@ func (s *SharedStore) NumEntries() int { func (s *SharedStore) SharedKeysMap() map[string]Key { s.mutex.RLock() defer s.mutex.RUnlock() - sharedKeysCopy := make(map[string]Key, len(s.sharedKeys)) - - for k, v := range s.sharedKeys { - sharedKeysCopy[k] = v - } - return sharedKeysCopy + return maps.Clone(s.sharedKeys) } // UpdateLocalKeySync synchronously synchronizes a local key with the kvstore @@ -395,19 +390,13 @@ func (s *SharedStore) DeleteLocalKey(ctx context.Context, key NamedKey) { if ok { if err != nil { - s.getLogger().WithError(err).Warning("Unable to delete key in kvstore") + s.logger.Warn("Unable to delete key in kvstore", logfields.Error, err) } s.onDelete(key) } } -func (s *SharedStore) getLogger() *logrus.Entry { - return log.WithFields(logrus.Fields{ - "storeName": s.name, - }) -} - func (s *SharedStore) updateKey(name string, value []byte) error { newKey := s.conf.KeyCreator() if err := newKey.Unmarshal(name, value); err != nil { @@ -435,16 +424,21 @@ func (s *SharedStore) deleteSharedKey(name string) { _, ok := s.sharedKeys[name] s.mutex.RUnlock() if ok { - s.getLogger().WithFields(logrus.Fields{"key": name, "timeWindow": s.conf.SharedKeyDeleteDelay}). - Warning("Received delete event for key which re-appeared within delay time window") + s.logger.Warn( + "Received delete event for key which re-appeared within delay time window", + logfields.Key, name, + logfields.TimeWindow, s.conf.SharedKeyDeleteDelay, + ) return } s.onDelete(existingKey) }() } else { - s.getLogger().WithField("key", name). - Warning("Unable to find deleted key in local state") + s.logger.Warn( + "Unable to find deleted key in local state", + logfields.Key, name, + ) } } @@ -469,19 +463,19 @@ func (s *SharedStore) listAndStartWatcher() error { func (s *SharedStore) watcher(listDone chan struct{}) { events := s.backend.ListAndWatch(s.conf.Context, s.conf.Prefix) + logger := s.logger for event := range events { if event.Typ == kvstore.EventTypeListDone { - s.getLogger().Debug("Initial list of objects received from kvstore") + logger.Debug("Initial list of objects received from kvstore") close(listDone) continue } - logger := s.getLogger().WithFields(logrus.Fields{ - "key": event.Key, - "eventType": event.Typ, - }) - - logger.Debugf("Received key update via kvstore [value %s]", string(event.Value)) + logger.Debug("Received key update via kvstore", + logfields.Value, string(event.Value), + logfields.Key, event.Key, + logfields.EventType, event.Typ, + ) keyName := strings.TrimPrefix(event.Key, s.conf.Prefix) if keyName[0] == '/' { @@ -491,12 +485,22 @@ func (s *SharedStore) watcher(listDone chan struct{}) { switch event.Typ { case kvstore.EventTypeCreate, kvstore.EventTypeModify: if err := s.updateKey(keyName, event.Value); err != nil { - logger.WithError(err).Warningf("Unable to unmarshal store value: %s", string(event.Value)) + logger.Warn( + "Unable to unmarshal store value", + logfields.Error, err, + logfields.Value, string(event.Value), + logfields.Key, event.Key, + logfields.EventType, event.Typ, + ) } case kvstore.EventTypeDelete: if localKey := s.lookupLocalKey(keyName); localKey != nil { - logger.Warning("Received delete event for local key. Re-creating the key in the kvstore") + logger.Warn( + "Received delete event for local key. Re-creating the key in the kvstore", + logfields.Key, event.Key, + logfields.EventType, event.Typ, + ) s.syncLocalKey(s.conf.Context, localKey, true) } else { diff --git a/vendor/github.com/cilium/cilium/pkg/kvstore/store/syncstore.go b/vendor/github.com/cilium/cilium/pkg/kvstore/store/syncstore.go index bc2008ace6..e946d2983f 100644 --- a/vendor/github.com/cilium/cilium/pkg/kvstore/store/syncstore.go +++ b/vendor/github.com/cilium/cilium/pkg/kvstore/store/syncstore.go @@ -7,13 +7,13 @@ import ( "bytes" "context" "fmt" + "log/slog" "path" "strings" "sync" "sync/atomic" "github.com/prometheus/client_golang/prometheus" - "github.com/sirupsen/logrus" "k8s.io/client-go/util/workqueue" "github.com/cilium/cilium/pkg/kvstore" @@ -74,7 +74,7 @@ type wqSyncStore struct { syncedKey string syncedCallbacks []func(context.Context) - log *logrus.Entry + log *slog.Logger queuedMetric prometheus.Gauge errorsMetric prometheus.Counter syncedMetric prometheus.Gauge @@ -118,7 +118,7 @@ func WSSWithSyncedKeyOverride(key string) WSSOpt { // NewWorkqueueSyncStore returns a SyncStore instance which leverages a workqueue // to coalescence update/delete requests and handle retries in case of errors. -func newWorkqueueSyncStore(clusterName string, backend SyncStoreBackend, prefix string, m *Metrics, opts ...WSSOpt) SyncStore { +func newWorkqueueSyncStore(logger *slog.Logger, clusterName string, backend SyncStoreBackend, prefix string, m *Metrics, opts ...WSSOpt) SyncStore { wss := &wqSyncStore{ backend: backend, prefix: prefix, @@ -129,14 +129,14 @@ func newWorkqueueSyncStore(clusterName string, backend SyncStoreBackend, prefix limiter: workqueue.DefaultTypedControllerRateLimiter[workqueueKey](), syncedKey: prefix, - log: log.WithField(logfields.Prefix, prefix), + log: logger.With(logfields.Prefix, prefix), } for _, opt := range opts { opt(wss) } - wss.log = wss.log.WithField(logfields.ClusterName, wss.source) + wss.log = wss.log.With(logfields.ClusterName, wss.source) wss.workqueue = workqueue.NewTypedRateLimitingQueue(wss.limiter) wss.queuedMetric = m.KVStoreSyncQueueSize.WithLabelValues(kvstore.GetScopeFromKey(prefix), wss.source) wss.errorsMetric = m.KVStoreSyncErrors.WithLabelValues(kvstore.GetScopeFromKey(prefix), wss.source) @@ -154,7 +154,7 @@ func (wss *wqSyncStore) Run(ctx context.Context) { wss.backend.RegisterLeaseExpiredObserver(wss.prefix, wss.handleExpiredLease) wss.backend.RegisterLeaseExpiredObserver(wss.getSyncedKey(), wss.handleExpiredLease) - wss.log.WithField(logfields.Workers, wss.workers).Info("Starting workqueue-based sync store") + wss.log.Info("Starting workqueue-based sync store", logfields.Workers, wss.workers) wg.Add(int(wss.workers)) for i := uint(0); i < wss.workers; i++ { go func() { @@ -187,7 +187,7 @@ func (wss *wqSyncStore) UpsertKey(_ context.Context, k Key) error { prevValue, loaded := wss.state.Swap(key, value) if loaded && bytes.Equal(prevValue, value) { - wss.log.WithField(logfields.Key, k).Debug("ignoring upsert request for already up-to-date key") + wss.log.Debug("ignoring upsert request for already up-to-date key", logfields.Key, k) } else { if !wss.synced.Load() { wss.pendingSync.Store(key, struct{}{}) @@ -209,7 +209,7 @@ func (wss *wqSyncStore) DeleteKey(_ context.Context, k NamedKey) error { wss.workqueue.Add(workqueueKey{value: key}) wss.queuedMetric.Set(float64(wss.workqueue.Len())) } else { - wss.log.WithField(logfields.Key, key).Debug("ignoring delete request for non-existing key") + wss.log.Debug("ignoring delete request for non-existing key", logfields.Key, key) } return nil @@ -268,27 +268,33 @@ func (wss *wqSyncStore) handle(ctx context.Context, item workqueueKey) error { } func (wss *wqSyncStore) handleUpsert(ctx context.Context, key string, value []byte) error { - scopedLog := wss.log.WithField(logfields.Key, key) - err := wss.backend.Update(ctx, wss.keyPath(key), value, wss.withLease) if err != nil { - scopedLog.WithError(err).Warning("Failed upserting key in kvstore. Retrying...") + wss.log.Warn("Failed upserting key in kvstore. Retrying...", + logfields.Error, err, + logfields.Key, key, + ) return err } - scopedLog.Debug("Upserted key in kvstore") + wss.log.Debug("Upserted key in kvstore", + logfields.Key, key, + ) return nil } func (wss *wqSyncStore) handleDelete(ctx context.Context, key string) error { - scopedLog := wss.log.WithField(logfields.Key, key) - if err := wss.backend.Delete(ctx, wss.keyPath(key)); err != nil { - scopedLog.WithError(err).Warning("Failed deleting key from kvstore. Retrying...") + wss.log.Warn("Failed deleting key from kvstore. Retrying...", + logfields.Error, err, + logfields.Key, key, + ) return err } - scopedLog.Debug("Deleted key from kvstore") + wss.log.Debug("Deleted key from kvstore", + logfields.Key, key, + ) return nil } @@ -305,15 +311,19 @@ func (wss *wqSyncStore) handleSync(ctx context.Context, skipCallbacks bool) erro } key := wss.getSyncedKey() - scopedLog := wss.log.WithField(logfields.Key, key) err := wss.backend.Update(ctx, key, []byte(time.Now().Format(time.RFC3339)), wss.withLease) if err != nil { - scopedLog.WithError(err).Warning("Failed upserting synced key in kvstore. Retrying...") + wss.log.Warn("Failed upserting synced key in kvstore. Retrying...", + logfields.Error, err, + logfields.Key, key, + ) return err } - wss.log.Info("Initial synchronization from the external source completed") + wss.log.Info("Initial synchronization from the external source completed", + logfields.Key, key, + ) wss.syncedMetric.Set(metrics.BoolToFloat64(true)) // Execute any callback that might have been registered. @@ -341,7 +351,7 @@ func (wss *wqSyncStore) handleExpiredLease(key string) { key = strings.TrimPrefix(strings.TrimPrefix(key, wss.prefix), "/") _, ok := wss.state.Load(key) if ok { - wss.log.WithField(logfields.Key, key).Debug("enqueuing upsert request for key as the attached lease expired") + wss.log.Debug("enqueuing upsert request for key as the attached lease expired", logfields.Key, key) if !wss.synced.Load() { wss.pendingSync.Store(key, struct{}{}) } diff --git a/vendor/github.com/cilium/cilium/pkg/kvstore/store/watchstore.go b/vendor/github.com/cilium/cilium/pkg/kvstore/store/watchstore.go index 0287fb0a0d..bfd6aba568 100644 --- a/vendor/github.com/cilium/cilium/pkg/kvstore/store/watchstore.go +++ b/vendor/github.com/cilium/cilium/pkg/kvstore/store/watchstore.go @@ -5,13 +5,14 @@ package store import ( "context" + "log/slog" "strings" "sync/atomic" "github.com/prometheus/client_golang/prometheus" - "github.com/sirupsen/logrus" "github.com/cilium/cilium/pkg/kvstore" + "github.com/cilium/cilium/pkg/logging" "github.com/cilium/cilium/pkg/logging/logfields" "github.com/cilium/cilium/pkg/metrics" "github.com/cilium/cilium/pkg/metrics/metric" @@ -87,7 +88,8 @@ type restartableWatchStore struct { state map[string]*rwsEntry numEntries atomic.Uint64 - log *logrus.Entry + baseLogger *slog.Logger + log *slog.Logger entriesMetric prometheus.Gauge syncMetric metric.Vec[metric.Gauge] } @@ -96,7 +98,7 @@ type restartableWatchStore struct { // restarting the watch operation multiple times, automatically handling // the emission of deletion events for all stale entries (if enabled). It // shall be restarted only once the previous Watch execution terminated. -func newRestartableWatchStore(clusterName string, keyCreator KeyCreator, observer Observer, m *Metrics, opts ...RWSOpt) WatchStore { +func newRestartableWatchStore(logger *slog.Logger, clusterName string, keyCreator KeyCreator, observer Observer, m *Metrics, opts ...RWSOpt) WatchStore { rws := &restartableWatchStore{ source: clusterName, keyCreator: keyCreator, @@ -104,7 +106,8 @@ func newRestartableWatchStore(clusterName string, keyCreator KeyCreator, observe state: make(map[string]*rwsEntry), - log: log, + log: logger, + baseLogger: logger, entriesMetric: metrics.NoOpGauge, syncMetric: m.KVStoreInitialSyncCompleted, } @@ -113,7 +116,8 @@ func newRestartableWatchStore(clusterName string, keyCreator KeyCreator, observe opt(rws) } - rws.log = rws.log.WithField(logfields.ClusterName, rws.source) + rws.baseLogger = rws.baseLogger.With(logfields.ClusterName, rws.source) + rws.log = rws.baseLogger return rws } @@ -128,7 +132,7 @@ func (rws *restartableWatchStore) Watch(ctx context.Context, backend WatchStoreB prefix = prefix + "/" } - rws.log = rws.log.WithField(logfields.Prefix, prefix) + rws.log = rws.baseLogger.With(logfields.Prefix, prefix) syncedMetric := rws.syncMetric.WithLabelValues( kvstore.GetScopeFromKey(prefix), rws.source, "read") @@ -136,7 +140,7 @@ func (rws *restartableWatchStore) Watch(ctx context.Context, backend WatchStoreB syncedMetric.Set(metrics.BoolToFloat64(false)) if rws.watching.Swap(true) { - rws.log.Panic("Cannot start the watch store while still running") + logging.Panic(rws.log, "Cannot start the watch store while still running") } defer func() { @@ -172,10 +176,11 @@ func (rws *restartableWatchStore) Watch(ctx context.Context, backend WatchStoreB } key := strings.TrimPrefix(event.Key, prefix) - rws.log.WithFields(logrus.Fields{ - logfields.Key: key, - logfields.Event: event.Typ, - }).Debug("Received event from kvstore") + rws.log.Debug( + "Received event from kvstore", + logfields.Key, key, + logfields.Event, event.Typ, + ) switch event.Typ { case kvstore.EventTypeCreate, kvstore.EventTypeModify: @@ -201,7 +206,7 @@ func (rws *restartableWatchStore) Synced() bool { // when no watch operation is in progress. func (rws *restartableWatchStore) Drain() { if rws.watching.Swap(true) { - rws.log.Panic("Cannot drain the watch store while still running") + logging.Panic(rws.log, "Cannot drain the watch store while still running") } defer rws.watching.Store(false) @@ -216,7 +221,10 @@ func (rws *restartableWatchStore) Drain() { func (rws *restartableWatchStore) drainKeys(staleOnly bool) { for key, entry := range rws.state { if !staleOnly || entry.stale { - rws.log.WithField(logfields.Key, key).Debug("Emitting deletion event for stale key") + rws.log.Debug( + "Emitting deletion event for stale key", + logfields.Key, key, + ) rws.handleDelete(key) } } @@ -225,10 +233,12 @@ func (rws *restartableWatchStore) drainKeys(staleOnly bool) { func (rws *restartableWatchStore) handleUpsert(key string, value []byte) { entry := &rwsEntry{key: rws.keyCreator()} if err := entry.key.Unmarshal(key, value); err != nil { - rws.log.WithFields(logrus.Fields{ - logfields.Key: key, - logfields.Value: string(value), - }).WithError(err).Warning("Unable to unmarshal value") + rws.log.Warn( + "Unable to unmarshal value", + logfields.Error, err, + logfields.Key, key, + logfields.Value, string(value), + ) return } @@ -241,7 +251,10 @@ func (rws *restartableWatchStore) handleUpsert(key string, value []byte) { func (rws *restartableWatchStore) handleDelete(key string) { entry, ok := rws.state[key] if !ok { - rws.log.WithField(logfields.Key, key).Warning("Received deletion event for unknown key") + rws.log.Warn( + "Received deletion event for unknown key", + logfields.Key, key, + ) return } diff --git a/vendor/github.com/cilium/cilium/pkg/kvstore/store/watchstoremgr.go b/vendor/github.com/cilium/cilium/pkg/kvstore/store/watchstoremgr.go index 44f85114c7..08c7776d23 100644 --- a/vendor/github.com/cilium/cilium/pkg/kvstore/store/watchstoremgr.go +++ b/vendor/github.com/cilium/cilium/pkg/kvstore/store/watchstoremgr.go @@ -5,13 +5,13 @@ package store import ( "context" + "log/slog" "path" "sync" "sync/atomic" - "github.com/sirupsen/logrus" - "github.com/cilium/cilium/pkg/kvstore" + "github.com/cilium/cilium/pkg/logging" "github.com/cilium/cilium/pkg/logging/logfields" ) @@ -36,13 +36,13 @@ type wsmCommon struct { functions map[string]WSMFunc running atomic.Bool - log *logrus.Entry + log *slog.Logger } -func newWSMCommon(clusterName string) wsmCommon { +func newWSMCommon(logger *slog.Logger, clusterName string) wsmCommon { return wsmCommon{ functions: make(map[string]WSMFunc), - log: log.WithField(logfields.ClusterName, clusterName), + log: logger.With(logfields.ClusterName, clusterName), } } @@ -50,7 +50,7 @@ func newWSMCommon(clusterName string) wsmCommon { // It cannot be called once Run() has started. func (mgr *wsmCommon) Register(prefix string, function WSMFunc) { if mgr.running.Load() { - mgr.log.Panic("Cannot call Register while the watch store manager is running") + logging.Panic(mgr.log, "Cannot call Register while the watch store manager is running") } mgr.functions[prefix] = function @@ -58,24 +58,24 @@ func (mgr *wsmCommon) Register(prefix string, function WSMFunc) { func (mgr *wsmCommon) ready(ctx context.Context, prefix string) { if fn := mgr.functions[prefix]; fn != nil { - mgr.log.WithField(logfields.Prefix, prefix).Debug("Starting function for kvstore prefix") + mgr.log.Debug("Starting function for kvstore prefix", logfields.Prefix, prefix) delete(mgr.functions, prefix) mgr.wg.Add(1) go func() { defer mgr.wg.Done() fn(ctx) - mgr.log.WithField(logfields.Prefix, prefix).Debug("Function terminated for kvstore prefix") + mgr.log.Debug("Function terminated for kvstore prefix", logfields.Prefix, prefix) }() } else { - mgr.log.WithField(logfields.Prefix, prefix).Debug("Received sync event for unregistered prefix") + mgr.log.Debug("Received sync event for unregistered prefix", logfields.Prefix, prefix) } } func (mgr *wsmCommon) run() { mgr.log.Info("Starting watch store manager") if mgr.running.Swap(true) { - mgr.log.Panic("Cannot start the watch store manager twice") + logging.Panic(mgr.log, "Cannot start the watch store manager twice") } } @@ -98,9 +98,9 @@ type wsmSync struct { // This ensures that the synchronization of the keys hosted under the given prefix // have been successfully synchronized from the external source, even in case an // ephemeral kvstore is used. -func newWatchStoreManagerSync(backend WatchStoreBackend, clusterName string, factory Factory) WatchStoreManager { +func newWatchStoreManagerSync(logger *slog.Logger, backend WatchStoreBackend, clusterName string, factory Factory) WatchStoreManager { mgr := wsmSync{ - wsmCommon: newWSMCommon(clusterName), + wsmCommon: newWSMCommon(logger, clusterName), clusterName: clusterName, backend: backend, } @@ -127,9 +127,9 @@ type wsmImmediate struct { // NewWatchStoreManagerImmediate implements the WatchStoreManager interface, // immediately starting the registered functions once Run() is executed. -func NewWatchStoreManagerImmediate(clusterName string) WatchStoreManager { +func NewWatchStoreManagerImmediate(logger *slog.Logger, clusterName string) WatchStoreManager { return &wsmImmediate{ - wsmCommon: newWSMCommon(clusterName), + wsmCommon: newWSMCommon(logger, clusterName), } } diff --git a/vendor/github.com/cilium/cilium/pkg/kvstore/trace.go b/vendor/github.com/cilium/cilium/pkg/kvstore/trace.go index bcbf5b0e27..807a9dbd4d 100644 --- a/vendor/github.com/cilium/cilium/pkg/kvstore/trace.go +++ b/vendor/github.com/cilium/cilium/pkg/kvstore/trace.go @@ -4,7 +4,7 @@ package kvstore import ( - "github.com/sirupsen/logrus" + "log/slog" ) var ( @@ -17,8 +17,11 @@ func EnableTracing() { } // Trace is used to trace kvstore debug messages -func Trace(msg string, err error, fields logrus.Fields) { +func Trace(logger *slog.Logger, msg string, fields ...any) { if traceEnabled { - log.WithError(err).WithFields(fields).Debug(msg) + logger.Debug( + msg, + fields..., + ) } } diff --git a/vendor/github.com/cilium/cilium/pkg/labels/labels.go b/vendor/github.com/cilium/cilium/pkg/labels/labels.go index 6c2acb3d45..909975bd9b 100644 --- a/vendor/github.com/cilium/cilium/pkg/labels/labels.go +++ b/vendor/github.com/cilium/cilium/pkg/labels/labels.go @@ -7,6 +7,7 @@ import ( "bytes" "encoding/json" "fmt" + "maps" "net/netip" "slices" "strings" @@ -625,9 +626,7 @@ func (l Labels) GetModel() []string { // // Labels{Label{key1, value3, source4}, Label{key2, value3, source4}} func (l Labels) MergeLabels(from Labels) { - for k, v := range from { - l[k] = v - } + maps.Copy(l, from) } // Remove is similar to MergeLabels, but returns a new Labels object with the diff --git a/vendor/github.com/cilium/cilium/pkg/labels/oplabels.go b/vendor/github.com/cilium/cilium/pkg/labels/oplabels.go index 4dad8e7ab8..31c1788bf5 100644 --- a/vendor/github.com/cilium/cilium/pkg/labels/oplabels.go +++ b/vendor/github.com/cilium/cilium/pkg/labels/oplabels.go @@ -5,6 +5,7 @@ package labels import ( "fmt" + "maps" "github.com/sirupsen/logrus" @@ -74,13 +75,9 @@ func (o *OpLabels) SplitUserLabelChanges(lbls Labels) (add, del Labels) { func (o *OpLabels) IdentityLabels() Labels { enabled := make(Labels, len(o.Custom)+len(o.OrchestrationIdentity)) - for k, v := range o.Custom { - enabled[k] = v - } + maps.Copy(enabled, o.Custom) - for k, v := range o.OrchestrationIdentity { - enabled[k] = v - } + maps.Copy(enabled, o.OrchestrationIdentity) return enabled } @@ -98,21 +95,13 @@ func (o *OpLabels) GetIdentityLabel(key string) (l Label, found bool) { func (o *OpLabels) AllLabels() Labels { all := make(Labels, len(o.Custom)+len(o.OrchestrationInfo)+len(o.OrchestrationIdentity)+len(o.Disabled)) - for k, v := range o.Custom { - all[k] = v - } + maps.Copy(all, o.Custom) - for k, v := range o.Disabled { - all[k] = v - } + maps.Copy(all, o.Disabled) - for k, v := range o.OrchestrationIdentity { - all[k] = v - } + maps.Copy(all, o.OrchestrationIdentity) - for k, v := range o.OrchestrationInfo { - all[k] = v - } + maps.Copy(all, o.OrchestrationInfo) return all } diff --git a/vendor/github.com/cilium/cilium/pkg/logging/logfields/logfields.go b/vendor/github.com/cilium/cilium/pkg/logging/logfields/logfields.go index 349cce2abe..1f6fd7fae9 100644 --- a/vendor/github.com/cilium/cilium/pkg/logging/logfields/logfields.go +++ b/vendor/github.com/cilium/cilium/pkg/logging/logfields/logfields.go @@ -604,6 +604,8 @@ const ( // K8sNamespace is the namespace something belongs to K8sNamespace = "k8sNamespace" + K8sNamespaceIllegal = "k8sNamespace.illegal" + // K8sIdentityAnnotation is a k8s non-identifying annotations on k8s objects K8sIdentityAnnotation = "k8sIdentityAnnotation" @@ -1332,4 +1334,116 @@ const ( Operation = "operation" KeyPairSN = "keyPairSN" + + AnnotationsOld = "annotationsOld" + + LabelsNew = "labelsNew" + + File = "file" + + Timeout = "timeout" + + EtcdDataDir = "etcdDataDir" + + EtcdClusterName = "etcdClusterName" + + EtcdInitialClusterToken = "etcdInitialClusterToken" + + EtcdListenClientUrl = "loopbackEndpoint" + + EtcdBinary = "etcdBinaryLocation" + + EtcdFlags = "etcdCmd" + + EtcdExitCode = "etcdExitCode" + + EtcdClientConfig = "etcdClientConfig" + + EtcdUsername = "etcdUsername" + + EtcdRoleName = "etcdRoleName" + + EtcdPermission = "etcdPermission" + + EtcdRangeStart = "etcdRangeStart" + + EtcdRangeEnd = "etcdRangeEnd" + + K8sExportName = "K8sExportName" + + ReliablyMissing = "reliablyMissing" + + KVStoreBackendConfigurationSuffix = "kvStoreBackendConfiguration.Suffix" + + KVStoreBackendConfigurationTyp = "kvStoreBackendConfiguration.Typ" + + KVStoreBackendConfigurationBasePath = "kvStoreBackendConfiguration.BasePath" + + ReadFromKVStore = "readFromKVStore" + + TTL = "ttl" + + ConfigPath = "configPath" + + KeepAliveHeartbeat = "keepAliveHeartbeat" + + KeepAliveTimeout = "keepAliveTimeout" + + RateLimit = "rateLimit" + + MaxInflight = "maxInflight" + + ListLimit = "listLimit" + + TimeWindow = "timeWindow" + + Entry = "entry" + + LastEventReceived = "lastEventReceived" + + PodIP = "podIP" + + PodIPs = "podIPs" + + NewPodIP = "newPodIP" + + NewPodIPs = "newPodIPs" + + NewHostIP = "newHostIP" + + OldPodIP = "oldPodIP" + + OldPodIPs = "oldPodIPs" + + OldHostIP = "oldHostIP" + + OldLabels = "oldLabels" + + OldAnnotations = "oldAnnotations" + + NewLabels = "newLabels" + + NewAnnotations = "newAnnotations" + + OldService = "oldService" + + OldEndpoints = "oldEndpoints" + + LenEndpoints = "lenEndpoints" + + LenBackends = "lenBackends" + + EnableK8sTerminatingEndpoint = "enableK8sTerminatingEndpoint" + + CRDs = "CRDs" + + PodCIDRs = "podCIDRs" + + LenIPs = "lenIPs" + + Alias = "alias" + + GlobalConfiguration = "globalConfiguration" + + Annotation = "annotation" ) diff --git a/vendor/github.com/cilium/cilium/pkg/logging/logging.go b/vendor/github.com/cilium/cilium/pkg/logging/logging.go index 9e03b20437..5dd668a81a 100644 --- a/vendor/github.com/cilium/cilium/pkg/logging/logging.go +++ b/vendor/github.com/cilium/cilium/pkg/logging/logging.go @@ -13,6 +13,7 @@ import ( "log/slog" "os" "regexp" + "slices" "strings" "sync/atomic" "time" @@ -368,13 +369,7 @@ func (o LogOptions) validateOpts(logDriver string, supportedOpts map[string]bool return fmt.Errorf("provided configuration key %q is not supported as a logging option for log driver %s", k, logDriver) } if validValues, ok := validKVs[k]; ok { - valid := false - for _, vv := range validValues { - if v == vv { - valid = true - break - } - } + valid := slices.Contains(validValues, v) if !valid { return fmt.Errorf("provided configuration value %q is not a valid value for %q in log driver %s, valid values: %v", v, k, logDriver, validValues) } diff --git a/vendor/github.com/cilium/cilium/pkg/metrics/metrics.go b/vendor/github.com/cilium/cilium/pkg/metrics/metrics.go index 91a7c75a4d..55dd5429b8 100644 --- a/vendor/github.com/cilium/cilium/pkg/metrics/metrics.go +++ b/vendor/github.com/cilium/cilium/pkg/metrics/metrics.go @@ -420,6 +420,9 @@ var ( // ConntrackGCDuration the duration of the conntrack GC process in milliseconds. ConntrackGCDuration = NoOpObserverVec + // ConntrackInterval is the interval in secodns between conntrack GC runs + ConntrackInterval = NoOpGaugeVec + // ConntrackDumpReset marks the count for conntrack dump resets ConntrackDumpResets = NoOpCounterVec @@ -689,6 +692,7 @@ type LegacyMetrics struct { ConntrackGCSize metric.Vec[metric.Gauge] NatGCSize metric.Vec[metric.Gauge] ConntrackGCDuration metric.Vec[metric.Observer] + ConntrackInterval metric.Vec[metric.Gauge] ConntrackDumpResets metric.Vec[metric.Counter] SignalsHandled metric.Vec[metric.Counter] ServicesEventsCount metric.Vec[metric.Counter] @@ -982,6 +986,14 @@ func NewLegacyMetrics() *LegacyMetrics { "labeled by datapath family and completion status", }, []string{LabelDatapathFamily, LabelProtocol, LabelStatus}), + ConntrackInterval: metric.NewGaugeVec(metric.GaugeOpts{ + ConfigName: Namespace + "_" + SubsystemDatapath + "_conntrack_gc_interval_seconds", + Namespace: Namespace, + Subsystem: SubsystemDatapath, + Name: "conntrack_gc_interval_seconds", + Help: "Interval in seconds between conntrack garbage collector runs", + }, []string{"global"}), + ConntrackDumpResets: metric.NewCounterVec(metric.CounterOpts{ ConfigName: Namespace + "_" + SubsystemDatapath + "_conntrack_dump_resets_total", Namespace: Namespace, @@ -1411,6 +1423,7 @@ func NewLegacyMetrics() *LegacyMetrics { ConntrackGCSize = lm.ConntrackGCSize NatGCSize = lm.NatGCSize ConntrackGCDuration = lm.ConntrackGCDuration + ConntrackInterval = lm.ConntrackInterval ConntrackDumpResets = lm.ConntrackDumpResets SignalsHandled = lm.SignalsHandled ServicesEventsCount = lm.ServicesEventsCount diff --git a/vendor/github.com/cilium/cilium/pkg/metrics/metrics_unix.go b/vendor/github.com/cilium/cilium/pkg/metrics/metrics_linux.go similarity index 62% rename from vendor/github.com/cilium/cilium/pkg/metrics/metrics_unix.go rename to vendor/github.com/cilium/cilium/pkg/metrics/metrics_linux.go index 184d3baa71..a8a4606cbf 100644 --- a/vendor/github.com/cilium/cilium/pkg/metrics/metrics_unix.go +++ b/vendor/github.com/cilium/cilium/pkg/metrics/metrics_linux.go @@ -1,24 +1,9 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright Authors of Cilium -//go:build !windows - package metrics -import ( - "golang.org/x/sys/unix" - - "github.com/cilium/cilium/pkg/datapath/linux/probes" -) - -// Errno2Outcome converts a unix.Errno to LabelOutcome -func Errno2Outcome(errno unix.Errno) string { - if errno != 0 { - return LabelValueOutcomeFail - } - - return LabelValueOutcomeSuccess -} +import "github.com/cilium/cilium/pkg/datapath/linux/probes" func enableIfIndexMetric() bool { // On kernels which do not provide ifindex via the FIB, Cilium needs diff --git a/vendor/github.com/cilium/cilium/pkg/metrics/metrics_windows.go b/vendor/github.com/cilium/cilium/pkg/metrics/metrics_other.go similarity index 87% rename from vendor/github.com/cilium/cilium/pkg/metrics/metrics_windows.go rename to vendor/github.com/cilium/cilium/pkg/metrics/metrics_other.go index dc4333ab32..083251c265 100644 --- a/vendor/github.com/cilium/cilium/pkg/metrics/metrics_windows.go +++ b/vendor/github.com/cilium/cilium/pkg/metrics/metrics_other.go @@ -1,6 +1,8 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright Authors of Cilium +//go:build !linux + package metrics func enableIfIndexMetric() bool { diff --git a/vendor/github.com/cilium/cilium/pkg/monitor/api/types.go b/vendor/github.com/cilium/cilium/pkg/monitor/api/types.go index 8bb2125fc2..1b3bce4edd 100644 --- a/vendor/github.com/cilium/cilium/pkg/monitor/api/types.go +++ b/vendor/github.com/cilium/cilium/pkg/monitor/api/types.go @@ -7,6 +7,7 @@ import ( "encoding/json" "fmt" "net" + "slices" "sort" "strconv" "strings" @@ -138,13 +139,7 @@ func (m *MessageTypeFilter) Type() string { } func (m *MessageTypeFilter) Contains(typ int) bool { - for _, v := range *m { - if v == typ { - return true - } - } - - return false + return slices.Contains(*m, typ) } // Must be synchronized with diff --git a/vendor/github.com/cilium/cilium/pkg/node/address.go b/vendor/github.com/cilium/cilium/pkg/node/address.go index 8bfeea9948..1d58303d05 100644 --- a/vendor/github.com/cilium/cilium/pkg/node/address.go +++ b/vendor/github.com/cilium/cilium/pkg/node/address.go @@ -117,7 +117,7 @@ func setDefaultPrefix(cfg *option.DaemonConfig, device string, node *LocalNode) } node.IPv4AllocCIDR = cidr.NewCIDR(ip4net) - log.WithField(logfields.V4Prefix, node.IPv4AllocCIDR).Info("Using autogenerated IPv4 allocation range") + log.WithField(logfields.V4Prefix, node.IPv4AllocCIDR).Debug("Using autogenerated IPv4 allocation range") } } @@ -147,7 +147,7 @@ func setDefaultPrefix(cfg *option.DaemonConfig, device string, node *LocalNode) } node.IPv6AllocCIDR = cidr.NewCIDR(ip6net) - log.WithField(logfields.V6Prefix, node.IPv6AllocCIDR).Info("Using autogenerated IPv6 allocation range") + log.WithField(logfields.V6Prefix, node.IPv6AllocCIDR).Debug("Using autogenerated IPv6 allocation range") } } } diff --git a/vendor/github.com/cilium/cilium/pkg/option/config.go b/vendor/github.com/cilium/cilium/pkg/option/config.go index 3ee4db8ebe..3da7219f26 100644 --- a/vendor/github.com/cilium/cilium/pkg/option/config.go +++ b/vendor/github.com/cilium/cilium/pkg/option/config.go @@ -9,6 +9,7 @@ import ( "encoding/json" "errors" "fmt" + "log/slog" "math" "net" "net/netip" @@ -60,9 +61,6 @@ const ( // ClusterMeshHealthPort is the TCP port for ClusterMesh apiserver health API ClusterMeshHealthPort = "clustermesh-health-port" - // AgentLabels are additional labels to identify this agent - AgentLabels = "agent-labels" - // AllowICMPFragNeeded allows ICMP Fragmentation Needed type packets in policy. AllowICMPFragNeeded = "allow-icmp-frag-needed" @@ -823,10 +821,6 @@ const ( // KVstoreConnectivityTimeout is the timeout when performing kvstore operations KVstoreConnectivityTimeout = "kvstore-connectivity-timeout" - // KVstorePodNetworkSupport enables the support for running the Cilium KVstore - // in pod network. - KVstorePodNetworkSupport = "kvstore-pod-network-support" - // IdentityChangeGracePeriod is the name of the // IdentityChangeGracePeriod option IdentityChangeGracePeriod = "identity-change-grace-period" @@ -1301,6 +1295,27 @@ func BindEnvWithLegacyEnvFallback(vp *viper.Viper, optName, legacyEnvName string vp.BindEnv(optName, envName) } +// LogRegisteredSlogOptions logs all options that where bound to viper. +func LogRegisteredSlogOptions(vp *viper.Viper, entry *slog.Logger) { + keys := vp.AllKeys() + slices.Sort(keys) + for _, k := range keys { + ss := vp.GetStringSlice(k) + if len(ss) == 0 { + sm := vp.GetStringMap(k) + for k, v := range sm { + ss = append(ss, fmt.Sprintf("%s=%s", k, v)) + } + } + + if len(ss) > 0 { + entry.Info(fmt.Sprintf(" --%s='%s'", k, strings.Join(ss, ","))) + } else { + entry.Info(fmt.Sprintf(" --%s='%s'", k, vp.GetString(k))) + } + } +} + // LogRegisteredOptions logs all options that where bound to viper. func LogRegisteredOptions(vp *viper.Viper, entry *logrus.Entry) { keys := vp.AllKeys() @@ -1379,9 +1394,6 @@ type DaemonConfig struct { // ClusterMeshHealthPort is the TCP port for ClusterMesh apiserver health API ClusterMeshHealthPort int - // AgentLabels contains additional labels to identify this agent in monitor events. - AgentLabels []string - // IPv6ClusterAllocCIDR is the base CIDR used to allocate IPv6 node // CIDRs if allocation is not performed by an orchestration system IPv6ClusterAllocCIDR string @@ -1794,10 +1806,6 @@ type DaemonConfig struct { // KVstoreConnectivityTimeout is the timeout when performing kvstore operations KVstoreConnectivityTimeout time.Duration - // KVstorePodNetworkSupport enables the support for running the Cilium KVstore - // in pod network. - KVstorePodNetworkSupport bool - // IdentityChangeGracePeriod is the grace period that needs to pass // before an endpoint that has changed its identity will start using // that new identity. During the grace period, the new identity has @@ -2272,7 +2280,6 @@ var ( ToFQDNsMaxIPsPerHost: defaults.ToFQDNsMaxIPsPerHost, KVstorePeriodicSync: defaults.KVstorePeriodicSync, KVstoreConnectivityTimeout: defaults.KVstoreConnectivityTimeout, - KVstorePodNetworkSupport: defaults.KVstorePodNetworkSupport, IdentityChangeGracePeriod: defaults.IdentityChangeGracePeriod, IdentityRestoreGracePeriod: defaults.IdentityRestoreGracePeriodK8s, FixedIdentityMapping: make(map[string]string), @@ -2377,7 +2384,7 @@ func (c *DaemonConfig) AreDevicesRequired() bool { } // NeedBPFHostOnWireGuardDevice returns true if the agent needs to attach -// a BPF program on the Ingress of Cilium's WireGuard device +// cil_from_netdev on the Ingress of Cilium's WireGuard device func (c *DaemonConfig) NeedBPFHostOnWireGuardDevice() bool { if !c.EnableWireguard { return false @@ -2405,6 +2412,27 @@ func (c *DaemonConfig) NeedBPFHostOnWireGuardDevice() bool { return false } +// NeedEgressOnWireGuardDevice returns true if the agent needs to attach +// cil_to_wireguard on the Egress of Cilium's WireGuard device +func (c *DaemonConfig) NeedEgressOnWireGuardDevice() bool { + if !c.EnableWireguard { + return false + } + + // No need to handle rev-NAT xlations in wireguard with tunneling enabled. + if c.TunnelingEnabled() { + return false + } + + // Attaching cil_to_wireguard to cilium_wg0 egress is required for handling + // the rev-NAT xlations when encrypting KPR traffic. + if c.EnableNodePort && c.EnableL7Proxy && c.KubeProxyReplacement == KubeProxyReplacementTrue { + return true + } + + return false +} + // MasqueradingEnabled returns true if either IPv4 or IPv6 masquerading is enabled. func (c *DaemonConfig) MasqueradingEnabled() bool { return c.EnableIPv4Masquerade || c.EnableIPv6Masquerade @@ -2512,12 +2540,7 @@ func (c *DaemonConfig) K8sNetworkPolicyEnabled() bool { } func (c *DaemonConfig) PolicyCIDRMatchesNodes() bool { - for _, mode := range c.PolicyCIDRMatchMode { - if mode == "nodes" { - return true - } - } - return false + return slices.Contains(c.PolicyCIDRMatchMode, "nodes") } // PerNodeLabelsEnabled returns true if per-node labels feature @@ -2560,10 +2583,9 @@ func (c *DaemonConfig) LoadBalancerUsesDSR() bool { c.LoadBalancerModeAnnotation } -// KVstoreEnabledWithoutPodNetworkSupport returns whether Cilium is configured to connect -// to an external KVStore, and the support for running it in pod network is disabled. -func (c *DaemonConfig) KVstoreEnabledWithoutPodNetworkSupport() bool { - return c.KVStore != "" && !c.KVstorePodNetworkSupport +// KVstoreEnabled returns whether Cilium is configured to connect to an external KVStore. +func (c *DaemonConfig) KVstoreEnabled() bool { + return c.KVStore != "" } func (c *DaemonConfig) validateIPv6ClusterAllocCIDR() error { @@ -2817,7 +2839,6 @@ func (c *DaemonConfig) Populate(vp *viper.Viper) { c.AgentHealthPort = vp.GetInt(AgentHealthPort) c.ClusterHealthPort = vp.GetInt(ClusterHealthPort) c.ClusterMeshHealthPort = vp.GetInt(ClusterMeshHealthPort) - c.AgentLabels = vp.GetStringSlice(AgentLabels) c.AllowICMPFragNeeded = vp.GetBool(AllowICMPFragNeeded) c.AllowLocalhost = vp.GetString(AllowLocalhost) c.AnnotateK8sNode = vp.GetBool(AnnotateK8sNode) @@ -2907,7 +2928,6 @@ func (c *DaemonConfig) Populate(vp *viper.Viper) { c.KVstoreLeaseTTL = vp.GetDuration(KVstoreLeaseTTL) c.KVstorePeriodicSync = vp.GetDuration(KVstorePeriodicSync) c.KVstoreConnectivityTimeout = vp.GetDuration(KVstoreConnectivityTimeout) - c.KVstorePodNetworkSupport = vp.GetBool(KVstorePodNetworkSupport) c.KVstoreMaxConsecutiveQuorumErrors = vp.GetUint(KVstoreMaxConsecutiveQuorumErrorsName) c.LabelPrefixFile = vp.GetString(LabelPrefixFile) c.Labels = vp.GetStringSlice(Labels) diff --git a/vendor/github.com/cilium/cilium/pkg/option/daemon.go b/vendor/github.com/cilium/cilium/pkg/option/daemon.go index 003f1cc604..16a918734b 100644 --- a/vendor/github.com/cilium/cilium/pkg/option/daemon.go +++ b/vendor/github.com/cilium/cilium/pkg/option/daemon.go @@ -3,6 +3,8 @@ package option +import "maps" + var ( specPolicyTracing = Option{ Description: "Enable tracing when resolving policy (Debug)", @@ -31,9 +33,7 @@ var ( ) func init() { - for k, v := range DaemonMutableOptionLibrary { - DaemonOptionLibrary[k] = v - } + maps.Copy(DaemonOptionLibrary, DaemonMutableOptionLibrary) } // ParseDaemonOption parses a string as daemon option diff --git a/vendor/github.com/cilium/cilium/pkg/option/endpoint.go b/vendor/github.com/cilium/cilium/pkg/option/endpoint.go index b72049ebe3..14bda6a80d 100644 --- a/vendor/github.com/cilium/cilium/pkg/option/endpoint.go +++ b/vendor/github.com/cilium/cilium/pkg/option/endpoint.go @@ -3,6 +3,8 @@ package option +import "maps" + var ( endpointMutableOptionLibrary = OptionLibrary{ ConntrackAccounting: &specConntrackAccounting, @@ -21,9 +23,5 @@ var ( ) func GetEndpointMutableOptionLibrary() OptionLibrary { - opt := OptionLibrary{} - for k, v := range endpointMutableOptionLibrary { - opt[k] = v - } - return opt + return maps.Clone(endpointMutableOptionLibrary) } diff --git a/vendor/github.com/cilium/cilium/pkg/option/option.go b/vendor/github.com/cilium/cilium/pkg/option/option.go index ad8e0467a3..108b3d9ebd 100644 --- a/vendor/github.com/cilium/cilium/pkg/option/option.go +++ b/vendor/github.com/cilium/cilium/pkg/option/option.go @@ -6,6 +6,7 @@ package option import ( "encoding/json" "fmt" + "maps" "slices" "strings" @@ -60,13 +61,7 @@ const ( // RequiresOption returns true if the option requires the specified option `name`. func (o Option) RequiresOption(name string) bool { - for _, o := range o.Requires { - if o == name { - return true - } - } - - return false + return slices.Contains(o.Requires, name) } type OptionLibrary map[string]*Option @@ -141,11 +136,7 @@ func (l OptionLibrary) Validate(name string, value string) error { type OptionMap map[string]OptionSetting func (om OptionMap) DeepCopy() OptionMap { - cpy := make(OptionMap, len(om)) - for k, v := range om { - cpy[k] = v - } - return cpy + return maps.Clone(om) } // IntOptions member functions with external access do not require diff --git a/vendor/github.com/cilium/cilium/pkg/policy/api/http.go b/vendor/github.com/cilium/cilium/pkg/policy/api/http.go index 2239420a1e..78da15c157 100644 --- a/vendor/github.com/cilium/cilium/pkg/policy/api/http.go +++ b/vendor/github.com/cilium/cilium/pkg/policy/api/http.go @@ -22,8 +22,8 @@ const ( ) // HeaderMatch extends the HeaderValue for matching requirement of a -// named header field against an immediate string, a secret value, or -// a regex. If none of the optional fields is present, then the +// named header field against an immediate string or a secret value. +// If none of the optional fields is present, then the // header value is not matched, only presence of the header is enough. type HeaderMatch struct { // Mismatch identifies what to do in case there is no match. The default is @@ -113,7 +113,6 @@ type PortRuleHTTP struct { // of regular expressions (e.g. that specified by ECMAScript), so this function // may return some false positives. If the rule is invalid, returns an error. func (h *PortRuleHTTP) Sanitize() error { - if h.Path != "" { _, err := regexp.Compile(h.Path) if err != nil { diff --git a/vendor/github.com/cilium/cilium/pkg/policy/api/rule_validation.go b/vendor/github.com/cilium/cilium/pkg/policy/api/rule_validation.go index 57f61f279b..cc5f787835 100644 --- a/vendor/github.com/cilium/cilium/pkg/policy/api/rule_validation.go +++ b/vendor/github.com/cilium/cilium/pkg/policy/api/rule_validation.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "net/netip" + "slices" "strconv" "strings" @@ -566,10 +567,8 @@ func (pr *PortRule) sanitize(ingress bool) error { if len(pr.ServerNames) > 0 && !pr.Rules.IsEmpty() && pr.TerminatingTLS == nil { return fmt.Errorf("ServerNames are not allowed with L7 rules without TLS termination") } - for _, sn := range pr.ServerNames { - if sn == "" { - return errEmptyServerName - } + if slices.Contains(pr.ServerNames, "") { + return errEmptyServerName } if len(pr.Ports) > maxPorts { diff --git a/vendor/github.com/cilium/cilium/pkg/policy/api/selector.go b/vendor/github.com/cilium/cilium/pkg/policy/api/selector.go index 7dc6c987fb..f1eafbead2 100644 --- a/vendor/github.com/cilium/cilium/pkg/policy/api/selector.go +++ b/vendor/github.com/cilium/cilium/pkg/policy/api/selector.go @@ -10,15 +10,13 @@ import ( k8sLbls "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/labels" slim_metav1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1" - validation "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1/validation" + "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1/validation" "github.com/cilium/cilium/pkg/labels" "github.com/cilium/cilium/pkg/logging" "github.com/cilium/cilium/pkg/logging/logfields" "github.com/cilium/cilium/pkg/metrics" ) -var log = logging.DefaultLogger.WithField(logfields.LogSubsys, "policy-api") - // EndpointSelector is a wrapper for k8s LabelSelector. type EndpointSelector struct { *slim_metav1.LabelSelector `json:",inline"` @@ -169,8 +167,13 @@ func labelSelectorToRequirements(labelSelector *slim_metav1.LabelSelector) *k8sL selector, err := slim_metav1.LabelSelectorAsSelector(labelSelector) if err != nil { metrics.PolicyChangeTotal.WithLabelValues(metrics.LabelValueOutcomeFail).Inc() - log.WithError(err).WithField(logfields.EndpointLabelSelector, - logfields.Repr(labelSelector)).Error("unable to construct selector in label selector") + // FIXME @aanm do we still need to log this error? + logging.DefaultSlogLogger.Error( + "unable to construct selector in label selector", + logfields.LogSubsys, "policy-api", + logfields.Error, err, + logfields.EndpointLabelSelector, labelSelector, + ) return nil } metrics.PolicyChangeTotal.WithLabelValues(metrics.LabelValueOutcomeSuccess).Inc() diff --git a/vendor/github.com/cilium/cilium/pkg/policy/api/utils.go b/vendor/github.com/cilium/cilium/pkg/policy/api/utils.go index f6019b3234..19d8dae0b9 100644 --- a/vendor/github.com/cilium/cilium/pkg/policy/api/utils.go +++ b/vendor/github.com/cilium/cilium/pkg/policy/api/utils.go @@ -6,18 +6,13 @@ package api import ( "context" "fmt" + "slices" "strings" ) // Exists returns true if the HTTP rule already exists in the list of rules func (h *PortRuleHTTP) Exists(rules L7Rules) bool { - for _, existingRule := range rules.HTTP { - if h.Equal(existingRule) { - return true - } - } - - return false + return slices.ContainsFunc(rules.HTTP, h.Equal) } // Equal returns true if both HTTP rules are equal @@ -62,24 +57,12 @@ func (h *HeaderMatch) Equal(o *HeaderMatch) bool { // Exists returns true if the DNS rule already exists in the list of rules func (d *PortRuleDNS) Exists(rules L7Rules) bool { - for _, existingRule := range rules.DNS { - if d.Equal(existingRule) { - return true - } - } - - return false + return slices.ContainsFunc(rules.DNS, d.Equal) } // Exists returns true if the L7 rule already exists in the list of rules func (h *PortRuleL7) Exists(rules L7Rules) bool { - for _, existingRule := range rules.L7 { - if h.Equal(existingRule) { - return true - } - } - - return false + return slices.ContainsFunc(rules.L7, h.Equal) } // Equal returns true if both rules are equal diff --git a/vendor/github.com/cilium/cilium/pkg/policy/config.go b/vendor/github.com/cilium/cilium/pkg/policy/config.go index 000d663e3e..21cf75d0e3 100644 --- a/vendor/github.com/cilium/cilium/pkg/policy/config.go +++ b/vendor/github.com/cilium/cilium/pkg/policy/config.go @@ -7,14 +7,11 @@ import ( ipcacheTypes "github.com/cilium/cilium/pkg/ipcache/types" "github.com/cilium/cilium/pkg/labels" "github.com/cilium/cilium/pkg/lock" - "github.com/cilium/cilium/pkg/logging" - "github.com/cilium/cilium/pkg/logging/logfields" "github.com/cilium/cilium/pkg/source" "github.com/cilium/cilium/pkg/time" ) var ( - log = logging.DefaultLogger.WithField(logfields.LogSubsys, "policy") mutex lock.RWMutex // Protects enablePolicy enablePolicy string // Whether policy enforcement is enabled. ) diff --git a/vendor/github.com/cilium/cilium/pkg/policy/l4.go b/vendor/github.com/cilium/cilium/pkg/policy/l4.go index 03a9e8b764..5705208ddc 100644 --- a/vendor/github.com/cilium/cilium/pkg/policy/l4.go +++ b/vendor/github.com/cilium/cilium/pkg/policy/l4.go @@ -7,6 +7,7 @@ import ( "bytes" "encoding/json" "fmt" + "log/slog" "math/bits" "sort" "strconv" @@ -15,7 +16,6 @@ import ( "unique" cilium "github.com/cilium/proxy/go/cilium/api" - "github.com/sirupsen/logrus" k8sTypes "k8s.io/apimachinery/pkg/types" "github.com/cilium/cilium/api/v1/models" @@ -154,6 +154,10 @@ func (a StringSet) Merge(b StringSet) StringSet { // PerSelectorPolicy contains policy rules for a CachedSelector, i.e. for a // selection of numerical identities. type PerSelectorPolicy struct { + // L7Parser specifies the L7 protocol parser (optional). If specified as + // an empty string, then means that no L7 proxy redirect is performed. + L7Parser L7ParserType `json:"-"` + // TerminatingTLS is the TLS context for the connection terminated by // the L7 proxy. For egress policy this specifies the server-side TLS // parameters to be applied on the connections originated from the local @@ -175,19 +179,17 @@ type PerSelectorPolicy struct { // TLS handshake. ServerNames StringSet `json:"serverNames,omitempty"` - // isRedirect is 'true' when traffic must be redirected - isRedirect bool `json:"-"` - - // Listener is an optional fully qualified name of a Envoy Listner defined in a CiliumEnvoyConfig CRD that should be - // used for this traffic instead of the default listener + // Listener is an optional fully qualified name of a Envoy Listner defined in a + // CiliumEnvoyConfig CRD that should be used for this traffic instead of the default + // listener Listener string `json:"listener,omitempty"` - // Priority of the listener used when multiple listeners would apply to the same + // Priority of the proxy redirect used when multiple proxy ports would apply to the same // MapStateEntry. // Lower numbers indicate higher priority. Except for the default 0, which indicates the // lowest priority. If higher priority desired, a low unique number like 1, 2, or 3 should // be explicitly specified here. - Priority uint8 `json:"priority,omitempty"` + Priority ListenerPriority `json:"priority,omitempty"` // Pre-computed HTTP rules, computed after rule merging is complete EnvoyHTTPRules *cilium.HttpNetworkPolicyRules `json:"-"` @@ -198,8 +200,8 @@ type PerSelectorPolicy struct { api.L7Rules - // Authentication is the kind of cryptographic authentication required for the traffic to be allowed - // at L3, if any. + // Authentication is the kind of cryptographic authentication required for the traffic to be + // allowed at L3, if any. Authentication *api.Authentication `json:"auth,omitempty"` // IsDeny is set if this L4Filter contains should be denied @@ -209,10 +211,10 @@ type PerSelectorPolicy struct { // Equal returns true if 'a' and 'b' represent the same L7 Rules func (a *PerSelectorPolicy) Equal(b *PerSelectorPolicy) bool { return a == nil && b == nil || a != nil && b != nil && + a.L7Parser == b.L7Parser && a.TerminatingTLS.Equal(b.TerminatingTLS) && a.OriginatingTLS.Equal(b.OriginatingTLS) && a.ServerNames.Equal(b.ServerNames) && - a.isRedirect == b.isRedirect && a.Listener == b.Listener && a.Priority == b.Priority && (a.Authentication == nil && b.Authentication == nil || a.Authentication != nil && a.Authentication.DeepEqual(b.Authentication)) && @@ -229,7 +231,7 @@ func (a *PerSelectorPolicy) GetListener() string { } // GetPriority returns the pritority of the listener of the PerSelectorPolicy. -func (a *PerSelectorPolicy) GetPriority() uint8 { +func (a *PerSelectorPolicy) GetPriority() ListenerPriority { if a == nil { return 0 } @@ -275,13 +277,13 @@ func (a *PerSelectorPolicy) getAuthRequirement() AuthRequirement { } // IsRedirect returns true if the L7Rules are a redirect. -func (a *PerSelectorPolicy) IsRedirect() bool { - return a != nil && a.isRedirect +func (sp *PerSelectorPolicy) IsRedirect() bool { + return sp != nil && sp.L7Parser != "" } // HasL7Rules returns whether the `L7Rules` contains any L7 rules. -func (a *PerSelectorPolicy) HasL7Rules() bool { - return !a.L7Rules.IsEmpty() +func (sp *PerSelectorPolicy) HasL7Rules() bool { + return sp != nil && !sp.L7Rules.IsEmpty() } // L7DataMap contains a map of L7 rules per endpoint where key is a CachedSelector @@ -351,6 +353,54 @@ const ( ParserTypeDNS L7ParserType = "dns" ) +type ListenerPriority = types.ListenerPriority + +// API listener priorities and corresponding defaults for L7 parser types +// 0 - default (low) priority for all proxy redirects +// 1 - highest listener priority +// .. +// 100 - lowest (non-default) listener priority +// 101 - priority for HTTP parser type +// 106 - priority for the Kafka parser type +// 111 - priority for the proxylib parsers +// 116 - priority for TLS interception parsers (can be promoted to HTTP/Kafka/proxylib) +// 121 - priority for DNS parser type +// 126 - default priority for CRD parser type +// 127 - reserved (listener priority passed as 0) +// +// MapStateEntry stores this reverted in 'ProxyPortPriority' where higher numbers have higher +// precedence +const ( + ListenerPriorityNone ListenerPriority = 0 + ListenerPriorityHTTP ListenerPriority = 101 + ListenerPriorityKafka ListenerPriority = 106 + ListenerPriorityProxylib ListenerPriority = 111 + ListenerPriorityTLS ListenerPriority = 116 + ListenerPriorityDNS ListenerPriority = 121 + ListenerPriorityCRD ListenerPriority = 126 +) + +// defaultPriority maps the parser type to an "API listener priority" +func (l7 L7ParserType) defaultPriority() ListenerPriority { + switch l7 { + case ParserTypeNone: + return ListenerPriorityNone // no priority + case ParserTypeHTTP: + return ListenerPriorityHTTP + case ParserTypeKafka: + return ListenerPriorityKafka + default: // proxylib parsers + return ListenerPriorityProxylib + case ParserTypeTLS: + return ListenerPriorityTLS + case ParserTypeDNS: + return ListenerPriorityDNS + case ParserTypeCRD: + // CRD type can have an explicit higher priority in range 1-100 + return ListenerPriorityCRD + } +} + // redirectTypes is a bitmask of redirection types of multiple filters type redirectTypes uint16 @@ -481,9 +531,6 @@ type L4Filter struct { // restriction (such as no L7 rules). Holds references to the cached selectors, which must // be released! PerSelectorPolicies L7DataMap `json:"l7-rules,omitempty"` - // L7Parser specifies the L7 protocol parser (optional). If specified as - // an empty string, then means that no L7 proxy redirect is performed. - L7Parser L7ParserType `json:"-"` // Ingress is true if filter applies at ingress; false if it applies at egress. Ingress bool `json:"-"` // RuleOrigin is a set of rule labels tracking which policy rules are the origin for this @@ -512,11 +559,6 @@ func (l4 *L4Filter) GetPerSelectorPolicies() L7DataMap { return l4.PerSelectorPolicies } -// GetL7Parser returns the L7ParserType of the L4Filter. -func (l4 *L4Filter) GetL7Parser() L7ParserType { - return l4.L7Parser -} - // GetIngress returns whether the L4Filter applies at ingress or egress. func (l4 *L4Filter) GetIngress() bool { return l4.Ingress @@ -534,7 +576,6 @@ func (l4 *L4Filter) Equals(bL4 *L4Filter) bool { l4.PortName == bL4.PortName && l4.Protocol == bL4.Protocol && l4.Ingress == bL4.Ingress && - l4.L7Parser == bL4.L7Parser && l4.wildcard == bL4.wildcard { if len(l4.PerSelectorPolicies) != len(bL4.PerSelectorPolicies) { @@ -596,7 +637,7 @@ func (c *ChangeState) Size() int { // 'redirects' is the map of currently realized redirects, it is used to find the proxy port for any redirects. // p.SelectorCache is used as Identities interface during this call, which only has GetPrefix() that // needs no lock. -func (l4 *L4Filter) toMapState(p *EndpointPolicy, features policyFeatures, changes ChangeState) { +func (l4 *L4Filter) toMapState(logger *slog.Logger, p *EndpointPolicy, features policyFeatures, changes ChangeState) { port := l4.Port proto := l4.U8Proto @@ -605,15 +646,15 @@ func (l4 *L4Filter) toMapState(p *EndpointPolicy, features policyFeatures, chang direction = trafficdirection.Ingress } - logger := log + scopedLog := logger if option.Config.Debug { - logger = log.WithFields(logrus.Fields{ - logfields.EndpointID: p.PolicyOwner.GetID(), - logfields.Port: port, - logfields.PortName: l4.PortName, - logfields.Protocol: proto, - logfields.TrafficDirection: direction, - }) + scopedLog = logger.With( + logfields.EndpointID, p.PolicyOwner.GetID(), + logfields.Port, port, + logfields.PortName, l4.PortName, + logfields.Protocol, proto, + logfields.TrafficDirection, direction, + ) } // resolve named port @@ -642,7 +683,10 @@ func (l4 *L4Filter) toMapState(p *EndpointPolicy, features policyFeatures, chang isL3L4withWildcardPresent := isL4Wildcard && cs != l4.wildcard if isL3L4withWildcardPresent && wildcardRule.covers(currentRule) { - logger.WithField(logfields.EndpointSelector, cs).Debug("ToMapState: Skipping L3/L4 key due to existing L4-only key") + scopedLog.Debug( + "ToMapState: Skipping L3/L4 key due to existing L4-only key", + logfields.EndpointSelector, cs, + ) continue } @@ -669,7 +713,11 @@ func (l4 *L4Filter) toMapState(p *EndpointPolicy, features policyFeatures, chang // Skip unrealized redirects; this happens routineously just // before new redirects are realized. Once created, we are called // again. - logger.WithError(err).WithField(logfields.EndpointSelector, cs).Debugf("Skipping unrealized redirect") + scopedLog.Debug( + "Skipping unrealized redirect", + logfields.Error, err, + logfields.EndpointSelector, cs, + ) continue } } @@ -682,10 +730,16 @@ func (l4 *L4Filter) toMapState(p *EndpointPolicy, features policyFeatures, chang if port == 0 { // Allow-all - logger.WithField(logfields.EndpointSelector, cs).Debug("ToMapState: allow all") + scopedLog.Debug( + "ToMapState: allow all", + logfields.EndpointSelector, cs, + ) } else { // L4 allow - logger.WithField(logfields.EndpointSelector, cs).Debug("ToMapState: L4 allow all") + scopedLog.Debug( + "ToMapState: L4 allow all", + logfields.EndpointSelector, cs, + ) } } continue @@ -694,17 +748,19 @@ func (l4 *L4Filter) toMapState(p *EndpointPolicy, features policyFeatures, chang idents := cs.GetSelections(p.VersionHandle) if option.Config.Debug { if isDenyRule { - logger.WithFields(logrus.Fields{ - logfields.Version: p.VersionHandle, - logfields.EndpointSelector: cs, - logfields.PolicyID: idents, - }).Debug("ToMapState: Denied remote IDs") + scopedLog.Debug( + "ToMapState: Denied remote IDs", + logfields.Version, p.VersionHandle, + logfields.EndpointSelector, cs, + logfields.PolicyID, idents, + ) } else { - logger.WithFields(logrus.Fields{ - logfields.Version: p.VersionHandle, - logfields.EndpointSelector: cs, - logfields.PolicyID: idents, - }).Debug("ToMapState: Allowed remote IDs") + scopedLog.Debug( + "ToMapState: Allowed remote IDs", + logfields.Version, p.VersionHandle, + logfields.EndpointSelector, cs, + logfields.PolicyID, idents, + ) } } for _, id := range idents { @@ -724,11 +780,12 @@ func (l4 *L4Filter) toMapState(p *EndpointPolicy, features policyFeatures, chang } } if option.Config.Debug { - log.WithFields(logrus.Fields{ - logfields.PolicyKeysAdded: changes.Adds, - logfields.PolicyKeysDeleted: changes.Deletes, - logfields.PolicyEntriesOld: changes.old, - }).Debug("ToMapChange changes") + scopedLog.Debug( + "ToMapChange changes", + logfields.PolicyKeysAdded, changes.Adds, + logfields.PolicyKeysDeleted, changes.Deletes, + logfields.PolicyEntriesOld, changes.old, + ) } } @@ -738,12 +795,13 @@ func (l4 *L4Filter) toMapState(p *EndpointPolicy, features policyFeatures, chang // // The caller is responsible for making sure the same identity is not // present in both 'added' and 'deleted'. -func (l4 *L4Filter) IdentitySelectionUpdated(cs types.CachedSelector, added, deleted []identity.NumericIdentity) { - log.WithFields(logrus.Fields{ - logfields.EndpointSelector: cs, - logfields.AddedPolicyID: added, - logfields.DeletedPolicyID: deleted, - }).Debug("identities selected by L4Filter updated") +func (l4 *L4Filter) IdentitySelectionUpdated(logger *slog.Logger, cs types.CachedSelector, added, deleted []identity.NumericIdentity) { + logger.Debug( + "identities selected by L4Filter updated", + logfields.EndpointSelector, cs, + logfields.AddedPolicyID, added, + logfields.DeletedPolicyID, deleted, + ) // Skip updates on wildcard selectors, as datapath and L7 // proxies do not need enumeration of all ids for L3 wildcard. @@ -758,12 +816,15 @@ func (l4 *L4Filter) IdentitySelectionUpdated(cs types.CachedSelector, added, del // that we could not push updates on an unstable policy. l4Policy := l4.policy.Load() if l4Policy != nil { - l4Policy.AccumulateMapChanges(l4, cs, added, deleted) + l4Policy.AccumulateMapChanges(logger, l4, cs, added, deleted) } } -func (l4 *L4Filter) IdentitySelectionCommit(txn *versioned.Tx) { - log.WithField(logfields.NewVersion, txn).Debug("identity selection updates done") +func (l4 *L4Filter) IdentitySelectionCommit(logger *slog.Logger, txn *versioned.Tx) { + logger.Debug( + "identity selection updates done", + logfields.NewVersion, txn, + ) // Push endpoint policy incremental sync. // @@ -808,16 +869,15 @@ func (l4 *L4Filter) cacheFQDNSelector(sel api.FQDNSelector, lbls stringLabels, s } // add L7 rules for all endpoints in the L7DataMap -func (l7 L7DataMap) addPolicyForSelector(rules *api.L7Rules, terminatingTLS, originatingTLS *TLSContext, auth *api.Authentication, deny bool, sni []string, listener string, priority uint8) { - isRedirect := !deny && (listener != "" || terminatingTLS != nil || originatingTLS != nil || len(sni) > 0 || !rules.IsEmpty()) +func (l7 L7DataMap) addPolicyForSelector(l7Parser L7ParserType, rules *api.L7Rules, terminatingTLS, originatingTLS *TLSContext, auth *api.Authentication, deny bool, sni []string, listener string, priority ListenerPriority) { for epsel := range l7 { l7policy := &PerSelectorPolicy{ + L7Parser: l7Parser, TerminatingTLS: terminatingTLS, OriginatingTLS: originatingTLS, Authentication: auth, IsDeny: deny, ServerNames: NewStringSet(sni), - isRedirect: isRedirect, Listener: listener, Priority: priority, } @@ -838,13 +898,17 @@ const ( // getCerts reads certificates out of the PolicyContext, reading from k8s or local files depending on config // and puts the values into the relevant keys in the TLSContext. Note that if the returned TLSContext.FromFile is // `false`, then this will be read from Kubernetes. -func (l4 *L4Filter) getCerts(policyCtx PolicyContext, tls *api.TLSContext, direction TLSDirection) (*TLSContext, error) { +func (l4 *L4Filter) getCerts(logger *slog.Logger, policyCtx PolicyContext, tls *api.TLSContext, direction TLSDirection) (*TLSContext, error) { if tls == nil { return nil, nil } ca, public, private, inlineSecrets, err := policyCtx.GetTLSContext(tls) if err != nil { - log.WithError(err).Warningf("policy: Error getting %s TLS Context.", direction) + logger.Warn( + "policy: Error getting TLS Context", + logfields.Error, err, + logfields.TrafficDirection, direction, + ) return nil, err } @@ -863,7 +927,7 @@ func (l4 *L4Filter) getCerts(policyCtx PolicyContext, tls *api.TLSContext, direc return nil, fmt.Errorf("invalid TLS direction: %s", direction) } } else { - log.Debug("Secret being read from Kubernetes", "secret", k8sTypes.NamespacedName(*tls.Secret)) + logger.Debug("Secret being read from Kubernetes", logfields.Secret, k8sTypes.NamespacedName(*tls.Secret)) } return &TLSContext{ @@ -880,7 +944,7 @@ func (l4 *L4Filter) getCerts(policyCtx PolicyContext, tls *api.TLSContext, direc // filter is derived from. This filter may be associated with a series of L7 // rules via the `rule` parameter. // Not called with an empty peerEndpoints. -func createL4Filter(policyCtx PolicyContext, peerEndpoints api.EndpointSelectorSlice, auth *api.Authentication, rule api.Ports, port api.PortProtocol, +func createL4Filter(logger *slog.Logger, policyCtx PolicyContext, peerEndpoints api.EndpointSelectorSlice, auth *api.Authentication, rule api.Ports, port api.PortProtocol, protocol api.L4Proto, ruleLabels stringLabels, ingress bool, fqdns api.FQDNSelectorSlice, ) (*L4Filter, error) { selectorCache := policyCtx.GetSelectorCache() @@ -916,12 +980,13 @@ func createL4Filter(policyCtx PolicyContext, peerEndpoints api.EndpointSelectorS l4.cacheFQDNSelectors(fqdns, ruleLabels, selectorCache) } + var l7Parser L7ParserType var terminatingTLS *TLSContext var originatingTLS *TLSContext var rules *api.L7Rules var sni []string listener := "" - var priority uint8 + var priority ListenerPriority pr := rule.GetPortRule() if pr != nil { @@ -930,11 +995,11 @@ func createL4Filter(policyCtx PolicyContext, peerEndpoints api.EndpointSelectorS // Get TLS contexts, if any var err error - terminatingTLS, err = l4.getCerts(policyCtx, pr.TerminatingTLS, TerminatingTLS) + terminatingTLS, err = l4.getCerts(logger, policyCtx, pr.TerminatingTLS, TerminatingTLS) if err != nil { return nil, err } - originatingTLS, err = l4.getCerts(policyCtx, pr.OriginatingTLS, OriginatingTLS) + originatingTLS, err = l4.getCerts(logger, policyCtx, pr.OriginatingTLS, OriginatingTLS) if err != nil { return nil, err } @@ -942,7 +1007,7 @@ func createL4Filter(policyCtx PolicyContext, peerEndpoints api.EndpointSelectorS // Set parser type to TLS, if TLS. This will be overridden by L7 below, if rules // exists. if terminatingTLS != nil || originatingTLS != nil || len(pr.ServerNames) > 0 { - l4.L7Parser = ParserTypeTLS + l7Parser = ParserTypeTLS } // Determine L7ParserType from rules present. Earlier validation ensures rules @@ -950,22 +1015,29 @@ func createL4Filter(policyCtx PolicyContext, peerEndpoints api.EndpointSelectorS if rules != nil { // we need this to redirect DNS UDP (or ANY, which is more useful) if len(rules.DNS) > 0 { - l4.L7Parser = ParserTypeDNS + l7Parser = ParserTypeDNS } else if protocol == api.ProtoTCP { // Other than DNS only support TCP switch { case len(rules.HTTP) > 0: - l4.L7Parser = ParserTypeHTTP + l7Parser = ParserTypeHTTP case len(rules.Kafka) > 0: - l4.L7Parser = ParserTypeKafka + l7Parser = ParserTypeKafka case rules.L7Proto != "": - l4.L7Parser = (L7ParserType)(rules.L7Proto) + l7Parser = (L7ParserType)(rules.L7Proto) } } } - // Override the parser type to CRD is applicable. + // Override the parser type and possibly priority for CRD is applicable. + if pr.Listener != nil { + l7Parser = ParserTypeCRD + } + + // Map parser type to default priority for the given parser type + priority = l7Parser.defaultPriority() + + // Override the parser type and possibly priority for CRD is applicable. if pr.Listener != nil { - l4.L7Parser = ParserTypeCRD ns := policyCtx.GetNamespace() resource := pr.Listener.EnvoyConfig switch resource.Kind { @@ -985,12 +1057,14 @@ func createL4Filter(policyCtx PolicyContext, peerEndpoints api.EndpointSelectorS default: } listener, _ = api.ResourceQualifiedName(ns, resource.Name, pr.Listener.Name, api.ForceNamespace) - priority = pr.Listener.Priority + if pr.Listener.Priority != 0 { + priority = ListenerPriority(pr.Listener.Priority) + } } } - if l4.L7Parser != ParserTypeNone || auth != nil || policyCtx.IsDeny() { - l4.PerSelectorPolicies.addPolicyForSelector(rules, terminatingTLS, originatingTLS, auth, policyCtx.IsDeny(), sni, listener, priority) + if l7Parser != ParserTypeNone || auth != nil || policyCtx.IsDeny() { + l4.PerSelectorPolicies.addPolicyForSelector(l7Parser, rules, terminatingTLS, originatingTLS, auth, policyCtx.IsDeny(), sni, listener, priority) } origin := singleRuleOrigin(ruleLabels) @@ -1022,17 +1096,17 @@ func (l4 *L4Filter) detach(selectorCache *SelectorCache) { // multiple goroutines will be reading the fields from that point on. func (l4 *L4Filter) attach(ctx PolicyContext, l4Policy *L4Policy) policyFeatures { var features policyFeatures - for cs, cp := range l4.PerSelectorPolicies { - if cp != nil { - if cp.isRedirect { + for cs, sp := range l4.PerSelectorPolicies { + if sp != nil { + if sp.L7Parser != "" { features.setFeature(redirectRules) } - if cp.IsDeny { + if sp.IsDeny { features.setFeature(denyRules) } - explicit, authType := getAuthType(cp.Authentication) + explicit, authType := getAuthType(sp.Authentication) if explicit { features.setFeature(authRules) @@ -1050,8 +1124,8 @@ func (l4 *L4Filter) attach(ctx PolicyContext, l4Policy *L4Policy) policyFeatures } // Compute Envoy policies when a policy is ready to be used - if len(cp.L7Rules.HTTP) > 0 { - cp.EnvoyHTTPRules, cp.CanShortCircuit = ctx.GetEnvoyHTTPRules(&cp.L7Rules) + if len(sp.L7Rules.HTTP) > 0 { + sp.EnvoyHTTPRules, sp.CanShortCircuit = ctx.GetEnvoyHTTPRules(&sp.L7Rules) } } } @@ -1067,10 +1141,10 @@ func (l4 *L4Filter) attach(ctx PolicyContext, l4Policy *L4Policy) policyFeatures // // hostWildcardL7 determines if L7 traffic from Host should be // wildcarded (in the relevant daemon mode). -func createL4IngressFilter(policyCtx PolicyContext, fromEndpoints api.EndpointSelectorSlice, auth *api.Authentication, hostWildcardL7 []string, rule api.Ports, port api.PortProtocol, +func createL4IngressFilter(logger *slog.Logger, policyCtx PolicyContext, fromEndpoints api.EndpointSelectorSlice, auth *api.Authentication, hostWildcardL7 []string, rule api.Ports, port api.PortProtocol, protocol api.L4Proto, ruleLabels stringLabels, ) (*L4Filter, error) { - filter, err := createL4Filter(policyCtx, fromEndpoints, auth, rule, port, protocol, ruleLabels, true, nil) + filter, err := createL4Filter(logger, policyCtx, fromEndpoints, auth, rule, port, protocol, ruleLabels, true, nil) if err != nil { return nil, err } @@ -1095,15 +1169,18 @@ func createL4IngressFilter(policyCtx PolicyContext, fromEndpoints api.EndpointSe // specified endpoints and port/protocol for egress traffic, with reference // to the original rules that the filter is derived from. This filter may be // associated with a series of L7 rules via the `rule` parameter. -func createL4EgressFilter(policyCtx PolicyContext, toEndpoints api.EndpointSelectorSlice, auth *api.Authentication, rule api.Ports, port api.PortProtocol, +func createL4EgressFilter(logger *slog.Logger, policyCtx PolicyContext, toEndpoints api.EndpointSelectorSlice, auth *api.Authentication, rule api.Ports, port api.PortProtocol, protocol api.L4Proto, ruleLabels stringLabels, fqdns api.FQDNSelectorSlice, ) (*L4Filter, error) { - return createL4Filter(policyCtx, toEndpoints, auth, rule, port, protocol, ruleLabels, false, fqdns) + return createL4Filter(logger, policyCtx, toEndpoints, auth, rule, port, protocol, ruleLabels, false, fqdns) } // redirectType returns the redirectType for this filter -func (l4 *L4Filter) redirectType() redirectTypes { - switch l4.L7Parser { +func (sp *PerSelectorPolicy) redirectType() redirectTypes { + if sp == nil { + return redirectTypeNone + } + switch sp.L7Parser { case ParserTypeNone: return redirectTypeNone case ParserTypeDNS: @@ -1116,11 +1193,6 @@ func (l4 *L4Filter) redirectType() redirectTypes { } } -// IsRedirect returns true if the L4 filter contains a port redirection -func (l4 *L4Filter) IsRedirect() bool { - return l4.L7Parser != ParserTypeNone -} - // Marshal returns the `L4Filter` in a JSON string. func (l4 *L4Filter) Marshal() string { b, err := json.Marshal(l4) @@ -1514,7 +1586,9 @@ func (l4 *L4DirectionPolicy) attach(ctx PolicyContext, l4Policy *L4Policy) redir var features policyFeatures l4.PortRules.ForEach(func(f *L4Filter) bool { features |= f.attach(ctx, l4Policy) - redirectTypes |= f.redirectType() + for _, sp := range f.PerSelectorPolicies { + redirectTypes |= sp.redirectType() + } return true }) l4.features = features @@ -1649,7 +1723,7 @@ func (l4 *L4Policy) removeUser(user *EndpointPolicy) { // // The caller is responsible for making sure the same identity is not // present in both 'adds' and 'deletes'. -func (l4Policy *L4Policy) AccumulateMapChanges(l4 *L4Filter, cs CachedSelector, adds, deletes []identity.NumericIdentity) { +func (l4Policy *L4Policy) AccumulateMapChanges(logger *slog.Logger, l4 *L4Filter, cs CachedSelector, adds, deletes []identity.NumericIdentity) { port := uint16(l4.Port) proto := l4.U8Proto derivedFrom := l4.RuleOrigin[cs] @@ -1684,15 +1758,16 @@ func (l4Policy *L4Policy) AccumulateMapChanges(l4 *L4Filter, cs CachedSelector, var err error proxyPort, err = epPolicy.LookupRedirectPort(l4.Ingress, string(l4.Protocol), port, listener) if err != nil { - log.WithFields(logrus.Fields{ - logfields.EndpointSelector: cs, - logfields.Port: port, - logfields.Protocol: proto, - logfields.TrafficDirection: direction, - logfields.IsRedirect: redirect, - logfields.Listener: listener, - logfields.ListenerPriority: priority, - }).Warn("AccumulateMapChanges: Missing redirect.") + logger.Warn( + "AccumulateMapChanges: Missing redirect.", + logfields.EndpointSelector, cs, + logfields.Port, port, + logfields.Protocol, proto, + logfields.TrafficDirection, direction, + logfields.IsRedirect, redirect, + logfields.Listener, listener, + logfields.ListenerPriority, priority, + ) continue } } @@ -1708,18 +1783,19 @@ func (l4Policy *L4Policy) AccumulateMapChanges(l4 *L4Filter, cs CachedSelector, if authReq.IsExplicit() { authString = authReq.AuthType().String() } - log.WithFields(logrus.Fields{ - logfields.EndpointSelector: cs, - logfields.AddedPolicyID: adds, - logfields.DeletedPolicyID: deletes, - logfields.Port: port, - logfields.Protocol: proto, - logfields.TrafficDirection: direction, - logfields.IsRedirect: redirect, - logfields.AuthType: authString, - logfields.Listener: listener, - logfields.ListenerPriority: priority, - }).Debug("AccumulateMapChanges") + logger.Debug( + "AccumulateMapChanges", + logfields.EndpointSelector, cs, + logfields.AddedPolicyID, adds, + logfields.DeletedPolicyID, deletes, + logfields.Port, port, + logfields.Protocol, proto, + logfields.TrafficDirection, direction, + logfields.IsRedirect, redirect, + logfields.AuthType, authString, + logfields.Listener, listener, + logfields.ListenerPriority, priority, + ) } epPolicy.policyMapChanges.AccumulateMapChanges(adds, deletes, keysToAdd, value) } @@ -1754,7 +1830,7 @@ func (l4 *L4Policy) detach(selectorCache *SelectorCache, isDelete bool, endpoint if !isDelete { for ePolicy := range l4.users { if endpointID != ePolicy.PolicyOwner.GetID() { - ePolicy.PolicyOwner.RegenerateIfAlive(®eneration.ExternalRegenerationMetadata{ + go ePolicy.PolicyOwner.RegenerateIfAlive(®eneration.ExternalRegenerationMetadata{ Reason: "selector policy has changed because of another endpoint with the same identity", RegenerationLevel: regeneration.RegenerateWithoutDatapath, }) diff --git a/vendor/github.com/cilium/cilium/pkg/policy/lookup.go b/vendor/github.com/cilium/cilium/pkg/policy/lookup.go index 156b45ea77..c04a1672c9 100644 --- a/vendor/github.com/cilium/cilium/pkg/policy/lookup.go +++ b/vendor/github.com/cilium/cilium/pkg/policy/lookup.go @@ -46,7 +46,7 @@ type EndpointInfo struct { // This function is only used for testing, but in multiple packages. // // TODO: add support for redirects -func LookupFlow(repo PolicyRepository, flow Flow, srcEP, dstEP *EndpointInfo) (api.Decision, error) { +func LookupFlow(logger *slog.Logger, repo PolicyRepository, flow Flow, srcEP, dstEP *EndpointInfo) (api.Decision, error) { if flow.From.ID == 0 || flow.To.ID == 0 { return api.Undecided, fmt.Errorf("cannot lookup flow: numeric IDs missing") } @@ -84,9 +84,9 @@ func LookupFlow(repo PolicyRepository, flow Flow, srcEP, dstEP *EndpointInfo) (a return api.Undecided, fmt.Errorf("GetSelectorPolicy(from) failed: %w", err) } - epp := selPolSrc.DistillPolicy(srcEP, nil) + epp := selPolSrc.DistillPolicy(logger, srcEP, nil) epp.Ready() - epp.Detach() + epp.Detach(logger) key := EgressKey().WithIdentity(flow.To.ID).WithPortProto(flow.Proto, flow.Dport) entry, _, _ := epp.Lookup(key) if entry.IsDeny() { @@ -98,9 +98,9 @@ func LookupFlow(repo PolicyRepository, flow Flow, srcEP, dstEP *EndpointInfo) (a if err != nil { return api.Undecided, fmt.Errorf("GetSelectorPolicy(to) failed: %w", err) } - epp = selPolDst.DistillPolicy(dstEP, nil) + epp = selPolDst.DistillPolicy(logger, dstEP, nil) epp.Ready() - epp.Detach() + epp.Detach(logger) key = IngressKey().WithIdentity(flow.From.ID).WithPortProto(flow.Proto, flow.Dport) entry, _, _ = epp.Lookup(key) if entry.IsDeny() { @@ -134,11 +134,11 @@ func (ei *EndpointInfo) GetNamedPort(ingress bool, name string, proto u8proto.U8 func (ei *EndpointInfo) PolicyDebug(fields logrus.Fields, msg string) { if ei.Logger != nil { - args := make([]any, 0, len(fields)*2) + attrs := make([]any, 0, len(fields)*2) for k, v := range fields { - args = append(args, k, v) + attrs = append(attrs, k, v) } - ei.Logger.Debug(msg, args...) + ei.Logger.Debug(msg, attrs...) } } diff --git a/vendor/github.com/cilium/cilium/pkg/policy/mapstate.go b/vendor/github.com/cilium/cilium/pkg/policy/mapstate.go index b0f02436cc..d05a2a870a 100644 --- a/vendor/github.com/cilium/cilium/pkg/policy/mapstate.go +++ b/vendor/github.com/cilium/cilium/pkg/policy/mapstate.go @@ -5,9 +5,9 @@ package policy import ( "iter" + "log/slog" "github.com/hashicorp/go-hclog" - "github.com/sirupsen/logrus" "github.com/cilium/cilium/pkg/container/bitlpm" "github.com/cilium/cilium/pkg/container/versioned" @@ -91,6 +91,7 @@ var ( // greatly enhances the usefuleness of the Trie and improves lookup, // deletion, and insertion times. type mapState struct { + logger *slog.Logger // entries is the map containing the MapStateEntries entries mapStateMap // trie is a Trie that indexes policy Keys without their identity @@ -165,10 +166,11 @@ func (ms *mapState) forKey(k Key, f func(Key, mapStateEntry) bool) bool { if ok { return f(k, e) } - log.WithFields(logrus.Fields{ - logfields.Stacktrace: hclog.Stacktrace(), - logfields.PolicyKey: k, - }).Errorf("Missing MapStateEntry") + ms.logger.Error( + "Missing MapStateEntry", + logfields.Stacktrace, hclog.Stacktrace(), + logfields.PolicyKey, k, + ) return true } @@ -398,7 +400,7 @@ type mapStateEntry struct { } // newMapStateEntry creates a map state entry. -func newMapStateEntry(derivedFrom ruleOrigin, proxyPort uint16, priority uint8, deny bool, authReq AuthRequirement) mapStateEntry { +func newMapStateEntry(derivedFrom ruleOrigin, proxyPort uint16, priority ListenerPriority, deny bool, authReq AuthRequirement) mapStateEntry { return mapStateEntry{ MapStateEntry: types.NewMapStateEntry(deny, proxyPort, priority, authReq), derivedFromRules: derivedFrom, @@ -418,12 +420,13 @@ func NewMapStateEntry(e MapStateEntry) mapStateEntry { } } -func emptyMapState() mapState { - return newMapState(0) +func emptyMapState(logger *slog.Logger) mapState { + return newMapState(logger, 0) } -func newMapState(size int) mapState { +func newMapState(logger *slog.Logger, size int) mapState { return mapState{ + logger: logger, entries: make(mapStateMap, size), trie: bitlpm.NewTrie[types.LPMKey, IDSet](types.MapStatePrefixLen), } @@ -441,10 +444,11 @@ func (ms *mapState) Get(k Key) (MapStateEntry, bool) { // Get the mapStateEntry that matches the Key. func (ms *mapState) get(k Key) (mapStateEntry, bool) { if k.DestPort == 0 && k.PortPrefixLen() > 0 { - log.WithFields(logrus.Fields{ - logfields.Stacktrace: hclog.Stacktrace(), - logfields.PolicyKey: k, - }).Errorf("mapState.Get: invalid port prefix length for wildcard port") + ms.logger.Error( + "mapState.Get: invalid port prefix length for wildcard port", + logfields.Stacktrace, hclog.Stacktrace(), + logfields.PolicyKey, k, + ) } v, ok := ms.entries[k] @@ -454,10 +458,11 @@ func (ms *mapState) get(k Key) (mapStateEntry, bool) { // insert the Key and MapStateEntry into the MapState func (ms *mapState) insert(k Key, v mapStateEntry) { if k.DestPort == 0 && k.PortPrefixLen() > 0 { - log.WithFields(logrus.Fields{ - logfields.Stacktrace: hclog.Stacktrace(), - logfields.PolicyKey: k, - }).Errorf("mapState.insert: invalid port prefix length for wildcard port") + ms.logger.Error( + "mapState.insert: invalid port prefix length for wildcard port", + logfields.Stacktrace, hclog.Stacktrace(), + logfields.PolicyKey, k, + ) } ms.upsert(k, v) } @@ -732,7 +737,7 @@ func (ms *mapState) insertWithChanges(newKey Key, newEntry mapStateEntry, featur // old entry in 'changes'. // Returns 'true' if changes were made. func (ms *mapState) overrideProxyPortForAuth(newEntry mapStateEntry, k Key, v mapStateEntry, changes ChangeState) bool { - if v.AuthRequirement != newEntry.AuthRequirement && v.AuthRequirement.IsExplicit() { + if v.AuthRequirement.IsExplicit() { // Save the old value first changes.insertOldIfNotExists(k, v) @@ -776,9 +781,9 @@ func (ms *mapState) authPreferredInsert(newKey Key, newEntry mapStateEntry, feat return // bail if covered by deny } if v.ProxyPortPriority > newEntry.ProxyPortPriority { - if !newEntryHasExplicitAuth || v.AuthRequirement == newEntry.AuthRequirement { + if !newEntryHasExplicitAuth { // Covering entry has higher proxy port priority and newEntry has a - // default auth type or the same auth requirement => can bail out + // default auth type => can bail out return } @@ -882,6 +887,7 @@ func (ms *mapState) allowAllIdentities(ingress, egress bool) { // granularity of individual mapstate key-value pairs for both adds // and deletes. 'mutex' must be held for any access. type MapChanges struct { + logger *slog.Logger firstVersion versioned.KeepVersion mutex lock.Mutex changes []mapChange @@ -941,14 +947,16 @@ func (mc *MapChanges) SyncMapChanges(txn *versioned.Tx) { mc.synced = append(mc.synced, mc.changes...) mc.version.Close() mc.version = txn.GetVersionHandle() - log.WithFields(logrus.Fields{ - logfields.NewVersion: mc.version, - }).Debug("SyncMapChanges: Got handle on the new version") + mc.logger.Debug( + "SyncMapChanges: Got handle on the new version", + logfields.NewVersion, mc.version, + ) } else { - log.WithFields(logrus.Fields{ - logfields.Version: mc.firstVersion, - logfields.OldVersion: txn, - }).Debug("SyncMapChanges: Discarding already applied changes") + mc.logger.Debug( + "SyncMapChanges: Discarding already applied changes", + logfields.Version, mc.firstVersion, + logfields.OldVersion, txn, + ) } } mc.changes = nil diff --git a/vendor/github.com/cilium/cilium/pkg/policy/policy.go b/vendor/github.com/cilium/cilium/pkg/policy/policy.go index b4ae208e46..c735ad65c4 100644 --- a/vendor/github.com/cilium/cilium/pkg/policy/policy.go +++ b/vendor/github.com/cilium/cilium/pkg/policy/policy.go @@ -30,7 +30,6 @@ func (s *SearchContext) TraceEnabled() bool { // TRACE_ENABLED or TRACE_VERBOSE is enabled in the receiver's SearchContext. func (s *SearchContext) PolicyTrace(format string, a ...interface{}) { if s.TraceEnabled() { - log.Debugf(format, a...) if s.Logging != nil { format = "%-" + s.CallDepth() + "s" + format a = append([]interface{}{""}, a...) @@ -44,7 +43,6 @@ func (s *SearchContext) PolicyTrace(format string, a ...interface{}) { func (s *SearchContext) PolicyTraceVerbose(format string, a ...interface{}) { switch s.Trace { case TRACE_VERBOSE: - log.Debugf(format, a...) if s.Logging != nil { s.Logging.Printf(format, a...) } diff --git a/vendor/github.com/cilium/cilium/pkg/policy/repository.go b/vendor/github.com/cilium/cilium/pkg/policy/repository.go index 8d77907255..fc6b0f32df 100644 --- a/vendor/github.com/cilium/cilium/pkg/policy/repository.go +++ b/vendor/github.com/cilium/cilium/pkg/policy/repository.go @@ -7,7 +7,9 @@ import ( "context" "encoding/json" "fmt" + "log/slog" "maps" + "slices" "sync/atomic" cilium "github.com/cilium/proxy/go/cilium/api" @@ -16,6 +18,7 @@ import ( "github.com/cilium/cilium/api/v1/models" "github.com/cilium/cilium/pkg/container/set" "github.com/cilium/cilium/pkg/crypto/certificatemanager" + envoypolicy "github.com/cilium/cilium/pkg/envoy/policy" "github.com/cilium/cilium/pkg/identity" "github.com/cilium/cilium/pkg/identity/identitymanager" ipcachetypes "github.com/cilium/cilium/pkg/ipcache/types" @@ -126,7 +129,6 @@ type PolicyRepository interface { ReplaceByResource(rules api.Rules, resource ipcachetypes.ResourceID) (affectedIDs *set.Set[identity.NumericIdentity], rev uint64, oldRevCnt int) ReplaceByLabels(rules api.Rules, searchLabelsList []labels.LabelArray) (affectedIDs *set.Set[identity.NumericIdentity], rev uint64, oldRevCnt int) Search(lbls labels.LabelArray) (api.Rules, uint64) - SetEnvoyRulesFunc(f func(certificatemanager.SecretManager, *api.L7Rules, string, string) (*cilium.HttpNetworkPolicyRules, bool)) } type GetPolicyStatistics interface { @@ -137,6 +139,7 @@ type GetPolicyStatistics interface { // Repository is a list of policy rules which in combination form the security // policy. A policy repository can be type Repository struct { + logger *slog.Logger // mutex protects the whole policy tree mutex lock.RWMutex @@ -161,12 +164,14 @@ type Repository struct { // PolicyCache tracks the selector policies created from this repo policyCache *policyCache - certManager certificatemanager.CertificateManager - secretManager certificatemanager.SecretManager + certManager certificatemanager.CertificateManager - getEnvoyHTTPRules func(certificatemanager.SecretManager, *api.L7Rules, string, string) (*cilium.HttpNetworkPolicyRules, bool) + metricsManager api.PolicyMetrics + l7RulesTranslator envoypolicy.EnvoyL7RulesTranslator +} - metricsManager api.PolicyMetrics +func (p *Repository) GetEnvoyHTTPRules(l7Rules *api.L7Rules, ns string) (*cilium.HttpNetworkPolicyRules, bool) { + return p.l7RulesTranslator.GetEnvoyHTTPRules(l7Rules, ns) } // GetSelectorCache() returns the selector cache used by the Repository @@ -179,34 +184,25 @@ func (p *Repository) GetAuthTypes(localID, remoteID identity.NumericIdentity) Au return p.policyCache.getAuthTypes(localID, remoteID) } -func (p *Repository) SetEnvoyRulesFunc(f func(certificatemanager.SecretManager, *api.L7Rules, string, string) (*cilium.HttpNetworkPolicyRules, bool)) { - p.getEnvoyHTTPRules = f -} - -func (p *Repository) GetEnvoyHTTPRules(l7Rules *api.L7Rules, ns string) (*cilium.HttpNetworkPolicyRules, bool) { - if p.getEnvoyHTTPRules == nil { - return nil, true - } - return p.getEnvoyHTTPRules(p.secretManager, l7Rules, ns, p.secretManager.GetSecretSyncNamespace()) -} - // NewPolicyRepository creates a new policy repository. func NewPolicyRepository( + logger *slog.Logger, initialIDs identity.IdentityMap, certManager certificatemanager.CertificateManager, - secretManager certificatemanager.SecretManager, + l7RulesTranslator envoypolicy.EnvoyL7RulesTranslator, idmgr identitymanager.IDManager, metricsManager api.PolicyMetrics, ) *Repository { - selectorCache := NewSelectorCache(initialIDs) + selectorCache := NewSelectorCache(logger, initialIDs) repo := &Repository{ - rules: make(map[ruleKey]*rule), - rulesByNamespace: make(map[string]sets.Set[ruleKey]), - rulesByResource: make(map[ipcachetypes.ResourceID]map[ruleKey]*rule), - selectorCache: selectorCache, - certManager: certManager, - secretManager: secretManager, - metricsManager: metricsManager, + logger: logger, + rules: make(map[ruleKey]*rule), + rulesByNamespace: make(map[string]sets.Set[ruleKey]), + rulesByResource: make(map[ipcachetypes.ResourceID]map[ruleKey]*rule), + selectorCache: selectorCache, + certManager: certManager, + metricsManager: metricsManager, + l7RulesTranslator: l7RulesTranslator, } repo.revision.Store(1) repo.policyCache = newPolicyCache(repo, idmgr) @@ -447,7 +443,7 @@ func (p *Repository) resolvePolicyLocked(securityIdentity *identity.Identity) (* } if ingressEnabled { - newL4IngressPolicy, err := matchingRules.resolveL4IngressPolicy(&policyCtx, &ingressCtx) + newL4IngressPolicy, err := matchingRules.resolveL4IngressPolicy(p.logger, &policyCtx, &ingressCtx) if err != nil { return nil, err } @@ -455,7 +451,7 @@ func (p *Repository) resolvePolicyLocked(securityIdentity *identity.Identity) (* } if egressEnabled { - newL4EgressPolicy, err := matchingRules.resolveL4EgressPolicy(&policyCtx, &egressCtx) + newL4EgressPolicy, err := matchingRules.resolveL4EgressPolicy(p.logger, &policyCtx, &egressCtx) if err != nil { return nil, err } @@ -562,13 +558,13 @@ func (p *Repository) computePolicyEnforcementAndRules(securityIdentity *identity // If there only ingress default-allow rules, then insert a wildcard rule if !hasIngressDefaultDeny && ingress { - log.WithField(logfields.Identity, securityIdentity).Debug("Only default-allow policies, synthesizing ingress wildcard-allow rule") + p.logger.Debug("Only default-allow policies, synthesizing ingress wildcard-allow rule", logfields.Identity, securityIdentity) matchingRules = append(matchingRules, wildcardRule(securityIdentity.LabelArray, true /*ingress*/)) } // Same for egress -- synthesize a wildcard rule if !hasEgressDefaultDeny && egress { - log.WithField(logfields.Identity, securityIdentity).Debug("Only default-allow policies, synthesizing egress wildcard-allow rule") + p.logger.Debug("Only default-allow policies, synthesizing egress wildcard-allow rule", logfields.Identity, securityIdentity) matchingRules = append(matchingRules, wildcardRule(securityIdentity.LabelArray, false /*egress*/)) } @@ -647,7 +643,7 @@ func (p *Repository) ReplaceByResource(rules api.Rules, resource ipcachetypes.Re if len(resource) == 0 { // This should never ever be hit, as the caller should have already validated the resource. // Out of paranoia, do nothing. - log.Error("Attempt to replace rules by resource with an empty resource.") + p.logger.Error("Attempt to replace rules by resource with an empty resource.") return } @@ -697,12 +693,9 @@ func (p *Repository) ReplaceByLabels(rules api.Rules, searchLabelsList []labels. // determine outgoing rules for ruleKey, rule := range p.rules { - for _, searchLabels := range searchLabelsList { - if rule.Labels.Contains(searchLabels) { - p.del(ruleKey) - oldRules = append(oldRules, rule) - break - } + if slices.ContainsFunc(searchLabelsList, rule.Labels.Contains) { + p.del(ruleKey) + oldRules = append(oldRules, rule) } } diff --git a/vendor/github.com/cilium/cilium/pkg/policy/resolve.go b/vendor/github.com/cilium/cilium/pkg/policy/resolve.go index f0fcc7337a..8d5424c973 100644 --- a/vendor/github.com/cilium/cilium/pkg/policy/resolve.go +++ b/vendor/github.com/cilium/cilium/pkg/policy/resolve.go @@ -7,6 +7,7 @@ import ( "errors" "fmt" "iter" + "log/slog" "runtime" "github.com/sirupsen/logrus" @@ -28,7 +29,7 @@ type SelectorPolicy interface { // DistillPolicy returns the policy in terms of connectivity to peer // Identities. - DistillPolicy(owner PolicyOwner, redirects map[string]uint16) *EndpointPolicy + DistillPolicy(logger *slog.Logger, owner PolicyOwner, redirects map[string]uint16) *EndpointPolicy } // selectorPolicy is a structure which contains the resolved policy for a @@ -166,7 +167,7 @@ func (p *selectorPolicy) detach(isDelete bool, endpointID uint64) { // Called without holding the Selector cache or Repository locks. // PolicyOwner (aka Endpoint) is also unlocked during this call, // but the Endpoint's build mutex is held. -func (p *selectorPolicy) DistillPolicy(policyOwner PolicyOwner, redirects map[string]uint16) *EndpointPolicy { +func (p *selectorPolicy) DistillPolicy(logger *slog.Logger, policyOwner PolicyOwner, redirects map[string]uint16) *EndpointPolicy { var calculatedPolicy *EndpointPolicy // EndpointPolicy is initialized while 'GetCurrentVersionHandleFunc' keeps the selector @@ -185,8 +186,9 @@ func (p *selectorPolicy) DistillPolicy(policyOwner PolicyOwner, redirects map[st calculatedPolicy = &EndpointPolicy{ selectorPolicy: p, VersionHandle: version, - policyMapState: newMapState(policyOwner.MapStateSize()), + policyMapState: newMapState(logger, policyOwner.MapStateSize()), policyMapChanges: MapChanges{ + logger: logger, firstVersion: version.Version(), }, PolicyOwner: policyOwner, @@ -205,7 +207,7 @@ func (p *selectorPolicy) DistillPolicy(policyOwner PolicyOwner, redirects map[st // Must come after the 'insertUser()' above to guarantee // PolicyMapChanges will contain all changes that are applied // after the computation of PolicyMapState has started. - calculatedPolicy.toMapState() + calculatedPolicy.toMapState(logger) if !policyOwner.IsHost() { calculatedPolicy.policyMapState.determineAllowLocalhostIngress() } @@ -225,13 +227,17 @@ func (p *EndpointPolicy) Ready() (err error) { // Detach removes EndpointPolicy references from selectorPolicy // to allow the EndpointPolicy to be GC'd. // PolicyOwner (aka Endpoint) is also locked during this call. -func (p *EndpointPolicy) Detach() { +func (p *EndpointPolicy) Detach(logger *slog.Logger) { p.selectorPolicy.removeUser(p) // in case the call was missed previouly if p.Ready() == nil { // succeeded, so it was missed previously _, file, line, _ := runtime.Caller(1) - log.Warningf("Detach: EndpointPolicy was not marked as Ready (%s:%d)", file, line) + logger.Warn( + "Detach: EndpointPolicy was not marked as Ready", + logfields.File, file, + logfields.Line, line, + ) } // Also release the version handle held for incremental updates, if any. // This must be done after the removeUser() call above, so that we do not get a new version @@ -356,9 +362,9 @@ func (p *EndpointPolicy) RevertChanges(changes ChangeState) { // Called without holding the Repository lock. // PolicyOwner (aka Endpoint) is also unlocked during this call, // but the Endpoint's build mutex is held. -func (p *EndpointPolicy) toMapState() { - p.L4Policy.Ingress.toMapState(p) - p.L4Policy.Egress.toMapState(p) +func (p *EndpointPolicy) toMapState(logger *slog.Logger) { + p.L4Policy.Ingress.toMapState(logger, p) + p.L4Policy.Egress.toMapState(logger, p) } // toMapState transforms the L4DirectionPolicy into @@ -367,9 +373,9 @@ func (p *EndpointPolicy) toMapState() { // Called without holding the Repository lock. // PolicyOwner (aka Endpoint) is also unlocked during this call, // but the Endpoint's build mutex is held. -func (l4policy L4DirectionPolicy) toMapState(p *EndpointPolicy) { +func (l4policy L4DirectionPolicy) toMapState(logger *slog.Logger, p *EndpointPolicy) { l4policy.PortRules.ForEach(func(l4 *L4Filter) bool { - l4.toMapState(p, l4policy.features, ChangeState{}) + l4.toMapState(logger, p, l4policy.features, ChangeState{}) return true }) } @@ -386,11 +392,9 @@ func (p *selectorPolicy) RedirectFilters() iter.Seq2[*L4Filter, *PerSelectorPoli func (l4policy L4DirectionPolicy) forEachRedirectFilter(yield func(*L4Filter, *PerSelectorPolicy) bool) bool { ok := true l4policy.PortRules.ForEach(func(l4 *L4Filter) bool { - if l4.IsRedirect() { - for _, ps := range l4.PerSelectorPolicies { - if ps != nil && ps.IsRedirect() { - ok = yield(l4, ps) - } + for _, ps := range l4.PerSelectorPolicies { + if ps != nil && ps.IsRedirect() { + ok = yield(l4, ps) } } return ok @@ -432,9 +436,9 @@ func (p *EndpointPolicy) ConsumeMapChanges() (closer func(), changes ChangeState } // NewEndpointPolicy returns an empty EndpointPolicy stub. -func NewEndpointPolicy(repo PolicyRepository) *EndpointPolicy { +func NewEndpointPolicy(logger *slog.Logger, repo PolicyRepository) *EndpointPolicy { return &EndpointPolicy{ selectorPolicy: newSelectorPolicy(repo.GetSelectorCache()), - policyMapState: emptyMapState(), + policyMapState: emptyMapState(logger), } } diff --git a/vendor/github.com/cilium/cilium/pkg/policy/rule.go b/vendor/github.com/cilium/cilium/pkg/policy/rule.go index aa8e024f40..a11a0d4058 100644 --- a/vendor/github.com/cilium/cilium/pkg/policy/rule.go +++ b/vendor/github.com/cilium/cilium/pkg/policy/rule.go @@ -5,6 +5,7 @@ package policy import ( "fmt" + "log/slog" "strconv" "strings" @@ -40,10 +41,10 @@ type rule struct { // IdentitySelectionUpdated is called by the SelectorCache when a new identity is added; // We can ignore it because the endpoint will be regenerated by the nature of // identities being updated. -func (r *rule) IdentitySelectionUpdated(_ policytypes.CachedSelector, _, _ []identity.NumericIdentity) { +func (r *rule) IdentitySelectionUpdated(logger *slog.Logger, selector policytypes.CachedSelector, added, deleted []identity.NumericIdentity) { } -func (d *rule) IdentitySelectionCommit(*versioned.Tx) { +func (d *rule) IdentitySelectionCommit(*slog.Logger, *versioned.Tx) { } func (r *rule) IsPeerSelector() bool { @@ -107,7 +108,7 @@ func (epd *PerSelectorPolicy) appendL7WildcardRule(ctx *SearchContext) api.L7Rul // takesListenerPrecedenceOver returns true if the listener reference in 'l7Rules' takes precedence // over the listener reference in 'other'. func (l7Rules *PerSelectorPolicy) takesListenerPrecedenceOver(other *PerSelectorPolicy) bool { - var priority, otherPriority uint8 + var priority, otherPriority ListenerPriority // decrement by one to wrap the undefined value (0) to be the highest numerical // value of the uint16, which is the lowest possible priority @@ -117,9 +118,21 @@ func (l7Rules *PerSelectorPolicy) takesListenerPrecedenceOver(other *PerSelector return priority < otherPriority } -// mergeListenerReference merges listener reference from 'newL7Rules' to 'l7Rules', giving +// mergeRedirect merges listener reference from 'newL7Rules' to 'l7Rules', giving // precedence to listener with the lowest priority, if any. -func (l7Rules *PerSelectorPolicy) mergeListenerReference(newL7Rules *PerSelectorPolicy) error { +func (l7Rules *PerSelectorPolicy) mergeRedirect(newL7Rules *PerSelectorPolicy) error { + // Merge L7ParserType, if possible + l7Parser, err := l7Rules.L7Parser.Merge(newL7Rules.L7Parser) + if err != nil { + return err + } + + if l7Parser != l7Rules.L7Parser { + // Also copy over the listener priority + l7Rules.L7Parser = l7Parser + l7Rules.Priority = newL7Rules.Priority + } + // Nothing to do if 'newL7Rules' has no listener reference if newL7Rules.Listener == "" { return nil @@ -146,15 +159,9 @@ func (l7Rules *PerSelectorPolicy) mergeListenerReference(newL7Rules *PerSelector return fmt.Errorf("cannot merge conflicting CiliumEnvoyConfig Listeners (%v/%v) with the same priority (%d)", newL7Rules.Listener, l7Rules.Listener, l7Rules.Priority) } +// mergePortProto merges the L7-related data from the filter to merge +// with the L7-related data already in the existing filter. func mergePortProto(ctx *SearchContext, existingFilter, filterToMerge *L4Filter, selectorCache *SelectorCache) (err error) { - // Merge the L7-related data from the filter to merge - // with the L7-related data already in the existing filter. - existingFilter.L7Parser, err = existingFilter.L7Parser.Merge(filterToMerge.L7Parser) - if err != nil { - ctx.PolicyTrace(" Merge conflict: mismatching parsers %s/%s\n", filterToMerge.L7Parser, existingFilter.L7Parser) - return err - } - for cs, newL7Rules := range filterToMerge.PerSelectorPolicies { // 'cs' will be merged or moved (see below), either way it needs // to be removed from the map it is in now. @@ -197,11 +204,8 @@ func mergePortProto(ctx *SearchContext, existingFilter, filterToMerge *L4Filter, newL7Rules = &PerSelectorPolicy{} } - // Merge isRedirect flag - l7Rules.isRedirect = l7Rules.isRedirect || newL7Rules.isRedirect - - // Merge listener reference - if err := l7Rules.mergeListenerReference(newL7Rules); err != nil { + // Merge Redirect + if err := l7Rules.mergeRedirect(newL7Rules); err != nil { ctx.PolicyTrace(" Merge conflict: %s\n", err.Error()) return err } @@ -332,10 +336,10 @@ func mergePortProto(ctx *SearchContext, existingFilter, filterToMerge *L4Filter, // wildcards via 'hostWildcardL7'. That is to say, traffic will be // forwarded to the proxy for endpoints matching those labels, but the proxy // will allow all such traffic. -func mergeIngressPortProto(policyCtx PolicyContext, ctx *SearchContext, endpoints api.EndpointSelectorSlice, auth *api.Authentication, hostWildcardL7 []string, +func mergeIngressPortProto(logger *slog.Logger, policyCtx PolicyContext, ctx *SearchContext, endpoints api.EndpointSelectorSlice, auth *api.Authentication, hostWildcardL7 []string, r api.Ports, p api.PortProtocol, proto api.L4Proto, ruleLabels stringLabels, resMap L4PolicyMap) (int, error) { // Create a new L4Filter - filterToMerge, err := createL4IngressFilter(policyCtx, endpoints, auth, hostWildcardL7, r, p, proto, ruleLabels) + filterToMerge, err := createL4IngressFilter(logger, policyCtx, endpoints, auth, hostWildcardL7, r, p, proto, ruleLabels) if err != nil { return 0, err } @@ -400,7 +404,7 @@ func rulePortsCoverSearchContext(ports []api.PortProtocol, ctx *SearchContext) b return false } -func mergeIngress(policyCtx PolicyContext, ctx *SearchContext, fromEndpoints api.EndpointSelectorSlice, auth *api.Authentication, toPorts, icmp api.PortsIterator, ruleLabels stringLabels, resMap L4PolicyMap) (int, error) { +func mergeIngress(logger *slog.Logger, policyCtx PolicyContext, ctx *SearchContext, fromEndpoints api.EndpointSelectorSlice, auth *api.Authentication, toPorts, icmp api.PortsIterator, ruleLabels stringLabels, resMap L4PolicyMap) (int, error) { found := 0 // short-circuit if no endpoint is selected @@ -436,7 +440,7 @@ func mergeIngress(policyCtx PolicyContext, ctx *SearchContext, fromEndpoints api // L3-only rule (with requirements folded into fromEndpoints). if toPorts.Len() == 0 && icmp.Len() == 0 && len(fromEndpoints) > 0 { - cnt, err = mergeIngressPortProto(policyCtx, ctx, fromEndpoints, auth, hostWildcardL7, &api.PortRule{}, api.PortProtocol{Port: "0", Protocol: api.ProtoAny}, api.ProtoAny, ruleLabels, resMap) + cnt, err = mergeIngressPortProto(logger, policyCtx, ctx, fromEndpoints, auth, hostWildcardL7, &api.PortRule{}, api.PortProtocol{Port: "0", Protocol: api.ProtoAny}, api.ProtoAny, ruleLabels, resMap) if err != nil { return found, err } @@ -480,25 +484,25 @@ func mergeIngress(policyCtx PolicyContext, ctx *SearchContext, fromEndpoints api for _, p := range r.GetPortProtocols() { if p.Protocol.IsAny() { - cnt, err := mergeIngressPortProto(policyCtx, ctx, fromEndpoints, auth, hostWildcardL7, r, p, api.ProtoTCP, ruleLabels, resMap) + cnt, err := mergeIngressPortProto(logger, policyCtx, ctx, fromEndpoints, auth, hostWildcardL7, r, p, api.ProtoTCP, ruleLabels, resMap) if err != nil { return err } found += cnt - cnt, err = mergeIngressPortProto(policyCtx, ctx, fromEndpoints, auth, hostWildcardL7, r, p, api.ProtoUDP, ruleLabels, resMap) + cnt, err = mergeIngressPortProto(logger, policyCtx, ctx, fromEndpoints, auth, hostWildcardL7, r, p, api.ProtoUDP, ruleLabels, resMap) if err != nil { return err } found += cnt - cnt, err = mergeIngressPortProto(policyCtx, ctx, fromEndpoints, auth, hostWildcardL7, r, p, api.ProtoSCTP, ruleLabels, resMap) + cnt, err = mergeIngressPortProto(logger, policyCtx, ctx, fromEndpoints, auth, hostWildcardL7, r, p, api.ProtoSCTP, ruleLabels, resMap) if err != nil { return err } found += cnt } else { - cnt, err := mergeIngressPortProto(policyCtx, ctx, fromEndpoints, auth, hostWildcardL7, r, p, p.Protocol, ruleLabels, resMap) + cnt, err := mergeIngressPortProto(logger, policyCtx, ctx, fromEndpoints, auth, hostWildcardL7, r, p, p.Protocol, ruleLabels, resMap) if err != nil { return err } @@ -527,7 +531,7 @@ func mergeIngress(policyCtx PolicyContext, ctx *SearchContext, fromEndpoints api } for _, p := range r.GetPortProtocols() { - cnt, err := mergeIngressPortProto(policyCtx, ctx, fromEndpoints, auth, hostWildcardL7, r, p, p.Protocol, ruleLabels, resMap) + cnt, err := mergeIngressPortProto(logger, policyCtx, ctx, fromEndpoints, auth, hostWildcardL7, r, p, p.Protocol, ruleLabels, resMap) if err != nil { return err } @@ -555,6 +559,7 @@ func (state *traceState) unSelectRule(ctx *SearchContext, labels labels.LabelArr // These requirements are dynamically inserted into a copy of the receiver rule, // as requirements form conjunctions across all rules. func (r *rule) resolveIngressPolicy( + logger *slog.Logger, policyCtx PolicyContext, ctx *SearchContext, state *traceState, @@ -578,7 +583,7 @@ func (r *rule) resolveIngressPolicy( } for _, ingressRule := range r.Ingress { fromEndpoints := ingressRule.GetSourceEndpointSelectorsWithRequirements(requirements) - cnt, err := mergeIngress(policyCtx, ctx, fromEndpoints, ingressRule.Authentication, ingressRule.ToPorts, ingressRule.ICMPs, makeStringLabels(r.Rule.Labels), result) + cnt, err := mergeIngress(logger, policyCtx, ctx, fromEndpoints, ingressRule.Authentication, ingressRule.ToPorts, ingressRule.ICMPs, makeStringLabels(r.Rule.Labels), result) if err != nil { return nil, err } @@ -593,7 +598,7 @@ func (r *rule) resolveIngressPolicy( }() for _, ingressRule := range r.IngressDeny { fromEndpoints := ingressRule.GetSourceEndpointSelectorsWithRequirements(requirementsDeny) - cnt, err := mergeIngress(policyCtx, ctx, fromEndpoints, nil, ingressRule.ToPorts, ingressRule.ICMPs, makeStringLabels(r.Rule.Labels), result) + cnt, err := mergeIngress(logger, policyCtx, ctx, fromEndpoints, nil, ingressRule.ToPorts, ingressRule.ICMPs, makeStringLabels(r.Rule.Labels), result) if err != nil { return nil, err } @@ -643,7 +648,7 @@ func (r *rule) getSubjects() []identity.NumericIdentity { // ****************** EGRESS POLICY ****************** -func mergeEgress(policyCtx PolicyContext, ctx *SearchContext, toEndpoints api.EndpointSelectorSlice, auth *api.Authentication, toPorts, icmp api.PortsIterator, ruleLabels stringLabels, resMap L4PolicyMap, fqdns api.FQDNSelectorSlice) (int, error) { +func mergeEgress(logger *slog.Logger, policyCtx PolicyContext, ctx *SearchContext, toEndpoints api.EndpointSelectorSlice, auth *api.Authentication, toPorts, icmp api.PortsIterator, ruleLabels stringLabels, resMap L4PolicyMap, fqdns api.FQDNSelectorSlice) (int, error) { found := 0 // short-circuit if no endpoint is selected @@ -669,7 +674,7 @@ func mergeEgress(policyCtx PolicyContext, ctx *SearchContext, toEndpoints api.En // L3-only rule (with requirements folded into toEndpoints). if toPorts.Len() == 0 && icmp.Len() == 0 && len(toEndpoints) > 0 { - cnt, err = mergeEgressPortProto(policyCtx, ctx, toEndpoints, auth, &api.PortRule{}, api.PortProtocol{Port: "0", Protocol: api.ProtoAny}, api.ProtoAny, ruleLabels, resMap, fqdns) + cnt, err = mergeEgressPortProto(logger, policyCtx, ctx, toEndpoints, auth, &api.PortRule{}, api.PortProtocol{Port: "0", Protocol: api.ProtoAny}, api.ProtoAny, ruleLabels, resMap, fqdns) if err != nil { return found, err } @@ -707,25 +712,25 @@ func mergeEgress(policyCtx PolicyContext, ctx *SearchContext, toEndpoints api.En for _, p := range r.GetPortProtocols() { if p.Protocol.IsAny() { - cnt, err := mergeEgressPortProto(policyCtx, ctx, toEndpoints, auth, r, p, api.ProtoTCP, ruleLabels, resMap, fqdns) + cnt, err := mergeEgressPortProto(logger, policyCtx, ctx, toEndpoints, auth, r, p, api.ProtoTCP, ruleLabels, resMap, fqdns) if err != nil { return err } found += cnt - cnt, err = mergeEgressPortProto(policyCtx, ctx, toEndpoints, auth, r, p, api.ProtoUDP, ruleLabels, resMap, fqdns) + cnt, err = mergeEgressPortProto(logger, policyCtx, ctx, toEndpoints, auth, r, p, api.ProtoUDP, ruleLabels, resMap, fqdns) if err != nil { return err } found += cnt - cnt, err = mergeEgressPortProto(policyCtx, ctx, toEndpoints, auth, r, p, api.ProtoSCTP, ruleLabels, resMap, fqdns) + cnt, err = mergeEgressPortProto(logger, policyCtx, ctx, toEndpoints, auth, r, p, api.ProtoSCTP, ruleLabels, resMap, fqdns) if err != nil { return err } found += cnt } else { - cnt, err := mergeEgressPortProto(policyCtx, ctx, toEndpoints, auth, r, p, p.Protocol, ruleLabels, resMap, fqdns) + cnt, err := mergeEgressPortProto(logger, policyCtx, ctx, toEndpoints, auth, r, p, p.Protocol, ruleLabels, resMap, fqdns) if err != nil { return err } @@ -750,7 +755,7 @@ func mergeEgress(policyCtx PolicyContext, ctx *SearchContext, toEndpoints api.En } for _, p := range r.GetPortProtocols() { - cnt, err := mergeEgressPortProto(policyCtx, ctx, toEndpoints, auth, r, p, p.Protocol, ruleLabels, resMap, fqdns) + cnt, err := mergeEgressPortProto(logger, policyCtx, ctx, toEndpoints, auth, r, p, p.Protocol, ruleLabels, resMap, fqdns) if err != nil { return err } @@ -767,10 +772,10 @@ func mergeEgress(policyCtx PolicyContext, ctx *SearchContext, toEndpoints api.En // port and protocol with the contents of the provided PortRule. If the rule // being merged has conflicting L7 rules with those already in the provided // L4PolicyMap for the specified port-protocol tuple, it returns an error. -func mergeEgressPortProto(policyCtx PolicyContext, ctx *SearchContext, endpoints api.EndpointSelectorSlice, auth *api.Authentication, r api.Ports, p api.PortProtocol, +func mergeEgressPortProto(logger *slog.Logger, policyCtx PolicyContext, ctx *SearchContext, endpoints api.EndpointSelectorSlice, auth *api.Authentication, r api.Ports, p api.PortProtocol, proto api.L4Proto, ruleLabels stringLabels, resMap L4PolicyMap, fqdns api.FQDNSelectorSlice) (int, error) { // Create a new L4Filter - filterToMerge, err := createL4EgressFilter(policyCtx, endpoints, auth, r, p, proto, ruleLabels, fqdns) + filterToMerge, err := createL4EgressFilter(logger, policyCtx, endpoints, auth, r, p, proto, ruleLabels, fqdns) if err != nil { return 0, err } @@ -783,6 +788,7 @@ func mergeEgressPortProto(policyCtx PolicyContext, ctx *SearchContext, endpoints } func (r *rule) resolveEgressPolicy( + logger *slog.Logger, policyCtx PolicyContext, ctx *SearchContext, state *traceState, @@ -806,7 +812,7 @@ func (r *rule) resolveEgressPolicy( } for _, egressRule := range r.Egress { toEndpoints := egressRule.GetDestinationEndpointSelectorsWithRequirements(requirements) - cnt, err := mergeEgress(policyCtx, ctx, toEndpoints, egressRule.Authentication, egressRule.ToPorts, egressRule.ICMPs, makeStringLabels(r.Rule.Labels), result, egressRule.ToFQDNs) + cnt, err := mergeEgress(logger, policyCtx, ctx, toEndpoints, egressRule.Authentication, egressRule.ToPorts, egressRule.ICMPs, makeStringLabels(r.Rule.Labels), result, egressRule.ToFQDNs) if err != nil { return nil, err } @@ -821,7 +827,7 @@ func (r *rule) resolveEgressPolicy( }() for _, egressRule := range r.EgressDeny { toEndpoints := egressRule.GetDestinationEndpointSelectorsWithRequirements(requirementsDeny) - cnt, err := mergeEgress(policyCtx, ctx, toEndpoints, nil, egressRule.ToPorts, egressRule.ICMPs, makeStringLabels(r.Rule.Labels), result, nil) + cnt, err := mergeEgress(logger, policyCtx, ctx, toEndpoints, nil, egressRule.ToPorts, egressRule.ICMPs, makeStringLabels(r.Rule.Labels), result, nil) if err != nil { return nil, err } diff --git a/vendor/github.com/cilium/cilium/pkg/policy/rules.go b/vendor/github.com/cilium/cilium/pkg/policy/rules.go index bc240a2283..27b17d63e7 100644 --- a/vendor/github.com/cilium/cilium/pkg/policy/rules.go +++ b/vendor/github.com/cilium/cilium/pkg/policy/rules.go @@ -4,6 +4,8 @@ package policy import ( + "log/slog" + slim_metav1 "github.com/cilium/cilium/pkg/k8s/slim/k8s/apis/meta/v1" policyapi "github.com/cilium/cilium/pkg/policy/api" ) @@ -12,7 +14,7 @@ import ( // to be written with []*rule as a receiver. type ruleSlice []*rule -func (rules ruleSlice) resolveL4IngressPolicy(policyCtx PolicyContext, ctx *SearchContext) (L4PolicyMap, error) { +func (rules ruleSlice) resolveL4IngressPolicy(logger *slog.Logger, policyCtx PolicyContext, ctx *SearchContext) (L4PolicyMap, error) { result := NewL4PolicyMap() ctx.PolicyTrace("\n") @@ -47,7 +49,7 @@ func (rules ruleSlice) resolveL4IngressPolicy(policyCtx PolicyContext, ctx *Sear ctx.rulesSelect = true for _, r := range matchedRules { - _, err := r.resolveIngressPolicy(policyCtx, ctx, &state, result, requirements, requirementsDeny) + _, err := r.resolveIngressPolicy(logger, policyCtx, ctx, &state, result, requirements, requirementsDeny) if err != nil { return nil, err } @@ -62,7 +64,7 @@ func (rules ruleSlice) resolveL4IngressPolicy(policyCtx PolicyContext, ctx *Sear return result, nil } -func (rules ruleSlice) resolveL4EgressPolicy(policyCtx PolicyContext, ctx *SearchContext) (L4PolicyMap, error) { +func (rules ruleSlice) resolveL4EgressPolicy(logger *slog.Logger, policyCtx PolicyContext, ctx *SearchContext) (L4PolicyMap, error) { result := NewL4PolicyMap() ctx.PolicyTrace("\n") @@ -98,7 +100,7 @@ func (rules ruleSlice) resolveL4EgressPolicy(policyCtx PolicyContext, ctx *Searc for i, r := range matchedRules { state.ruleID = i - _, err := r.resolveEgressPolicy(policyCtx, ctx, &state, result, requirements, requirementsDeny) + _, err := r.resolveEgressPolicy(logger, policyCtx, ctx, &state, result, requirements, requirementsDeny) if err != nil { return nil, err } diff --git a/vendor/github.com/cilium/cilium/pkg/policy/selectorcache.go b/vendor/github.com/cilium/cilium/pkg/policy/selectorcache.go index 71e58a1f88..53fafb5d0a 100644 --- a/vendor/github.com/cilium/cilium/pkg/policy/selectorcache.go +++ b/vendor/github.com/cilium/cilium/pkg/policy/selectorcache.go @@ -4,10 +4,9 @@ package policy import ( + "log/slog" "sync" - "github.com/sirupsen/logrus" - "github.com/cilium/cilium/api/v1/models" "github.com/cilium/cilium/pkg/container/versioned" "github.com/cilium/cilium/pkg/identity" @@ -54,6 +53,8 @@ type userNotification struct { // SelectorCache caches identities, identity selectors, and the // subsets of identities each selector selects. type SelectorCache struct { + logger *slog.Logger + versioned *versioned.Coordinator mutex lock.RWMutex @@ -191,9 +192,9 @@ func (sc *SelectorCache) handleUserNotifications() { for _, n := range notifications { if n.selector == nil { - n.user.IdentitySelectionCommit(n.txn) + n.user.IdentitySelectionCommit(sc.logger, n.txn) } else { - n.user.IdentitySelectionUpdated(n.selector, n.added, n.deleted) + n.user.IdentitySelectionUpdated(sc.logger, n.selector, n.added, n.deleted) } n.wg.Done() } @@ -239,15 +240,16 @@ func (sc *SelectorCache) queueNotifiedUsersCommit(txn *versioned.Tx, wg *sync.Wa } // NewSelectorCache creates a new SelectorCache with the given identities. -func NewSelectorCache(ids identity.IdentityMap) *SelectorCache { +func NewSelectorCache(logger *slog.Logger, ids identity.IdentityMap) *SelectorCache { sc := &SelectorCache{ + logger: logger, idCache: make(map[identity.NumericIdentity]scIdentity, len(ids)), selectors: make(map[string]*identitySelector), } sc.userCond = sync.NewCond(&sc.userMutex) sc.versioned = &versioned.Coordinator{ Cleaner: sc.oldVersionCleaner, - Logger: log, + Logger: logger, } for nid, lbls := range ids { @@ -259,14 +261,17 @@ func NewSelectorCache(ids identity.IdentityMap) *SelectorCache { func (sc *SelectorCache) RegisterMetrics() { if err := metrics.Register(newSelectorCacheMetrics(sc)); err != nil { - log.WithError(err).Warning("Selector cache metrics registration failed. No metrics will be reported.") + sc.logger.Warn("Selector cache metrics registration failed. No metrics will be reported.", logfields.Error, err) } } // oldVersionCleaner is called from a goroutine without holding any locks func (sc *SelectorCache) oldVersionCleaner(keepVersion versioned.KeepVersion) { // Log before taking the lock so that if we ever have a deadlock here this log line will be seen - log.WithField(logfields.Version, keepVersion).Debug("Cleaning old selector and identity versions") + sc.logger.Debug( + "Cleaning old selector and identity versions", + logfields.Version, keepVersion, + ) // This is called when some versions are no longer needed, from wherever // VersionHandle's may be kept, so we must take the lock to safely access @@ -347,6 +352,7 @@ func (sc *SelectorCache) AddFQDNSelector(user CachedSelectionUser, lbls stringLa // must hold lock for writing func (sc *SelectorCache) addSelectorLocked(user CachedSelectionUser, lbls stringLabels, key string, source selectorSource) (types.CachedSelector, bool) { idSel := &identitySelector{ + logger: sc.logger, key: key, users: make(map[CachedSelectionUser]struct{}), cachedSelections: make(map[identity.NumericIdentity]struct{}), @@ -515,17 +521,19 @@ func (sc *SelectorCache) UpdateIdentities(added, deleted identity.IdentityMap, w // prepopulated with all matching numeric identities. for numericID := range deleted { if old, exists := sc.idCache[numericID]; exists { - log.WithFields(logrus.Fields{ - logfields.NewVersion: txn, - logfields.Identity: numericID, - logfields.Labels: old.lbls, - }).Debug("UpdateIdentities: Deleting identity") + sc.logger.Debug( + "UpdateIdentities: Deleting identity", + logfields.NewVersion, txn, + logfields.Identity, numericID, + logfields.Labels, old.lbls, + ) delete(sc.idCache, numericID) } else { - log.WithFields(logrus.Fields{ - logfields.NewVersion: txn, - logfields.Identity: numericID, - }).Warning("UpdateIdentities: Skipping Delete of a non-existing identity") + sc.logger.Warn( + "UpdateIdentities: Skipping Delete of a non-existing identity", + logfields.NewVersion, txn, + logfields.Identity, numericID, + ) delete(deleted, numericID) } } @@ -536,35 +544,41 @@ func (sc *SelectorCache) UpdateIdentities(added, deleted identity.IdentityMap, w // sorted for the kv-store, so there should // not be too many false negatives. if lbls.Equals(old.lbls) { - log.WithFields(logrus.Fields{ - logfields.NewVersion: txn, - logfields.Identity: numericID, - }).Debug("UpdateIdentities: Skipping add of an existing identical identity") + sc.logger.Debug( + "UpdateIdentities: Skipping add of an existing identical identity", + logfields.NewVersion, txn, + logfields.Identity, numericID, + ) delete(added, numericID) continue } - scopedLog := log.WithFields(logrus.Fields{ - logfields.NewVersion: txn, - logfields.Identity: numericID, - logfields.Labels: old.lbls, - logfields.Labels + "(new)": lbls}, - ) msg := "UpdateIdentities: Updating an existing identity" // Warn if any other ID has their labels change, besides local // host. The local host can have its labels change at runtime if // the kube-apiserver is running on the local host, see // ipcache.TriggerLabelInjection(). if numericID == identity.ReservedIdentityHost { - scopedLog.Debug(msg) + sc.logger.Debug(msg, + logfields.NewVersion, txn, + logfields.Identity, numericID, + logfields.Labels, old.lbls, + logfields.LabelsNew, lbls, + ) } else { - scopedLog.Warning(msg) + sc.logger.Warn(msg, + logfields.NewVersion, txn, + logfields.Identity, numericID, + logfields.Labels, old.lbls, + logfields.LabelsNew, lbls, + ) } } else { - log.WithFields(logrus.Fields{ - logfields.NewVersion: txn, - logfields.Identity: numericID, - logfields.Labels: lbls, - }).Debug("UpdateIdentities: Adding a new identity") + sc.logger.Debug( + "UpdateIdentities: Adding a new identity", + logfields.NewVersion, txn, + logfields.Identity, numericID, + logfields.Labels, lbls, + ) } sc.idCache[numericID] = newIdentity(numericID, lbls) } @@ -613,9 +627,10 @@ func (sc *SelectorCache) UpdateIdentities(added, deleted identity.IdentityMap, w go func(version *versioned.VersionHandle) { wg.Wait() - log.WithFields(logrus.Fields{ - logfields.NewVersion: txn, - }).Debug("UpdateIdentities: Waited for incremental updates to have committed, closing handle on the new version.") + sc.logger.Debug( + "UpdateIdentities: Waited for incremental updates to have committed, closing handle on the new version.", + logfields.NewVersion, txn, + ) version.Close() }(txn.GetVersionHandle()) diff --git a/vendor/github.com/cilium/cilium/pkg/policy/selectorcache_selector.go b/vendor/github.com/cilium/cilium/pkg/policy/selectorcache_selector.go index 332396e1e6..ff0a627a9d 100644 --- a/vendor/github.com/cilium/cilium/pkg/policy/selectorcache_selector.go +++ b/vendor/github.com/cilium/cilium/pkg/policy/selectorcache_selector.go @@ -4,11 +4,12 @@ package policy import ( + "log/slog" + "slices" "sort" "sync" "github.com/hashicorp/go-hclog" - "github.com/sirupsen/logrus" "github.com/cilium/cilium/pkg/container/versioned" "github.com/cilium/cilium/pkg/identity" @@ -58,6 +59,7 @@ type CachedSelectionUser types.CachedSelectionUser // so it must always be given to the user as a pointer to the actual type. // (The public methods only expose the CachedSelector interface.) type identitySelector struct { + logger *slog.Logger source selectorSource key string selections versioned.Value[identity.NumericIdentitySlice] @@ -124,10 +126,8 @@ func (l *labelIdentitySelector) xxxMatches(labels labels.LabelArray) bool { func (l *labelIdentitySelector) matchesNamespace(ns string) bool { if len(l.namespaces) > 0 { if ns != "" { - for i := range l.namespaces { - if ns == l.namespaces[i] { - return true - } + if slices.Contains(l.namespaces, ns) { + return true } } // namespace required, but no match @@ -188,10 +188,11 @@ func (i *identitySelector) Equal(b *identitySelector) bool { // guaranteed to receive a notification including the update. func (i *identitySelector) GetSelections(version *versioned.VersionHandle) identity.NumericIdentitySlice { if !version.IsValid() { - log.WithFields(logrus.Fields{ - logfields.Version: version, - logfields.Stacktrace: hclog.Stacktrace(), - }).Error("GetSelections: Invalid VersionHandle finds nothing") + i.logger.Error( + "GetSelections: Invalid VersionHandle finds nothing", + logfields.Version, version, + logfields.Stacktrace, hclog.Stacktrace(), + ) return identity.NumericIdentitySlice{} } return i.selections.At(version) @@ -266,9 +267,7 @@ func (i *identitySelector) updateSelections(nextVersion *versioned.Tx) { // Sort the numeric identities so that the map iteration order // does not matter. This makes testing easier, but may help // identifying changes easier also otherwise. - sort.Slice(selections, func(i, j int) bool { - return selections[i] < selections[j] - }) + slices.Sort(selections) i.setSelections(selections, nextVersion) } @@ -280,7 +279,10 @@ func (i *identitySelector) setSelections(selections identity.NumericIdentitySlic err = i.selections.RemoveAt(nextVersion) } if err != nil { - stacktrace := hclog.Stacktrace() - log.WithError(err).WithField(logfields.Stacktrace, stacktrace).Error("setSelections failed") + i.logger.Error( + "setSelections failed", + logfields.Error, err, + logfields.Stacktrace, hclog.Stacktrace(), + ) } } diff --git a/vendor/github.com/cilium/cilium/pkg/policy/trigger.go b/vendor/github.com/cilium/cilium/pkg/policy/trigger.go index 383e8a7cb1..190e7ef0a2 100644 --- a/vendor/github.com/cilium/cilium/pkg/policy/trigger.go +++ b/vendor/github.com/cilium/cilium/pkg/policy/trigger.go @@ -4,6 +4,8 @@ package policy import ( + "log/slog" + "github.com/cilium/cilium/pkg/logging/logfields" ) @@ -14,24 +16,26 @@ import ( // used in policy calculation has changed. func (u *Updater) TriggerPolicyUpdates(reason string) { u.repo.BumpRevision() - log.WithField(logfields.Reason, reason).Info("Triggering full policy recalculation and regeneration of all endpoints") + u.logger.Info("Triggering full policy recalculation and regeneration of all endpoints", logfields.Reason, reason) u.regen.TriggerRegenerateAllEndpoints() } // NewUpdater returns a new Updater instance to handle triggering policy // updates ready for use. -func NewUpdater(r PolicyRepository, regen regenerator) *Updater { +func NewUpdater(logger *slog.Logger, r PolicyRepository, regen regenerator) *Updater { return &Updater{ - regen: regen, - repo: r, + logger: logger, + regen: regen, + repo: r, } } // Updater is responsible for triggering policy updates, in order to perform // policy recalculation. type Updater struct { - repo PolicyRepository - regen regenerator + logger *slog.Logger + repo PolicyRepository + regen regenerator } type regenerator interface { diff --git a/vendor/github.com/cilium/cilium/pkg/policy/types/entry.go b/vendor/github.com/cilium/cilium/pkg/policy/types/entry.go index 9c360759b0..a72821112d 100644 --- a/vendor/github.com/cilium/cilium/pkg/policy/types/entry.go +++ b/vendor/github.com/cilium/cilium/pkg/policy/types/entry.go @@ -5,11 +5,12 @@ package types import "strconv" +type ListenerPriority uint8 type ProxyPortPriority uint8 const ( - MaxProxyPortPriority = 255 - MaxListenerPriority = 100 + MaxProxyPortPriority = 127 + MaxListenerPriority = 126 ) // MapStateEntry is the configuration associated with a Key in a @@ -53,9 +54,9 @@ func (e MapStateEntry) String() string { authText } -// NewMapStateEntry creeates a new MapStateEntry +// NewMapStateEntry creates a new MapStateEntry // Listener 'priority' is encoded in ProxyPortPriority, inverted -func NewMapStateEntry(deny bool, proxyPort uint16, priority uint8, authReq AuthRequirement) MapStateEntry { +func NewMapStateEntry(deny bool, proxyPort uint16, priority ListenerPriority, authReq AuthRequirement) MapStateEntry { // Normalize inputs if deny { proxyPort = 0 @@ -66,7 +67,7 @@ func NewMapStateEntry(deny bool, proxyPort uint16, priority uint8, authReq AuthR isDeny: deny, ProxyPort: proxyPort, AuthRequirement: authReq, - }.WithProxyPriority(priority) + }.WithListenerPriority(priority) } func (e MapStateEntry) IsDeny() bool { @@ -94,19 +95,28 @@ func (e MapStateEntry) WithDeny(isDeny bool) MapStateEntry { return e } -// WithProxyPriority returns a MapStateEntry with the given listener priority: +// WithListenerPriority returns a MapStateEntry with the given listener priority: // 0 - default (low) priority for all proxy redirects // 1 - highest listener priority // .. // 100 - lowest (non-default) listener priority -func (e MapStateEntry) WithProxyPriority(priority uint8) MapStateEntry { +// 101 - priority for HTTP parser type +// 106 - priority for the Kafka parser type +// 111 - priority for the proxylib parsers +// 116 - priority for TLS interception parsers (can be promoted to HTTP/Kafka/proxylib) +// 121 - priority for DNS parser type +// 126 - default priority for CRD parser type +// 127 - reserved (listener priority passed as 0) +func (e MapStateEntry) WithListenerPriority(priority ListenerPriority) MapStateEntry { if e.ProxyPort != 0 { if priority > 0 { priority = min(priority, MaxListenerPriority) // invert the priority so that higher number has the - // precedence, priority 1 becomes 254, 100 -> 155 - e.ProxyPortPriority = MaxProxyPortPriority - ProxyPortPriority(priority) + // precedence, priority 1 becomes '127', 100 -> '28', 126 -> '2' + // '1' is reserved for a listener priority passed as 0 + // '0' is reserved for entries without proxy redirect + e.ProxyPortPriority = MaxProxyPortPriority + 1 - ProxyPortPriority(priority) } else { e.ProxyPortPriority = 1 // proxy port without explicit priority } @@ -116,8 +126,10 @@ func (e MapStateEntry) WithProxyPriority(priority uint8) MapStateEntry { // WithProxyPort return the MapStateEntry with proxy port set at the default precedence func (e MapStateEntry) WithProxyPort(proxyPort uint16) MapStateEntry { - e.ProxyPort = proxyPort - e.ProxyPortPriority = 1 // proxy port without explicit priority + if proxyPort > 0 { + e.ProxyPort = proxyPort + e.ProxyPortPriority = 1 // proxy port without explicit priority + } return e } diff --git a/vendor/github.com/cilium/cilium/pkg/policy/types/selector.go b/vendor/github.com/cilium/cilium/pkg/policy/types/selector.go index 4cbbd4d4f3..18d2b7fcfa 100644 --- a/vendor/github.com/cilium/cilium/pkg/policy/types/selector.go +++ b/vendor/github.com/cilium/cilium/pkg/policy/types/selector.go @@ -6,6 +6,7 @@ package types import ( "bytes" "encoding/json" + "log/slog" "strings" "github.com/cilium/cilium/pkg/container/versioned" @@ -90,11 +91,11 @@ func (s CachedSelectorSlice) SelectsAllEndpoints() bool { type CachedSelectionUser interface { // The caller is responsible for making sure the same identity is not // present in both 'added' and 'deleted'. - IdentitySelectionUpdated(selector CachedSelector, added, deleted []identity.NumericIdentity) + IdentitySelectionUpdated(logger *slog.Logger, selector CachedSelector, added, deleted []identity.NumericIdentity) // IdentitySelectionCommit tells the user that all IdentitySelectionUpdated calls relating // to a specific added or removed identity have been made. - IdentitySelectionCommit(*versioned.Tx) + IdentitySelectionCommit(logger *slog.Logger, txn *versioned.Tx) // IsPeerSelector returns true if the selector is used by the policy // engine for selecting traffic for remote peers. False if used for diff --git a/vendor/github.com/cilium/cilium/pkg/promise/promise.go b/vendor/github.com/cilium/cilium/pkg/promise/promise.go index d6a7a5f4e0..d20c1bfd8f 100644 --- a/vendor/github.com/cilium/cilium/pkg/promise/promise.go +++ b/vendor/github.com/cilium/cilium/pkg/promise/promise.go @@ -132,14 +132,3 @@ func Map[A, B any](p Promise[A], transform func(A) B) Promise[B] { return transform(v), nil }) } - -// MapError transforms the error of a rejected promise with the provided function. -func MapError[A any](p Promise[A], transform func(error) error) Promise[A] { - return wrappedPromise[A](func(ctx context.Context) (out A, err error) { - v, err := p.Await(ctx) - if err != nil { - err = transform(err) - } - return v, err - }) -} diff --git a/vendor/github.com/cilium/cilium/pkg/safetime/safetime.go b/vendor/github.com/cilium/cilium/pkg/safetime/safetime.go index cc00c25ac5..68cb169078 100644 --- a/vendor/github.com/cilium/cilium/pkg/safetime/safetime.go +++ b/vendor/github.com/cilium/cilium/pkg/safetime/safetime.go @@ -4,10 +4,9 @@ package safetime import ( + "log/slog" "runtime" - "github.com/sirupsen/logrus" - "github.com/cilium/cilium/pkg/logging/logfields" "github.com/cilium/cilium/pkg/time" ) @@ -16,24 +15,24 @@ import ( // returns false to indicate the fact. // // Used to workaround a malfunctioning monotonic clock. -func TimeSinceSafe(t time.Time, logger *logrus.Entry) (time.Duration, bool) { +func TimeSinceSafe(t time.Time, logger *slog.Logger) (time.Duration, bool) { n := time.Now() d := n.Sub(t) if d < 0 { - logger = logger.WithFields(logrus.Fields{ - logfields.StartTime: t, - logfields.EndTime: n, - logfields.Duration: d, - }) + scopedLog := logger.With( + logfields.StartTime, t, + logfields.EndTime, n, + logfields.Duration, d, + ) _, file, line, ok := runtime.Caller(1) if ok { - logger = logger.WithFields(logrus.Fields{ - logfields.Path: file, - logfields.Line: line, - }) + scopedLog = scopedLog.With( + logfields.Path, file, + logfields.Line, line, + ) } - logger.Warn("BUG: negative duration") + scopedLog.Warn("BUG: negative duration") return time.Duration(0), false } diff --git a/vendor/github.com/cilium/cilium/pkg/spanstat/spanstat.go b/vendor/github.com/cilium/cilium/pkg/spanstat/spanstat.go index 2557233533..5e9fd8405f 100644 --- a/vendor/github.com/cilium/cilium/pkg/spanstat/spanstat.go +++ b/vendor/github.com/cilium/cilium/pkg/spanstat/spanstat.go @@ -6,16 +6,10 @@ package spanstat import ( "github.com/cilium/cilium/pkg/lock" "github.com/cilium/cilium/pkg/logging" - "github.com/cilium/cilium/pkg/logging/logfields" "github.com/cilium/cilium/pkg/safetime" "github.com/cilium/cilium/pkg/time" ) -var ( - subSystem = "spanstat" - log = logging.DefaultLogger.WithField(logfields.LogSubsys, subSystem) -) - // SpanStat measures the total duration of all time spent in between Start() // and Stop() calls. type SpanStat struct { @@ -58,7 +52,7 @@ func (s *SpanStat) End(success bool) *SpanStat { // must be called with Lock() held func (s *SpanStat) end(success bool) *SpanStat { if !s.spanStart.IsZero() { - d, _ := safetime.TimeSinceSafe(s.spanStart, log) + d, _ := safetime.TimeSinceSafe(s.spanStart, logging.DefaultSlogLogger) if success { s.successDuration += d } else { diff --git a/vendor/github.com/cilium/cilium/stable.txt b/vendor/github.com/cilium/cilium/stable.txt deleted file mode 100644 index 7893098b6f..0000000000 --- a/vendor/github.com/cilium/cilium/stable.txt +++ /dev/null @@ -1 +0,0 @@ -v1.17.1 diff --git a/vendor/github.com/cilium/ebpf/.golangci.yaml b/vendor/github.com/cilium/ebpf/.golangci.yaml index 366d4893f2..8298e59ed8 100644 --- a/vendor/github.com/cilium/ebpf/.golangci.yaml +++ b/vendor/github.com/cilium/ebpf/.golangci.yaml @@ -11,9 +11,21 @@ linters: - typecheck - unused - gofmt + - depguard linters-settings: goimports: # A comma-separated list of prefixes, which, if set, checks import paths # with the given prefixes are grouped after 3rd-party packages. # Default: "" local-prefixes: github.com/cilium/ebpf + depguard: + rules: + no-x-sys-unix: + files: + # Filenames are matched against absolute paths, include **/ at the start. + - '!**/internal/unix/*.go' + - '!**/examples/**/*.go' + - '!**/docs/**/*.go' + deny: + - pkg: golang.org/x/sys/unix + desc: use internal/unix instead diff --git a/vendor/github.com/cilium/ebpf/asm/func.go b/vendor/github.com/cilium/ebpf/asm/func.go index 84a40b2277..7a59acf383 100644 --- a/vendor/github.com/cilium/ebpf/asm/func.go +++ b/vendor/github.com/cilium/ebpf/asm/func.go @@ -5,10 +5,6 @@ package asm // BuiltinFunc is a built-in eBPF function. type BuiltinFunc int32 -func (_ BuiltinFunc) Max() BuiltinFunc { - return maxBuiltinFunc - 1 -} - // eBPF built-in functions // // You can regenerate this list using the following gawk script: @@ -237,8 +233,6 @@ const ( FnUserRingbufDrain FnCgrpStorageGet FnCgrpStorageDelete - - maxBuiltinFunc ) // Call emits a function call. diff --git a/vendor/github.com/cilium/ebpf/asm/func_string.go b/vendor/github.com/cilium/ebpf/asm/func_string.go index 47150bc4f2..6c4ba37892 100644 --- a/vendor/github.com/cilium/ebpf/asm/func_string.go +++ b/vendor/github.com/cilium/ebpf/asm/func_string.go @@ -220,12 +220,11 @@ func _() { _ = x[FnUserRingbufDrain-209] _ = x[FnCgrpStorageGet-210] _ = x[FnCgrpStorageDelete-211] - _ = x[maxBuiltinFunc-212] } -const _BuiltinFunc_name = "FnUnspecFnMapLookupElemFnMapUpdateElemFnMapDeleteElemFnProbeReadFnKtimeGetNsFnTracePrintkFnGetPrandomU32FnGetSmpProcessorIdFnSkbStoreBytesFnL3CsumReplaceFnL4CsumReplaceFnTailCallFnCloneRedirectFnGetCurrentPidTgidFnGetCurrentUidGidFnGetCurrentCommFnGetCgroupClassidFnSkbVlanPushFnSkbVlanPopFnSkbGetTunnelKeyFnSkbSetTunnelKeyFnPerfEventReadFnRedirectFnGetRouteRealmFnPerfEventOutputFnSkbLoadBytesFnGetStackidFnCsumDiffFnSkbGetTunnelOptFnSkbSetTunnelOptFnSkbChangeProtoFnSkbChangeTypeFnSkbUnderCgroupFnGetHashRecalcFnGetCurrentTaskFnProbeWriteUserFnCurrentTaskUnderCgroupFnSkbChangeTailFnSkbPullDataFnCsumUpdateFnSetHashInvalidFnGetNumaNodeIdFnSkbChangeHeadFnXdpAdjustHeadFnProbeReadStrFnGetSocketCookieFnGetSocketUidFnSetHashFnSetsockoptFnSkbAdjustRoomFnRedirectMapFnSkRedirectMapFnSockMapUpdateFnXdpAdjustMetaFnPerfEventReadValueFnPerfProgReadValueFnGetsockoptFnOverrideReturnFnSockOpsCbFlagsSetFnMsgRedirectMapFnMsgApplyBytesFnMsgCorkBytesFnMsgPullDataFnBindFnXdpAdjustTailFnSkbGetXfrmStateFnGetStackFnSkbLoadBytesRelativeFnFibLookupFnSockHashUpdateFnMsgRedirectHashFnSkRedirectHashFnLwtPushEncapFnLwtSeg6StoreBytesFnLwtSeg6AdjustSrhFnLwtSeg6ActionFnRcRepeatFnRcKeydownFnSkbCgroupIdFnGetCurrentCgroupIdFnGetLocalStorageFnSkSelectReuseportFnSkbAncestorCgroupIdFnSkLookupTcpFnSkLookupUdpFnSkReleaseFnMapPushElemFnMapPopElemFnMapPeekElemFnMsgPushDataFnMsgPopDataFnRcPointerRelFnSpinLockFnSpinUnlockFnSkFullsockFnTcpSockFnSkbEcnSetCeFnGetListenerSockFnSkcLookupTcpFnTcpCheckSyncookieFnSysctlGetNameFnSysctlGetCurrentValueFnSysctlGetNewValueFnSysctlSetNewValueFnStrtolFnStrtoulFnSkStorageGetFnSkStorageDeleteFnSendSignalFnTcpGenSyncookieFnSkbOutputFnProbeReadUserFnProbeReadKernelFnProbeReadUserStrFnProbeReadKernelStrFnTcpSendAckFnSendSignalThreadFnJiffies64FnReadBranchRecordsFnGetNsCurrentPidTgidFnXdpOutputFnGetNetnsCookieFnGetCurrentAncestorCgroupIdFnSkAssignFnKtimeGetBootNsFnSeqPrintfFnSeqWriteFnSkCgroupIdFnSkAncestorCgroupIdFnRingbufOutputFnRingbufReserveFnRingbufSubmitFnRingbufDiscardFnRingbufQueryFnCsumLevelFnSkcToTcp6SockFnSkcToTcpSockFnSkcToTcpTimewaitSockFnSkcToTcpRequestSockFnSkcToUdp6SockFnGetTaskStackFnLoadHdrOptFnStoreHdrOptFnReserveHdrOptFnInodeStorageGetFnInodeStorageDeleteFnDPathFnCopyFromUserFnSnprintfBtfFnSeqPrintfBtfFnSkbCgroupClassidFnRedirectNeighFnPerCpuPtrFnThisCpuPtrFnRedirectPeerFnTaskStorageGetFnTaskStorageDeleteFnGetCurrentTaskBtfFnBprmOptsSetFnKtimeGetCoarseNsFnImaInodeHashFnSockFromFileFnCheckMtuFnForEachMapElemFnSnprintfFnSysBpfFnBtfFindByNameKindFnSysCloseFnTimerInitFnTimerSetCallbackFnTimerStartFnTimerCancelFnGetFuncIpFnGetAttachCookieFnTaskPtRegsFnGetBranchSnapshotFnTraceVprintkFnSkcToUnixSockFnKallsymsLookupNameFnFindVmaFnLoopFnStrncmpFnGetFuncArgFnGetFuncRetFnGetFuncArgCntFnGetRetvalFnSetRetvalFnXdpGetBuffLenFnXdpLoadBytesFnXdpStoreBytesFnCopyFromUserTaskFnSkbSetTstampFnImaFileHashFnKptrXchgFnMapLookupPercpuElemFnSkcToMptcpSockFnDynptrFromMemFnRingbufReserveDynptrFnRingbufSubmitDynptrFnRingbufDiscardDynptrFnDynptrReadFnDynptrWriteFnDynptrDataFnTcpRawGenSyncookieIpv4FnTcpRawGenSyncookieIpv6FnTcpRawCheckSyncookieIpv4FnTcpRawCheckSyncookieIpv6FnKtimeGetTaiNsFnUserRingbufDrainFnCgrpStorageGetFnCgrpStorageDeletemaxBuiltinFunc" +const _BuiltinFunc_name = "FnUnspecFnMapLookupElemFnMapUpdateElemFnMapDeleteElemFnProbeReadFnKtimeGetNsFnTracePrintkFnGetPrandomU32FnGetSmpProcessorIdFnSkbStoreBytesFnL3CsumReplaceFnL4CsumReplaceFnTailCallFnCloneRedirectFnGetCurrentPidTgidFnGetCurrentUidGidFnGetCurrentCommFnGetCgroupClassidFnSkbVlanPushFnSkbVlanPopFnSkbGetTunnelKeyFnSkbSetTunnelKeyFnPerfEventReadFnRedirectFnGetRouteRealmFnPerfEventOutputFnSkbLoadBytesFnGetStackidFnCsumDiffFnSkbGetTunnelOptFnSkbSetTunnelOptFnSkbChangeProtoFnSkbChangeTypeFnSkbUnderCgroupFnGetHashRecalcFnGetCurrentTaskFnProbeWriteUserFnCurrentTaskUnderCgroupFnSkbChangeTailFnSkbPullDataFnCsumUpdateFnSetHashInvalidFnGetNumaNodeIdFnSkbChangeHeadFnXdpAdjustHeadFnProbeReadStrFnGetSocketCookieFnGetSocketUidFnSetHashFnSetsockoptFnSkbAdjustRoomFnRedirectMapFnSkRedirectMapFnSockMapUpdateFnXdpAdjustMetaFnPerfEventReadValueFnPerfProgReadValueFnGetsockoptFnOverrideReturnFnSockOpsCbFlagsSetFnMsgRedirectMapFnMsgApplyBytesFnMsgCorkBytesFnMsgPullDataFnBindFnXdpAdjustTailFnSkbGetXfrmStateFnGetStackFnSkbLoadBytesRelativeFnFibLookupFnSockHashUpdateFnMsgRedirectHashFnSkRedirectHashFnLwtPushEncapFnLwtSeg6StoreBytesFnLwtSeg6AdjustSrhFnLwtSeg6ActionFnRcRepeatFnRcKeydownFnSkbCgroupIdFnGetCurrentCgroupIdFnGetLocalStorageFnSkSelectReuseportFnSkbAncestorCgroupIdFnSkLookupTcpFnSkLookupUdpFnSkReleaseFnMapPushElemFnMapPopElemFnMapPeekElemFnMsgPushDataFnMsgPopDataFnRcPointerRelFnSpinLockFnSpinUnlockFnSkFullsockFnTcpSockFnSkbEcnSetCeFnGetListenerSockFnSkcLookupTcpFnTcpCheckSyncookieFnSysctlGetNameFnSysctlGetCurrentValueFnSysctlGetNewValueFnSysctlSetNewValueFnStrtolFnStrtoulFnSkStorageGetFnSkStorageDeleteFnSendSignalFnTcpGenSyncookieFnSkbOutputFnProbeReadUserFnProbeReadKernelFnProbeReadUserStrFnProbeReadKernelStrFnTcpSendAckFnSendSignalThreadFnJiffies64FnReadBranchRecordsFnGetNsCurrentPidTgidFnXdpOutputFnGetNetnsCookieFnGetCurrentAncestorCgroupIdFnSkAssignFnKtimeGetBootNsFnSeqPrintfFnSeqWriteFnSkCgroupIdFnSkAncestorCgroupIdFnRingbufOutputFnRingbufReserveFnRingbufSubmitFnRingbufDiscardFnRingbufQueryFnCsumLevelFnSkcToTcp6SockFnSkcToTcpSockFnSkcToTcpTimewaitSockFnSkcToTcpRequestSockFnSkcToUdp6SockFnGetTaskStackFnLoadHdrOptFnStoreHdrOptFnReserveHdrOptFnInodeStorageGetFnInodeStorageDeleteFnDPathFnCopyFromUserFnSnprintfBtfFnSeqPrintfBtfFnSkbCgroupClassidFnRedirectNeighFnPerCpuPtrFnThisCpuPtrFnRedirectPeerFnTaskStorageGetFnTaskStorageDeleteFnGetCurrentTaskBtfFnBprmOptsSetFnKtimeGetCoarseNsFnImaInodeHashFnSockFromFileFnCheckMtuFnForEachMapElemFnSnprintfFnSysBpfFnBtfFindByNameKindFnSysCloseFnTimerInitFnTimerSetCallbackFnTimerStartFnTimerCancelFnGetFuncIpFnGetAttachCookieFnTaskPtRegsFnGetBranchSnapshotFnTraceVprintkFnSkcToUnixSockFnKallsymsLookupNameFnFindVmaFnLoopFnStrncmpFnGetFuncArgFnGetFuncRetFnGetFuncArgCntFnGetRetvalFnSetRetvalFnXdpGetBuffLenFnXdpLoadBytesFnXdpStoreBytesFnCopyFromUserTaskFnSkbSetTstampFnImaFileHashFnKptrXchgFnMapLookupPercpuElemFnSkcToMptcpSockFnDynptrFromMemFnRingbufReserveDynptrFnRingbufSubmitDynptrFnRingbufDiscardDynptrFnDynptrReadFnDynptrWriteFnDynptrDataFnTcpRawGenSyncookieIpv4FnTcpRawGenSyncookieIpv6FnTcpRawCheckSyncookieIpv4FnTcpRawCheckSyncookieIpv6FnKtimeGetTaiNsFnUserRingbufDrainFnCgrpStorageGetFnCgrpStorageDelete" -var _BuiltinFunc_index = [...]uint16{0, 8, 23, 38, 53, 64, 76, 89, 104, 123, 138, 153, 168, 178, 193, 212, 230, 246, 264, 277, 289, 306, 323, 338, 348, 363, 380, 394, 406, 416, 433, 450, 466, 481, 497, 512, 528, 544, 568, 583, 596, 608, 624, 639, 654, 669, 683, 700, 714, 723, 735, 750, 763, 778, 793, 808, 828, 847, 859, 875, 894, 910, 925, 939, 952, 958, 973, 990, 1000, 1022, 1033, 1049, 1066, 1082, 1096, 1115, 1133, 1148, 1158, 1169, 1182, 1202, 1219, 1238, 1259, 1272, 1285, 1296, 1309, 1321, 1334, 1347, 1359, 1373, 1383, 1395, 1407, 1416, 1429, 1446, 1460, 1479, 1494, 1517, 1536, 1555, 1563, 1572, 1586, 1603, 1615, 1632, 1643, 1658, 1675, 1693, 1713, 1725, 1743, 1754, 1773, 1794, 1805, 1821, 1849, 1859, 1875, 1886, 1896, 1908, 1928, 1943, 1959, 1974, 1990, 2004, 2015, 2030, 2044, 2066, 2087, 2102, 2116, 2128, 2141, 2156, 2173, 2193, 2200, 2214, 2227, 2241, 2259, 2274, 2285, 2297, 2311, 2327, 2346, 2365, 2378, 2396, 2410, 2424, 2434, 2450, 2460, 2468, 2487, 2497, 2508, 2526, 2538, 2551, 2562, 2579, 2591, 2610, 2624, 2639, 2659, 2668, 2674, 2683, 2695, 2707, 2722, 2733, 2744, 2759, 2773, 2788, 2806, 2820, 2833, 2843, 2864, 2880, 2895, 2917, 2938, 2960, 2972, 2985, 2997, 3021, 3045, 3071, 3097, 3112, 3130, 3146, 3165, 3179} +var _BuiltinFunc_index = [...]uint16{0, 8, 23, 38, 53, 64, 76, 89, 104, 123, 138, 153, 168, 178, 193, 212, 230, 246, 264, 277, 289, 306, 323, 338, 348, 363, 380, 394, 406, 416, 433, 450, 466, 481, 497, 512, 528, 544, 568, 583, 596, 608, 624, 639, 654, 669, 683, 700, 714, 723, 735, 750, 763, 778, 793, 808, 828, 847, 859, 875, 894, 910, 925, 939, 952, 958, 973, 990, 1000, 1022, 1033, 1049, 1066, 1082, 1096, 1115, 1133, 1148, 1158, 1169, 1182, 1202, 1219, 1238, 1259, 1272, 1285, 1296, 1309, 1321, 1334, 1347, 1359, 1373, 1383, 1395, 1407, 1416, 1429, 1446, 1460, 1479, 1494, 1517, 1536, 1555, 1563, 1572, 1586, 1603, 1615, 1632, 1643, 1658, 1675, 1693, 1713, 1725, 1743, 1754, 1773, 1794, 1805, 1821, 1849, 1859, 1875, 1886, 1896, 1908, 1928, 1943, 1959, 1974, 1990, 2004, 2015, 2030, 2044, 2066, 2087, 2102, 2116, 2128, 2141, 2156, 2173, 2193, 2200, 2214, 2227, 2241, 2259, 2274, 2285, 2297, 2311, 2327, 2346, 2365, 2378, 2396, 2410, 2424, 2434, 2450, 2460, 2468, 2487, 2497, 2508, 2526, 2538, 2551, 2562, 2579, 2591, 2610, 2624, 2639, 2659, 2668, 2674, 2683, 2695, 2707, 2722, 2733, 2744, 2759, 2773, 2788, 2806, 2820, 2833, 2843, 2864, 2880, 2895, 2917, 2938, 2960, 2972, 2985, 2997, 3021, 3045, 3071, 3097, 3112, 3130, 3146, 3165} func (i BuiltinFunc) String() string { if i < 0 || i >= BuiltinFunc(len(_BuiltinFunc_index)-1) { diff --git a/vendor/github.com/cilium/ebpf/attachtype_string.go b/vendor/github.com/cilium/ebpf/attachtype_string.go index bece896bb6..84445a3256 100644 --- a/vendor/github.com/cilium/ebpf/attachtype_string.go +++ b/vendor/github.com/cilium/ebpf/attachtype_string.go @@ -52,6 +52,7 @@ func _() { _ = x[AttachSkReuseportSelectOrMigrate-40] _ = x[AttachPerfEvent-41] _ = x[AttachTraceKprobeMulti-42] + _ = x[AttachTraceKprobeSession-56] _ = x[AttachLSMCgroup-43] _ = x[AttachStructOps-44] _ = x[AttachNetfilter-45] @@ -67,9 +68,9 @@ func _() { _ = x[AttachNetkitPeer-55] } -const _AttachType_name = "NoneCGroupInetEgressCGroupInetSockCreateCGroupSockOpsSkSKBStreamParserSkSKBStreamVerdictCGroupDeviceSkMsgVerdictCGroupInet4BindCGroupInet6BindCGroupInet4ConnectCGroupInet6ConnectCGroupInet4PostBindCGroupInet6PostBindCGroupUDP4SendmsgCGroupUDP6SendmsgLircMode2FlowDissectorCGroupSysctlCGroupUDP4RecvmsgCGroupUDP6RecvmsgCGroupGetsockoptCGroupSetsockoptTraceRawTpTraceFEntryTraceFExitModifyReturnLSMMacTraceIterCgroupInet4GetPeernameCgroupInet6GetPeernameCgroupInet4GetSocknameCgroupInet6GetSocknameXDPDevMapCgroupInetSockReleaseXDPCPUMapSkLookupXDPSkSKBVerdictSkReuseportSelectSkReuseportSelectOrMigratePerfEventTraceKprobeMultiLSMCgroupStructOpsNetfilterTCXIngressTCXEgressTraceUprobeMultiCgroupUnixConnectCgroupUnixSendmsgCgroupUnixRecvmsgCgroupUnixGetpeernameCgroupUnixGetsocknameNetkitPrimaryNetkitPeer" +const _AttachType_name = "NoneCGroupInetEgressCGroupInetSockCreateCGroupSockOpsSkSKBStreamParserSkSKBStreamVerdictCGroupDeviceSkMsgVerdictCGroupInet4BindCGroupInet6BindCGroupInet4ConnectCGroupInet6ConnectCGroupInet4PostBindCGroupInet6PostBindCGroupUDP4SendmsgCGroupUDP6SendmsgLircMode2FlowDissectorCGroupSysctlCGroupUDP4RecvmsgCGroupUDP6RecvmsgCGroupGetsockoptCGroupSetsockoptTraceRawTpTraceFEntryTraceFExitModifyReturnLSMMacTraceIterCgroupInet4GetPeernameCgroupInet6GetPeernameCgroupInet4GetSocknameCgroupInet6GetSocknameXDPDevMapCgroupInetSockReleaseXDPCPUMapSkLookupXDPSkSKBVerdictSkReuseportSelectSkReuseportSelectOrMigratePerfEventTraceKprobeMultiLSMCgroupStructOpsNetfilterTCXIngressTCXEgressTraceUprobeMultiCgroupUnixConnectCgroupUnixSendmsgCgroupUnixRecvmsgCgroupUnixGetpeernameCgroupUnixGetsocknameNetkitPrimaryNetkitPeerTraceKprobeSession" -var _AttachType_index = [...]uint16{0, 4, 20, 40, 53, 70, 88, 100, 112, 127, 142, 160, 178, 197, 216, 233, 250, 259, 272, 284, 301, 318, 334, 350, 360, 371, 381, 393, 399, 408, 430, 452, 474, 496, 505, 526, 535, 543, 546, 558, 575, 601, 610, 626, 635, 644, 653, 663, 672, 688, 705, 722, 739, 760, 781, 794, 804} +var _AttachType_index = [...]uint16{0, 4, 20, 40, 53, 70, 88, 100, 112, 127, 142, 160, 178, 197, 216, 233, 250, 259, 272, 284, 301, 318, 334, 350, 360, 371, 381, 393, 399, 408, 430, 452, 474, 496, 505, 526, 535, 543, 546, 558, 575, 601, 610, 626, 635, 644, 653, 663, 672, 688, 705, 722, 739, 760, 781, 794, 804, 822} func (i AttachType) String() string { if i >= AttachType(len(_AttachType_index)-1) { diff --git a/vendor/github.com/cilium/ebpf/btf/btf.go b/vendor/github.com/cilium/ebpf/btf/btf.go index 880c5ade0c..e8f80c1399 100644 --- a/vendor/github.com/cilium/ebpf/btf/btf.go +++ b/vendor/github.com/cilium/ebpf/btf/btf.go @@ -99,6 +99,10 @@ func (mt *mutableTypes) copy() *mutableTypes { return nil } + // Prevent concurrent modification of mt.copiedTypeIDs. + mt.mu.RLock() + defer mt.mu.RUnlock() + mtCopy := &mutableTypes{ mt.imm, sync.RWMutex{}, @@ -106,10 +110,6 @@ func (mt *mutableTypes) copy() *mutableTypes { make(map[Type]TypeID, len(mt.copiedTypeIDs)), } - // Prevent concurrent modification of mt.copiedTypeIDs. - mt.mu.RLock() - defer mt.mu.RUnlock() - copiesOfCopies := make(map[Type]Type, len(mt.copies)) for orig, copy := range mt.copies { // NB: We make a copy of copy, not orig, so that changes to mutable types diff --git a/vendor/github.com/cilium/ebpf/btf/ext_info.go b/vendor/github.com/cilium/ebpf/btf/ext_info.go index 2c684fe2a7..71dbba8061 100644 --- a/vendor/github.com/cilium/ebpf/btf/ext_info.go +++ b/vendor/github.com/cilium/ebpf/btf/ext_info.go @@ -666,20 +666,19 @@ func parseLineInfos(r io.Reader, bo binary.ByteOrder, strings *stringTable) (map // These records appear after a btf_ext_info_sec header in the line_info // sub-section of .BTF.ext. func parseLineInfoRecords(r io.Reader, bo binary.ByteOrder, recordSize uint32, recordNum uint32, offsetInBytes bool) ([]bpfLineInfo, error) { - var li bpfLineInfo - - if exp, got := uint32(binary.Size(li)), recordSize; exp != got { + if exp, got := uint32(binary.Size(bpfLineInfo{})), recordSize; exp != got { // BTF blob's record size is longer than we know how to parse. return nil, fmt.Errorf("expected LineInfo record size %d, but BTF blob contains %d", exp, got) } - out := make([]bpfLineInfo, 0, recordNum) - for i := uint32(0); i < recordNum; i++ { - if err := binary.Read(r, bo, &li); err != nil { - return nil, fmt.Errorf("can't read line info: %v", err) - } + out := make([]bpfLineInfo, recordNum) + if err := binary.Read(r, bo, out); err != nil { + return nil, fmt.Errorf("can't read line info: %v", err) + } - if offsetInBytes { + if offsetInBytes { + for i := range out { + li := &out[i] if li.InsnOff%asm.InstructionSize != 0 { return nil, fmt.Errorf("offset %v is not aligned with instruction size", li.InsnOff) } @@ -688,8 +687,6 @@ func parseLineInfoRecords(r io.Reader, bo binary.ByteOrder, recordSize uint32, r // Convert as early as possible. li.InsnOff /= asm.InstructionSize } - - out = append(out, li) } return out, nil diff --git a/vendor/github.com/cilium/ebpf/collection.go b/vendor/github.com/cilium/ebpf/collection.go index 1bda110a40..a7b6cb31bf 100644 --- a/vendor/github.com/cilium/ebpf/collection.go +++ b/vendor/github.com/cilium/ebpf/collection.go @@ -411,15 +411,10 @@ func newCollectionLoader(coll *CollectionSpec, opts *CollectionOptions) (*collec } // Check for existing MapSpecs in the CollectionSpec for all provided replacement maps. - for name, m := range opts.MapReplacements { - spec, ok := coll.Maps[name] - if !ok { + for name := range opts.MapReplacements { + if _, ok := coll.Maps[name]; !ok { return nil, fmt.Errorf("replacement map %s not found in CollectionSpec", name) } - - if err := spec.Compatible(m); err != nil { - return nil, fmt.Errorf("using replacement map %s: %w", spec.Name, err) - } } if err := populateKallsyms(coll.Programs); err != nil { @@ -494,7 +489,22 @@ func (cl *collectionLoader) loadMap(mapName string) (*Map, error) { return nil, fmt.Errorf("missing map %s", mapName) } + mapSpec = mapSpec.Copy() + + // Defer setting the mmapable flag on maps until load time. This avoids the + // MapSpec having different flags on some kernel versions. Also avoid running + // syscalls during ELF loading, so platforms like wasm can also parse an ELF. + if isDataSection(mapSpec.Name) && haveMmapableMaps() == nil { + mapSpec.Flags |= sys.BPF_F_MMAPABLE + } + if replaceMap, ok := cl.opts.MapReplacements[mapName]; ok { + // Check compatibility with the replacement map after setting + // feature-dependent map flags. + if err := mapSpec.Compatible(replaceMap); err != nil { + return nil, fmt.Errorf("using replacement map %s: %w", mapSpec.Name, err) + } + // Clone the map to avoid closing user's map later on. m, err := replaceMap.Clone() if err != nil { @@ -505,13 +515,6 @@ func (cl *collectionLoader) loadMap(mapName string) (*Map, error) { return m, nil } - // Defer setting the mmapable flag on maps until load time. This avoids the - // MapSpec having different flags on some kernel versions. Also avoid running - // syscalls during ELF loading, so platforms like wasm can also parse an ELF. - if isDataSection(mapSpec.Name) && haveMmapableMaps() == nil { - mapSpec.Flags |= sys.BPF_F_MMAPABLE - } - m, err := newMapWithOptions(mapSpec, cl.opts.Maps) if err != nil { return nil, fmt.Errorf("map %s: %w", mapName, err) diff --git a/vendor/github.com/cilium/ebpf/elf_sections.go b/vendor/github.com/cilium/ebpf/elf_sections.go index 4b58251d9a..43dcfb103e 100644 --- a/vendor/github.com/cilium/ebpf/elf_sections.go +++ b/vendor/github.com/cilium/ebpf/elf_sections.go @@ -18,6 +18,7 @@ var elfSectionDefs = []libbpfElfSectionDef{ {"uretprobe.s+", sys.BPF_PROG_TYPE_KPROBE, 0, _SEC_SLEEPABLE}, {"kprobe.multi+", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_KPROBE_MULTI, _SEC_NONE}, {"kretprobe.multi+", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_KPROBE_MULTI, _SEC_NONE}, + {"kprobe.session+", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_KPROBE_SESSION, _SEC_NONE}, {"uprobe.multi+", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_UPROBE_MULTI, _SEC_NONE}, {"uretprobe.multi+", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_UPROBE_MULTI, _SEC_NONE}, {"uprobe.multi.s+", sys.BPF_PROG_TYPE_KPROBE, sys.BPF_TRACE_UPROBE_MULTI, _SEC_SLEEPABLE}, @@ -69,6 +70,7 @@ var elfSectionDefs = []libbpfElfSectionDef{ {"sockops", sys.BPF_PROG_TYPE_SOCK_OPS, sys.BPF_CGROUP_SOCK_OPS, _SEC_ATTACHABLE_OPT}, {"sk_skb/stream_parser", sys.BPF_PROG_TYPE_SK_SKB, sys.BPF_SK_SKB_STREAM_PARSER, _SEC_ATTACHABLE_OPT}, {"sk_skb/stream_verdict", sys.BPF_PROG_TYPE_SK_SKB, sys.BPF_SK_SKB_STREAM_VERDICT, _SEC_ATTACHABLE_OPT}, + {"sk_skb/verdict", sys.BPF_PROG_TYPE_SK_SKB, sys.BPF_SK_SKB_VERDICT, _SEC_ATTACHABLE_OPT}, {"sk_skb", sys.BPF_PROG_TYPE_SK_SKB, 0, _SEC_NONE}, {"sk_msg", sys.BPF_PROG_TYPE_SK_MSG, sys.BPF_SK_MSG_VERDICT, _SEC_ATTACHABLE_OPT}, {"lirc_mode2", sys.BPF_PROG_TYPE_LIRC_MODE2, sys.BPF_LIRC_MODE2, _SEC_ATTACHABLE_OPT}, diff --git a/vendor/github.com/cilium/ebpf/features/map.go b/vendor/github.com/cilium/ebpf/features/map.go index 4b16e6af42..2c68ab05db 100644 --- a/vendor/github.com/cilium/ebpf/features/map.go +++ b/vendor/github.com/cilium/ebpf/features/map.go @@ -1,3 +1,5 @@ +//go:build linux + package features import ( diff --git a/vendor/github.com/cilium/ebpf/features/misc.go b/vendor/github.com/cilium/ebpf/features/misc.go index c039020a95..f07ef92adc 100644 --- a/vendor/github.com/cilium/ebpf/features/misc.go +++ b/vendor/github.com/cilium/ebpf/features/misc.go @@ -1,3 +1,5 @@ +//go:build linux + package features import ( diff --git a/vendor/github.com/cilium/ebpf/features/prog.go b/vendor/github.com/cilium/ebpf/features/prog.go index 003bf00646..e71525e5f8 100644 --- a/vendor/github.com/cilium/ebpf/features/prog.go +++ b/vendor/github.com/cilium/ebpf/features/prog.go @@ -1,9 +1,12 @@ +//go:build linux + package features import ( "errors" "fmt" - "os" + "slices" + "strings" "github.com/cilium/ebpf" "github.com/cilium/ebpf/asm" @@ -229,10 +232,6 @@ var helperCache = internal.NewFeatureCache(func(key helperKey) *internal.Feature // // Probe results are cached and persist throughout any process capability changes. func HaveProgramHelper(pt ebpf.ProgramType, helper asm.BuiltinFunc) error { - if helper > helper.Max() { - return os.ErrInvalid - } - return helperCache.Result(helperKey{pt, helper}) } @@ -267,30 +266,59 @@ func haveProgramHelper(pt ebpf.ProgramType, helper asm.BuiltinFunc) error { } prog, err := ebpf.NewProgramWithOptions(spec, ebpf.ProgramOptions{ - LogDisabled: true, + LogLevel: 1, }) if err == nil { prog.Close() } + var verr *ebpf.VerifierError + if !errors.As(err, &verr) { + return err + } + + helperTag := fmt.Sprintf("#%d", helper) + switch { // EACCES occurs when attempting to create a program probe with a helper // while the register args when calling this helper aren't set up properly. // We interpret this as the helper being available, because the verifier // returns EINVAL if the helper is not supported by the running kernel. case errors.Is(err, unix.EACCES): - // TODO: possibly we need to check verifier output here to be sure err = nil // EINVAL occurs when attempting to create a program with an unknown helper. case errors.Is(err, unix.EINVAL): - // TODO: possibly we need to check verifier output here to be sure - err = ebpf.ErrNotSupported + // https://github.com/torvalds/linux/blob/09a0fa92e5b45e99cf435b2fbf5ebcf889cf8780/kernel/bpf/verifier.c#L10663 + if logContainsAll(verr.Log, "invalid func", helperTag) { + return ebpf.ErrNotSupported + } + + // https://github.com/torvalds/linux/blob/09a0fa92e5b45e99cf435b2fbf5ebcf889cf8780/kernel/bpf/verifier.c#L10668 + wrongProgramType := logContainsAll(verr.Log, "program of this type cannot use helper", helperTag) + // https://github.com/torvalds/linux/blob/59b418c7063d30e0a3e1f592d47df096db83185c/kernel/bpf/verifier.c#L10204 + // 4.9 doesn't include # in verifier output. + wrongProgramType = wrongProgramType || logContainsAll(verr.Log, "unknown func") + if wrongProgramType { + return fmt.Errorf("program of this type cannot use helper: %w", ebpf.ErrNotSupported) + } } return err } +func logContainsAll(log []string, needles ...string) bool { + first := max(len(log)-5, 0) // Check last 5 lines. + return slices.ContainsFunc(log[first:], func(line string) bool { + for _, needle := range needles { + if !strings.Contains(line, needle) { + return false + } + } + return true + }) +} + func helperProbeNotImplemented(pt ebpf.ProgramType) bool { switch pt { case ebpf.Extension, ebpf.LSM, ebpf.StructOps, ebpf.Tracing: diff --git a/vendor/github.com/cilium/ebpf/features/version.go b/vendor/github.com/cilium/ebpf/features/version.go index d54d3ea212..124dcd2bc9 100644 --- a/vendor/github.com/cilium/ebpf/features/version.go +++ b/vendor/github.com/cilium/ebpf/features/version.go @@ -1,3 +1,5 @@ +//go:build linux + package features import "github.com/cilium/ebpf/internal/linux" diff --git a/vendor/github.com/cilium/ebpf/info.go b/vendor/github.com/cilium/ebpf/info.go index 56a1f1e9a3..676e8fa6e7 100644 --- a/vendor/github.com/cilium/ebpf/info.go +++ b/vendor/github.com/cilium/ebpf/info.go @@ -185,7 +185,7 @@ type programJitedInfo struct { // subprograms. // // Available from 4.18. - ksyms []uintptr + ksyms []uint64 numKsyms uint32 // insns holds the JITed machine native instructions of the program, @@ -344,7 +344,7 @@ func newProgramInfoFromFd(fd *sys.FD) (*ProgramInfo, error) { if info.NrJitedKsyms > 0 { pi.jitedInfo.numKsyms = info.NrJitedKsyms - pi.jitedInfo.ksyms = make([]uintptr, info.NrJitedKsyms) + pi.jitedInfo.ksyms = make([]uint64, info.NrJitedKsyms) info2.JitedKsyms = sys.NewSlicePointer(pi.jitedInfo.ksyms) info2.NrJitedKsyms = info.NrJitedKsyms makeSecondCall = true @@ -630,8 +630,25 @@ func (pi *ProgramInfo) VerifiedInstructions() (uint32, bool) { // programs without subprograms (bpf2bpf calls). // // The bool return value indicates whether this optional field is available. +// +// When a kernel address can't fit into uintptr (which is usually the case when +// running 32 bit program on a 64 bit kernel), this returns an empty slice and +// a false. func (pi *ProgramInfo) JitedKsymAddrs() ([]uintptr, bool) { - return pi.jitedInfo.ksyms, len(pi.jitedInfo.ksyms) > 0 + ksyms := make([]uintptr, 0, len(pi.jitedInfo.ksyms)) + if cap(ksyms) == 0 { + return ksyms, false + } + // Check if a kernel address fits into uintptr (it might not when + // using a 32 bit binary on a 64 bit kernel). This check should work + // with any kernel address, since they have 1s at the highest bits. + if a := pi.jitedInfo.ksyms[0]; uint64(uintptr(a)) != a { + return nil, false + } + for _, ksym := range pi.jitedInfo.ksyms { + ksyms = append(ksyms, uintptr(ksym)) + } + return ksyms, true } // JitedInsns returns the JITed machine native instructions of the program. diff --git a/vendor/github.com/cilium/ebpf/internal/feature.go b/vendor/github.com/cilium/ebpf/internal/feature.go index 6399be0851..6f64822e11 100644 --- a/vendor/github.com/cilium/ebpf/internal/feature.go +++ b/vendor/github.com/cilium/ebpf/internal/feature.go @@ -93,7 +93,7 @@ func NewFeatureTest(name string, fn FeatureTestFn, versions ...string) func() er break } - if runtime.GOOS == "linux" && !strings.ContainsRune(version, ':') { + if OnLinux && !strings.ContainsRune(version, ':') { // Allow version numbers without a GOOS prefix on Linux. ft.Version = version break diff --git a/vendor/github.com/cilium/ebpf/internal/goos.go b/vendor/github.com/cilium/ebpf/internal/goos.go new file mode 100644 index 0000000000..0569d5ce0b --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/goos.go @@ -0,0 +1,7 @@ +package internal + +import "runtime" + +const ( + OnLinux = runtime.GOOS == "linux" +) diff --git a/vendor/github.com/cilium/ebpf/internal/kallsyms/kallsyms.go b/vendor/github.com/cilium/ebpf/internal/kallsyms/kallsyms.go index f93d785849..2cdfb56a58 100644 --- a/vendor/github.com/cilium/ebpf/internal/kallsyms/kallsyms.go +++ b/vendor/github.com/cilium/ebpf/internal/kallsyms/kallsyms.go @@ -8,6 +8,8 @@ import ( "slices" "strconv" "strings" + + "github.com/cilium/ebpf/internal" ) var errAmbiguousKsym = errors.New("multiple kernel symbols with the same name") @@ -47,6 +49,10 @@ func Module(name string) (string, error) { // Any symbols missing in the kernel are ignored. Returns an error if multiple // symbols with a given name were found. func AssignModules(symbols map[string]string) error { + if !internal.OnLinux { + return fmt.Errorf("read /proc/kallsyms: %w", internal.ErrNotSupportedOnOS) + } + if len(symbols) == 0 { return nil } @@ -71,6 +77,7 @@ func AssignModules(symbols map[string]string) error { if err != nil { return err } + defer f.Close() if err := assignModules(f, request); err != nil { return fmt.Errorf("assigning symbol modules: %w", err) @@ -161,6 +168,10 @@ func Address(symbol string) (uint64, error) { // Any symbols missing in the kernel are ignored. Returns an error if multiple // addresses were found for a symbol. func AssignAddresses(symbols map[string]uint64) error { + if !internal.OnLinux { + return fmt.Errorf("read /proc/kallsyms: %w", internal.ErrNotSupportedOnOS) + } + if len(symbols) == 0 { return nil } @@ -185,6 +196,7 @@ func AssignAddresses(symbols map[string]uint64) error { if err != nil { return err } + defer f.Close() if err := assignAddresses(f, request); err != nil { return fmt.Errorf("loading symbol addresses: %w", err) diff --git a/vendor/github.com/cilium/ebpf/internal/linux/auxv.go b/vendor/github.com/cilium/ebpf/internal/linux/auxv.go index 98bb5d83ca..10508fd8fc 100644 --- a/vendor/github.com/cilium/ebpf/internal/linux/auxv.go +++ b/vendor/github.com/cilium/ebpf/internal/linux/auxv.go @@ -1,9 +1,11 @@ package linux import ( - "errors" + "fmt" "io" - _ "unsafe" + + "github.com/cilium/ebpf/internal" + "github.com/cilium/ebpf/internal/unix" ) type auxvPairReader interface { @@ -17,11 +19,8 @@ const ( _AT_SYSINFO_EHDR = 33 // Offset to vDSO blob in process image ) -//go:linkname runtime_getAuxv runtime.getAuxv -func runtime_getAuxv() []uintptr - type auxvRuntimeReader struct { - data []uintptr + data [][2]uintptr index int } @@ -37,20 +36,23 @@ func (r *auxvRuntimeReader) ReadAuxvPair() (uint64, uint64, error) { // we manually add the (_AT_NULL, _AT_NULL) pair at the end // that is not provided by the go runtime var tag, value uintptr - if r.index+1 < len(r.data) { - tag, value = r.data[r.index], r.data[r.index+1] + if r.index < len(r.data) { + tag, value = r.data[r.index][0], r.data[r.index][1] } else { tag, value = _AT_NULL, _AT_NULL } - r.index += 2 + r.index += 1 return uint64(tag), uint64(value), nil } func newAuxvRuntimeReader() (auxvPairReader, error) { - data := runtime_getAuxv() + if !internal.OnLinux { + return nil, fmt.Errorf("read auxv from runtime: %w", internal.ErrNotSupportedOnOS) + } - if len(data)%2 != 0 { - return nil, errors.New("malformed auxv passed from runtime") + data, err := unix.Auxv() + if err != nil { + return nil, fmt.Errorf("read auxv from runtime: %w", err) } return &auxvRuntimeReader{ diff --git a/vendor/github.com/cilium/ebpf/internal/sys/syscall.go b/vendor/github.com/cilium/ebpf/internal/sys/syscall.go index e37f4cf671..4fdb74c57a 100644 --- a/vendor/github.com/cilium/ebpf/internal/sys/syscall.go +++ b/vendor/github.com/cilium/ebpf/internal/sys/syscall.go @@ -2,7 +2,6 @@ package sys import ( "runtime" - "syscall" "unsafe" "github.com/cilium/ebpf/internal/unix" @@ -11,7 +10,7 @@ import ( // ENOTSUPP is a Linux internal error code that has leaked into UAPI. // // It is not the same as ENOTSUP or EOPNOTSUPP. -const ENOTSUPP = syscall.Errno(524) +const ENOTSUPP = unix.Errno(524) // BPF wraps SYS_BPF. // @@ -179,12 +178,12 @@ const ( const BPF_TAG_SIZE = 8 const BPF_OBJ_NAME_LEN = 16 -// wrappedErrno wraps syscall.Errno to prevent direct comparisons with +// wrappedErrno wraps [unix.Errno] to prevent direct comparisons with // syscall.E* or unix.E* constants. // // You should never export an error of this type. type wrappedErrno struct { - syscall.Errno + unix.Errno } func (we wrappedErrno) Unwrap() error { @@ -200,10 +199,10 @@ func (we wrappedErrno) Error() string { type syscallError struct { error - errno syscall.Errno + errno unix.Errno } -func Error(err error, errno syscall.Errno) error { +func Error(err error, errno unix.Errno) error { return &syscallError{err, errno} } diff --git a/vendor/github.com/cilium/ebpf/internal/sys/types.go b/vendor/github.com/cilium/ebpf/internal/sys/types.go index 88001c319e..82f3492a83 100644 --- a/vendor/github.com/cilium/ebpf/internal/sys/types.go +++ b/vendor/github.com/cilium/ebpf/internal/sys/types.go @@ -26,6 +26,7 @@ const ( BPF_FIB_LKUP_RET_UNREACHABLE = 2 BPF_FIB_LKUP_RET_UNSUPP_LWT = 6 BPF_FIB_LOOKUP_DIRECT = 1 + BPF_FIB_LOOKUP_MARK = 32 BPF_FIB_LOOKUP_OUTPUT = 2 BPF_FIB_LOOKUP_SKIP_NEIGH = 4 BPF_FIB_LOOKUP_SRC = 16 @@ -68,6 +69,7 @@ const ( BPF_F_NO_COMMON_LRU = 2 BPF_F_NO_PREALLOC = 1 BPF_F_NO_TUNNEL_KEY = 16 + BPF_F_NO_USER_CONV = 262144 BPF_F_NUMA_NODE = 4 BPF_F_PATH_FD = 16384 BPF_F_PEER = 4 @@ -77,17 +79,20 @@ const ( BPF_F_RDONLY_PROG = 128 BPF_F_RECOMPUTE_CSUM = 1 BPF_F_REUSE_STACKID = 1024 + BPF_F_SEGV_ON_FAULT = 131072 BPF_F_SEQ_NUMBER = 8 BPF_F_SKIP_FIELD_MASK = 255 BPF_F_STACK_BUILD_ID = 32 BPF_F_SYSCTL_BASE_NAME = 1 BPF_F_TIMER_ABS = 1 BPF_F_TIMER_CPU_PIN = 2 + BPF_F_TOKEN_FD = 65536 BPF_F_TUNINFO_FLAGS = 16 BPF_F_TUNINFO_IPV6 = 1 BPF_F_UPROBE_MULTI_RETURN = 1 BPF_F_USER_BUILD_ID = 2048 BPF_F_USER_STACK = 256 + BPF_F_VTYPE_BTF_OBJ_FD = 32768 BPF_F_WRONLY = 16 BPF_F_WRONLY_PROG = 256 BPF_F_ZERO_CSUM_TX = 2 @@ -117,6 +122,9 @@ const ( BPF_RINGBUF_BUSY_BIT = 2147483648 BPF_RINGBUF_DISCARD_BIT = 1073741824 BPF_RINGBUF_HDR_SZ = 8 + BPF_SKB_CLOCK_MONOTONIC = 1 + BPF_SKB_CLOCK_REALTIME = 0 + BPF_SKB_CLOCK_TAI = 2 BPF_SKB_TSTAMP_DELIVERY_MONO = 1 BPF_SKB_TSTAMP_UNSPEC = 0 BPF_SK_LOOKUP_F_NO_REUSEPORT = 2 @@ -146,8 +154,6 @@ const ( BPF_SOCK_OPS_VOID = 0 BPF_SOCK_OPS_WRITE_HDR_OPT_CB = 15 BPF_SOCK_OPS_WRITE_HDR_OPT_CB_FLAG = 64 - BPF_STRUCT_OPS_TYPE_bpf_dummy_ops = 0 - BPF_STRUCT_OPS_TYPE_tcp_congestion_ops = 1 BPF_TASK_ITER_ALL_PROCS = 0 BPF_TASK_ITER_ALL_THREADS = 1 BPF_TASK_ITER_PROC_THREADS = 2 @@ -236,7 +242,8 @@ const ( BPF_CGROUP_UNIX_GETSOCKNAME AttachType = 53 BPF_NETKIT_PRIMARY AttachType = 54 BPF_NETKIT_PEER AttachType = 55 - __MAX_BPF_ATTACH_TYPE AttachType = 56 + BPF_TRACE_KPROBE_SESSION AttachType = 56 + __MAX_BPF_ATTACH_TYPE AttachType = 57 ) type Cmd uint32 @@ -279,6 +286,8 @@ const ( BPF_ITER_CREATE Cmd = 33 BPF_LINK_DETACH Cmd = 34 BPF_PROG_BIND_MAP Cmd = 35 + BPF_TOKEN_CREATE Cmd = 36 + __MAX_BPF_CMD Cmd = 37 ) type FunctionId uint32 @@ -523,7 +532,8 @@ const ( BPF_LINK_TYPE_TCX LinkType = 11 BPF_LINK_TYPE_UPROBE_MULTI LinkType = 12 BPF_LINK_TYPE_NETKIT LinkType = 13 - __MAX_BPF_LINK_TYPE LinkType = 14 + BPF_LINK_TYPE_SOCKMAP LinkType = 14 + __MAX_BPF_LINK_TYPE LinkType = 15 ) type MapType uint32 @@ -564,6 +574,8 @@ const ( BPF_MAP_TYPE_BLOOM_FILTER MapType = 30 BPF_MAP_TYPE_USER_RINGBUF MapType = 31 BPF_MAP_TYPE_CGRP_STORAGE MapType = 32 + BPF_MAP_TYPE_ARENA MapType = 33 + __MAX_BPF_MAP_TYPE MapType = 34 ) type ObjType uint32 @@ -623,6 +635,7 @@ const ( BPF_PROG_TYPE_SK_LOOKUP ProgType = 30 BPF_PROG_TYPE_SYSCALL ProgType = 31 BPF_PROG_TYPE_NETFILTER ProgType = 32 + __MAX_BPF_PROG_TYPE ProgType = 33 ) type RetCode uint32 @@ -719,7 +732,7 @@ type MapInfo struct { BtfId uint32 BtfKeyTypeId TypeID BtfValueTypeId TypeID - _ [4]byte + BtfVmlinuxId uint32 MapExtra uint64 } @@ -816,6 +829,8 @@ type BtfLoadAttr struct { BtfLogSize uint32 BtfLogLevel uint32 BtfLogTrueSize uint32 + BtfFlags uint32 + BtfTokenFd int32 } func BtfLoad(attr *BtfLoadAttr) (*FD, error) { @@ -1069,6 +1084,8 @@ type MapCreateAttr struct { BtfValueTypeId TypeID BtfVmlinuxValueTypeId TypeID MapExtra uint64 + ValueTypeBtfObjFd int32 + MapTokenFd int32 } func MapCreate(attr *MapCreateAttr) (*FD, error) { @@ -1362,6 +1379,8 @@ type ProgLoadAttr struct { CoreRelos Pointer CoreReloRecSize uint32 LogTrueSize uint32 + ProgTokenFd int32 + _ [4]byte } func ProgLoad(attr *ProgLoadAttr) (*FD, error) { @@ -1419,6 +1438,7 @@ type RawTracepointOpenAttr struct { Name Pointer ProgFd uint32 _ [4]byte + Cookie uint64 } func RawTracepointOpen(attr *RawTracepointOpenAttr) (*FD, error) { @@ -1460,19 +1480,20 @@ type KprobeLinkInfo struct { Offset uint32 Addr uint64 Missed uint64 - _ [8]byte + Cookie uint64 } type KprobeMultiLinkInfo struct { - Type LinkType - Id LinkID - ProgId uint32 - _ [4]byte - Addrs Pointer - Count uint32 - Flags uint32 - Missed uint64 - _ [24]byte + Type LinkType + Id LinkID + ProgId uint32 + _ [4]byte + Addrs Pointer + Count uint32 + Flags uint32 + Missed uint64 + Cookies uint64 + _ [16]byte } type NetNsLinkInfo struct { diff --git a/vendor/github.com/cilium/ebpf/internal/tracefs/kprobe.go b/vendor/github.com/cilium/ebpf/internal/tracefs/kprobe.go index 062bef9ec3..bdadf4d8ea 100644 --- a/vendor/github.com/cilium/ebpf/internal/tracefs/kprobe.go +++ b/vendor/github.com/cilium/ebpf/internal/tracefs/kprobe.go @@ -113,6 +113,10 @@ func sanitizeTracefsPath(path ...string) (string, error) { // but may be also be available at /sys/kernel/debug/tracing if debugfs is mounted. // The available tracefs paths will depends on distribution choices. var getTracefsPath = sync.OnceValues(func() (string, error) { + if !internal.OnLinux { + return "", fmt.Errorf("tracefs: %w", internal.ErrNotSupportedOnOS) + } + for _, p := range []struct { path string fsType int64 diff --git a/vendor/github.com/cilium/ebpf/internal/unix/errno_linux.go b/vendor/github.com/cilium/ebpf/internal/unix/errno_linux.go new file mode 100644 index 0000000000..0c4886bd13 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/unix/errno_linux.go @@ -0,0 +1,29 @@ +package unix + +import ( + "syscall" + + linux "golang.org/x/sys/unix" +) + +type Errno = syscall.Errno + +const ( + E2BIG = linux.E2BIG + EACCES = linux.EACCES + EAGAIN = linux.EAGAIN + EBADF = linux.EBADF + EEXIST = linux.EEXIST + EFAULT = linux.EFAULT + EILSEQ = linux.EILSEQ + EINTR = linux.EINTR + EINVAL = linux.EINVAL + ENODEV = linux.ENODEV + ENOENT = linux.ENOENT + ENOSPC = linux.ENOSPC + EOPNOTSUPP = linux.EOPNOTSUPP + EPERM = linux.EPERM + EPOLLIN = linux.EPOLLIN + ESRCH = linux.ESRCH + ESTALE = linux.ESTALE +) diff --git a/vendor/github.com/cilium/ebpf/internal/unix/errno_other.go b/vendor/github.com/cilium/ebpf/internal/unix/errno_other.go new file mode 100644 index 0000000000..fc2b042b54 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/unix/errno_other.go @@ -0,0 +1,29 @@ +//go:build !linux && !windows + +package unix + +import "syscall" + +type Errno = syscall.Errno + +// Errnos are distinct and non-zero. +const ( + E2BIG Errno = iota + 1 + EACCES + EAGAIN + EBADF + EEXIST + EFAULT + EILSEQ + EINTR + EINVAL + ENODEV + ENOENT + ENOSPC + ENOTSUP + ENOTSUPP + EOPNOTSUPP + EPERM + ESRCH + ESTALE +) diff --git a/vendor/github.com/cilium/ebpf/internal/unix/errno_string_windows.go b/vendor/github.com/cilium/ebpf/internal/unix/errno_string_windows.go new file mode 100644 index 0000000000..6077e983f3 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/unix/errno_string_windows.go @@ -0,0 +1,59 @@ +// Code generated by "stringer -type=Errno -tags=windows -output=errno_string_windows.go"; DO NOT EDIT. + +package unix + +import "strconv" + +func _() { + // An "invalid array index" compiler error signifies that the constant values have changed. + // Re-run the stringer command to generate them again. + var x [1]struct{} + _ = x[EPERM-1] + _ = x[ENOENT-2] + _ = x[ESRCH-3] + _ = x[EINTR-4] + _ = x[E2BIG-7] + _ = x[EBADF-9] + _ = x[EAGAIN-11] + _ = x[EACCES-13] + _ = x[EFAULT-14] + _ = x[EEXIST-17] + _ = x[ENODEV-19] + _ = x[EINVAL-22] + _ = x[ENOSPC-28] + _ = x[EILSEQ-42] + _ = x[ENOTSUP-129] + _ = x[EOPNOTSUPP-130] + _ = x[ENOTSUPP-536870912] + _ = x[ESTALE-536870913] +} + +const _Errno_name = "EPERMENOENTESRCHEINTRE2BIGEBADFEAGAINEACCESEFAULTEEXISTENODEVEINVALENOSPCEILSEQENOTSUPEOPNOTSUPPENOTSUPPESTALE" + +var _Errno_map = map[Errno]string{ + 1: _Errno_name[0:5], + 2: _Errno_name[5:11], + 3: _Errno_name[11:16], + 4: _Errno_name[16:21], + 7: _Errno_name[21:26], + 9: _Errno_name[26:31], + 11: _Errno_name[31:37], + 13: _Errno_name[37:43], + 14: _Errno_name[43:49], + 17: _Errno_name[49:55], + 19: _Errno_name[55:61], + 22: _Errno_name[61:67], + 28: _Errno_name[67:73], + 42: _Errno_name[73:79], + 129: _Errno_name[79:86], + 130: _Errno_name[86:96], + 536870912: _Errno_name[96:104], + 536870913: _Errno_name[104:110], +} + +func (i Errno) String() string { + if str, ok := _Errno_map[i]; ok { + return str + } + return "Errno(" + strconv.FormatInt(int64(i), 10) + ")" +} diff --git a/vendor/github.com/cilium/ebpf/internal/unix/errno_windows.go b/vendor/github.com/cilium/ebpf/internal/unix/errno_windows.go new file mode 100644 index 0000000000..7500cd6d4e --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/unix/errno_windows.go @@ -0,0 +1,78 @@ +package unix + +// The code in this file is derived from syscall_unix.go in the Go source code, +// licensed under the MIT license. + +import ( + "errors" + "os" + "syscall" +) + +//go:generate go run golang.org/x/tools/cmd/stringer@latest -type=Errno -tags=windows -output=errno_string_windows.go + +// Windows specific constants for Unix errnos. +// +// The values do not always match Linux, for example EILSEQ and EOPNOTSUPP. +// +// See https://learn.microsoft.com/en-us/cpp/c-runtime-library/errno-constants?view=msvc-170 +const ( + EPERM Errno = 1 + ENOENT Errno = 2 + ESRCH Errno = 3 + EINTR Errno = 4 + E2BIG Errno = 7 + EBADF Errno = 9 + EAGAIN Errno = 11 + EACCES Errno = 13 + EFAULT Errno = 14 + EEXIST Errno = 17 + ENODEV Errno = 19 + EINVAL Errno = 22 + ENFILE Errno = 23 + EMFILE Errno = 24 + ENOSPC Errno = 28 + ENOSYS Errno = 40 + ENOTEMPTY Errno = 41 + EILSEQ Errno = 42 + ENOTSUP Errno = 129 + EOPNOTSUPP Errno = 130 + ETIMEDOUT Errno = 138 + EWOULDBLOCK Errno = 140 +) + +// These constants do not exist on Windows and therefore have a non-zero +// dummy value. +const ( + ENOTSUPP Errno = Errno(syscall.APPLICATION_ERROR) + iota + ESTALE +) + +// Errno is a Windows compatibility shim for Unix errnos. +type Errno uintptr + +func (e Errno) Error() string { + return e.String() +} + +func (e Errno) Is(target error) bool { + switch target { + case os.ErrPermission: + return e == EACCES || e == EPERM + case os.ErrExist: + return e == EEXIST || e == ENOTEMPTY + case os.ErrNotExist: + return e == ENOENT + case errors.ErrUnsupported: + return e == ENOSYS || e == ENOTSUP || e == EOPNOTSUPP + } + return false +} + +func (e Errno) Temporary() bool { + return e == EINTR || e == EMFILE || e == ENFILE || e.Timeout() +} + +func (e Errno) Timeout() bool { + return e == EAGAIN || e == EWOULDBLOCK || e == ETIMEDOUT +} diff --git a/vendor/github.com/cilium/ebpf/internal/unix/error.go b/vendor/github.com/cilium/ebpf/internal/unix/error.go new file mode 100644 index 0000000000..48017c1009 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/unix/error.go @@ -0,0 +1,23 @@ +package unix + +import ( + "fmt" + "runtime" + "strings" + + "github.com/cilium/ebpf/internal" +) + +// errNonLinux returns an error which wraps [internal.ErrNotSupportedOnOS] and +// includes the name of the calling function. +func errNonLinux() error { + name := "unknown" + pc, _, _, ok := runtime.Caller(1) + if ok { + name = runtime.FuncForPC(pc).Name() + if pos := strings.LastIndexByte(name, '.'); pos != -1 { + name = name[pos+1:] + } + } + return fmt.Errorf("unix: %s: %w", name, internal.ErrNotSupportedOnOS) +} diff --git a/vendor/github.com/cilium/ebpf/internal/unix/strings_other.go b/vendor/github.com/cilium/ebpf/internal/unix/strings_other.go new file mode 100644 index 0000000000..49e1fe2172 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/unix/strings_other.go @@ -0,0 +1,11 @@ +//go:build !linux && !windows + +package unix + +func BytePtrFromString(s string) (*byte, error) { + return nil, errNonLinux() +} + +func ByteSliceToString(s []byte) string { + return "" +} diff --git a/vendor/github.com/cilium/ebpf/internal/unix/strings_windows.go b/vendor/github.com/cilium/ebpf/internal/unix/strings_windows.go new file mode 100644 index 0000000000..7e3d4da777 --- /dev/null +++ b/vendor/github.com/cilium/ebpf/internal/unix/strings_windows.go @@ -0,0 +1,19 @@ +package unix + +import ( + "syscall" + + "golang.org/x/sys/windows" +) + +func BytePtrFromString(s string) (*byte, error) { + p, err := windows.BytePtrFromString(s) + if err == syscall.EINVAL { + err = EINVAL + } + return p, err +} + +func ByteSliceToString(s []byte) string { + return windows.ByteSliceToString(s) +} diff --git a/vendor/github.com/cilium/ebpf/internal/unix/types_linux.go b/vendor/github.com/cilium/ebpf/internal/unix/types_linux.go index 144e608d1c..385adf08b8 100644 --- a/vendor/github.com/cilium/ebpf/internal/unix/types_linux.go +++ b/vendor/github.com/cilium/ebpf/internal/unix/types_linux.go @@ -8,26 +8,6 @@ import ( linux "golang.org/x/sys/unix" ) -const ( - ENOENT = linux.ENOENT - EEXIST = linux.EEXIST - EAGAIN = linux.EAGAIN - ENOSPC = linux.ENOSPC - EINVAL = linux.EINVAL - EPOLLIN = linux.EPOLLIN - EINTR = linux.EINTR - EPERM = linux.EPERM - ESRCH = linux.ESRCH - ENODEV = linux.ENODEV - EBADF = linux.EBADF - E2BIG = linux.E2BIG - EFAULT = linux.EFAULT - EACCES = linux.EACCES - EILSEQ = linux.EILSEQ - EOPNOTSUPP = linux.EOPNOTSUPP - ESTALE = linux.ESTALE -) - const ( BPF_F_NO_PREALLOC = linux.BPF_F_NO_PREALLOC BPF_F_NUMA_NODE = linux.BPF_F_NUMA_NODE @@ -215,3 +195,7 @@ func SchedSetaffinity(pid int, set *CPUSet) error { func SchedGetaffinity(pid int, set *CPUSet) error { return linux.SchedGetaffinity(pid, set) } + +func Auxv() ([][2]uintptr, error) { + return linux.Auxv() +} diff --git a/vendor/github.com/cilium/ebpf/internal/unix/types_other.go b/vendor/github.com/cilium/ebpf/internal/unix/types_other.go index 06cc3a0966..323f7ff34e 100644 --- a/vendor/github.com/cilium/ebpf/internal/unix/types_other.go +++ b/vendor/github.com/cilium/ebpf/internal/unix/types_other.go @@ -3,33 +3,9 @@ package unix import ( - "fmt" - "runtime" "syscall" ) -var errNonLinux = fmt.Errorf("unsupported platform %s/%s", runtime.GOOS, runtime.GOARCH) - -// Errnos are distinct and non-zero. -const ( - ENOENT syscall.Errno = iota + 1 - EEXIST - EAGAIN - ENOSPC - EINVAL - EINTR - EPERM - ESRCH - ENODEV - EBADF - E2BIG - EFAULT - EACCES - EILSEQ - EOPNOTSUPP - ESTALE -) - // Constants are distinct to avoid breaking switch statements. const ( BPF_F_NO_PREALLOC = iota @@ -137,28 +113,28 @@ type Sigset_t struct { Val [4]uint64 } -func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno) { - return 0, 0, syscall.ENOTSUP +func Syscall(trap, a1, a2, a3 uintptr) (r1, r2 uintptr, err Errno) { + return 0, 0, ENOTSUP } func PthreadSigmask(how int, set, oldset *Sigset_t) error { - return errNonLinux + return errNonLinux() } func FcntlInt(fd uintptr, cmd, arg int) (int, error) { - return -1, errNonLinux + return -1, errNonLinux() } func IoctlSetInt(fd int, req uint, value int) error { - return errNonLinux + return errNonLinux() } func Statfs(path string, buf *Statfs_t) error { - return errNonLinux + return errNonLinux() } func Close(fd int) (err error) { - return errNonLinux + return errNonLinux() } type EpollEvent struct { @@ -168,23 +144,23 @@ type EpollEvent struct { } func EpollWait(epfd int, events []EpollEvent, msec int) (n int, err error) { - return 0, errNonLinux + return 0, errNonLinux() } func EpollCtl(epfd int, op int, fd int, event *EpollEvent) (err error) { - return errNonLinux + return errNonLinux() } func Eventfd(initval uint, flags int) (fd int, err error) { - return 0, errNonLinux + return 0, errNonLinux() } func Write(fd int, p []byte) (n int, err error) { - return 0, errNonLinux + return 0, errNonLinux() } func EpollCreate1(flag int) (fd int, err error) { - return 0, errNonLinux + return 0, errNonLinux() } type PerfEventMmapPage struct { @@ -214,15 +190,15 @@ type PerfEventMmapPage struct { } func SetNonblock(fd int, nonblocking bool) (err error) { - return errNonLinux + return errNonLinux() } func Mmap(fd int, offset int64, length int, prot int, flags int) (data []byte, err error) { - return []byte{}, errNonLinux + return []byte{}, errNonLinux() } func Munmap(b []byte) (err error) { - return errNonLinux + return errNonLinux() } type PerfEventAttr struct { @@ -247,7 +223,7 @@ type PerfEventAttr struct { } func PerfEventOpen(attr *PerfEventAttr, pid int, cpu int, groupFd int, flags int) (fd int, err error) { - return 0, errNonLinux + return 0, errNonLinux() } type Utsname struct { @@ -256,7 +232,7 @@ type Utsname struct { } func Uname(buf *Utsname) (err error) { - return errNonLinux + return errNonLinux() } func Getpid() int { @@ -268,35 +244,27 @@ func Gettid() int { } func Tgkill(tgid int, tid int, sig syscall.Signal) (err error) { - return errNonLinux -} - -func BytePtrFromString(s string) (*byte, error) { - return nil, errNonLinux -} - -func ByteSliceToString(s []byte) string { - return "" + return errNonLinux() } func Renameat2(olddirfd int, oldpath string, newdirfd int, newpath string, flags uint) error { - return errNonLinux + return errNonLinux() } func Prlimit(pid, resource int, new, old *Rlimit) error { - return errNonLinux + return errNonLinux() } func Open(path string, mode int, perm uint32) (int, error) { - return -1, errNonLinux + return -1, errNonLinux() } func Fstat(fd int, stat *Stat_t) error { - return errNonLinux + return errNonLinux() } func SetsockoptInt(fd, level, opt, value int) error { - return errNonLinux + return errNonLinux() } type CPUSet struct{} @@ -304,9 +272,13 @@ type CPUSet struct{} func (*CPUSet) Set(int) {} func SchedSetaffinity(pid int, set *CPUSet) error { - return errNonLinux + return errNonLinux() } func SchedGetaffinity(pid int, set *CPUSet) error { - return errNonLinux + return errNonLinux() +} + +func Auxv() ([][2]uintptr, error) { + return nil, errNonLinux() } diff --git a/vendor/github.com/cilium/ebpf/link/kprobe_multi.go b/vendor/github.com/cilium/ebpf/link/kprobe_multi.go index 094cb0538c..f19f9f4c73 100644 --- a/vendor/github.com/cilium/ebpf/link/kprobe_multi.go +++ b/vendor/github.com/cilium/ebpf/link/kprobe_multi.go @@ -37,6 +37,14 @@ type KprobeMultiOptions struct { // Each Cookie is assigned to the Symbol or Address specified at the // corresponding slice index. Cookies []uint64 + + // Session must be true when attaching Programs with the + // [ebpf.AttachTraceKprobeSession] attach type. + // + // This makes a Kprobe execute on both function entry and return. The entry + // program can share a cookie value with the return program and can decide + // whether the return program gets executed. + Session bool } // KprobeMulti attaches the given eBPF program to the entry point of a given set @@ -82,9 +90,14 @@ func kprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions, flags uint32) (Lin return nil, fmt.Errorf("Cookies must be exactly Symbols or Addresses in length: %w", errInvalidInput) } + attachType := sys.BPF_TRACE_KPROBE_MULTI + if opts.Session { + attachType = sys.BPF_TRACE_KPROBE_SESSION + } + attr := &sys.LinkCreateKprobeMultiAttr{ ProgFd: uint32(prog.FD()), - AttachType: sys.BPF_TRACE_KPROBE_MULTI, + AttachType: attachType, KprobeMultiFlags: flags, } @@ -103,21 +116,31 @@ func kprobeMulti(prog *ebpf.Program, opts KprobeMultiOptions, flags uint32) (Lin } fd, err := sys.LinkCreateKprobeMulti(attr) + if err == nil { + return &kprobeMultiLink{RawLink{fd, ""}}, nil + } + if errors.Is(err, unix.ESRCH) { return nil, fmt.Errorf("couldn't find one or more symbols: %w", os.ErrNotExist) } - if errors.Is(err, unix.EINVAL) { - return nil, fmt.Errorf("%w (missing kernel symbol or prog's AttachType not AttachTraceKprobeMulti?)", err) - } - if err != nil { + if opts.Session { + if haveFeatErr := haveBPFLinkKprobeSession(); haveFeatErr != nil { + return nil, haveFeatErr + } + } else { if haveFeatErr := haveBPFLinkKprobeMulti(); haveFeatErr != nil { return nil, haveFeatErr } - return nil, err } - return &kprobeMultiLink{RawLink{fd, ""}}, nil + // Check EINVAL after running feature probes, since it's also returned when + // the kernel doesn't support the multi/session attach types. + if errors.Is(err, unix.EINVAL) { + return nil, fmt.Errorf("%w (missing kernel symbol or prog's AttachType not %s?)", err, ebpf.AttachType(attachType)) + } + + return nil, err } type kprobeMultiLink struct { @@ -189,3 +212,44 @@ var haveBPFLinkKprobeMulti = internal.NewFeatureTest("bpf_link_kprobe_multi", fu return nil }, "5.18") + +var haveBPFLinkKprobeSession = internal.NewFeatureTest("bpf_link_kprobe_session", func() error { + prog, err := ebpf.NewProgram(&ebpf.ProgramSpec{ + Name: "probe_kps_link", + Type: ebpf.Kprobe, + Instructions: asm.Instructions{ + asm.Mov.Imm(asm.R0, 0), + asm.Return(), + }, + AttachType: ebpf.AttachTraceKprobeSession, + License: "MIT", + }) + if errors.Is(err, unix.E2BIG) { + // Kernel doesn't support AttachType field. + return internal.ErrNotSupported + } + if err != nil { + return err + } + defer prog.Close() + + fd, err := sys.LinkCreateKprobeMulti(&sys.LinkCreateKprobeMultiAttr{ + ProgFd: uint32(prog.FD()), + AttachType: sys.BPF_TRACE_KPROBE_SESSION, + Count: 1, + Syms: sys.NewStringSlicePointer([]string{"vprintk"}), + }) + switch { + case errors.Is(err, unix.EINVAL): + return internal.ErrNotSupported + // If CONFIG_FPROBE isn't set. + case errors.Is(err, unix.EOPNOTSUPP): + return internal.ErrNotSupported + case err != nil: + return err + } + + fd.Close() + + return nil +}, "6.10") diff --git a/vendor/github.com/cilium/ebpf/linker.go b/vendor/github.com/cilium/ebpf/linker.go index 6f97af2784..024b72bbc8 100644 --- a/vendor/github.com/cilium/ebpf/linker.go +++ b/vendor/github.com/cilium/ebpf/linker.go @@ -207,13 +207,19 @@ func flattenPrograms(progs map[string]*ProgramSpec, names []string) { // dependencies of each program. func flattenInstructions(name string, progs map[string]*ProgramSpec, refs map[*ProgramSpec][]string) asm.Instructions { prog := progs[name] + progRefs := refs[prog] + + if len(progRefs) == 0 { + // No references, nothing to do. + return prog.Instructions + } insns := make(asm.Instructions, len(prog.Instructions)) copy(insns, prog.Instructions) // Add all direct references of prog to the list of to be linked programs. - pending := make([]string, len(refs[prog])) - copy(pending, refs[prog]) + pending := make([]string, len(progRefs)) + copy(pending, progRefs) // All references for which we've appended instructions. linked := make(map[string]bool) diff --git a/vendor/github.com/cilium/ebpf/netlify.toml b/vendor/github.com/cilium/ebpf/netlify.toml index 67c83f3b30..764c3b447b 100644 --- a/vendor/github.com/cilium/ebpf/netlify.toml +++ b/vendor/github.com/cilium/ebpf/netlify.toml @@ -2,3 +2,4 @@ base = "docs/" publish = "site/" command = "mkdocs build" + environment = { PYTHON_VERSION = "3.13" } diff --git a/vendor/github.com/cilium/ebpf/prog.go b/vendor/github.com/cilium/ebpf/prog.go index 4f3ce43bfa..8fcae6a3fb 100644 --- a/vendor/github.com/cilium/ebpf/prog.go +++ b/vendor/github.com/cilium/ebpf/prog.go @@ -169,7 +169,7 @@ func (ps *ProgramSpec) Tag() (string, error) { // ProgramSpec.AttachTo, if any. Returns an empty string if the symbol is not // present or not part of a kernel module. func (ps *ProgramSpec) kernelModule() (string, error) { - if ps.AttachTo == "" && ps.targetsKernelModule() { + if ps.targetsKernelModule() { return kallsyms.Module(ps.AttachTo) } diff --git a/vendor/github.com/cilium/ebpf/types.go b/vendor/github.com/cilium/ebpf/types.go index 211b308bbc..d7fb00509d 100644 --- a/vendor/github.com/cilium/ebpf/types.go +++ b/vendor/github.com/cilium/ebpf/types.go @@ -236,6 +236,7 @@ const ( AttachSkReuseportSelectOrMigrate = AttachType(sys.BPF_SK_REUSEPORT_SELECT_OR_MIGRATE) AttachPerfEvent = AttachType(sys.BPF_PERF_EVENT) AttachTraceKprobeMulti = AttachType(sys.BPF_TRACE_KPROBE_MULTI) + AttachTraceKprobeSession = AttachType(sys.BPF_TRACE_KPROBE_SESSION) AttachLSMCgroup = AttachType(sys.BPF_LSM_CGROUP) AttachStructOps = AttachType(sys.BPF_STRUCT_OPS) AttachNetfilter = AttachType(sys.BPF_NETFILTER) diff --git a/vendor/github.com/cilium/hive/cell/invoke.go b/vendor/github.com/cilium/hive/cell/invoke.go index 739b5b62f4..7998242e6d 100644 --- a/vendor/github.com/cilium/hive/cell/invoke.go +++ b/vendor/github.com/cilium/hive/cell/invoke.go @@ -49,7 +49,7 @@ func (inv *invoker) invoke(log *slog.Logger, cont container, logThreshold time.D defer inv.funcs[i].infoMu.Unlock() if err := cont.Invoke(nf.fn, opts...); err != nil { - log.Error("Invoke failed", "error", err, "function", nf.name) + log.Error("Invoke failed", "error", dig.RootCause(err), "function", nf.name) return err } d := time.Since(t0) diff --git a/vendor/github.com/cilium/hive/command.go b/vendor/github.com/cilium/hive/command.go index b398e77539..a79de9788e 100644 --- a/vendor/github.com/cilium/hive/command.go +++ b/vendor/github.com/cilium/hive/command.go @@ -14,10 +14,11 @@ import ( // command can be used to inspect the dependency graph. func (h *Hive) Command() *cobra.Command { cmd := &cobra.Command{ - Use: "hive", - Short: "Inspect the hive", - Run: func(cmd *cobra.Command, args []string) { - h.PrintObjects(os.Stdout, slog.Default()) + Use: "hive", + Short: "Inspect the hive", + SilenceUsage: true, + RunE: func(cmd *cobra.Command, args []string) error { + return h.PrintObjects(os.Stdout, slog.Default()) }, TraverseChildren: false, } @@ -25,10 +26,11 @@ func (h *Hive) Command() *cobra.Command { cmd.AddCommand( &cobra.Command{ - Use: "dot-graph", - Short: "Output the dependencies graph in graphviz dot format", - Run: func(cmd *cobra.Command, args []string) { - h.PrintDotGraph() + Use: "dot-graph", + Short: "Output the dependencies graph in graphviz dot format", + SilenceUsage: true, + RunE: func(cmd *cobra.Command, args []string) error { + return h.PrintDotGraph() }, TraverseChildren: false, }) diff --git a/vendor/github.com/cilium/hive/hive.go b/vendor/github.com/cilium/hive/hive.go index 760fe08436..6ef24a5384 100644 --- a/vendor/github.com/cilium/hive/hive.go +++ b/vendor/github.com/cilium/hive/hive.go @@ -229,6 +229,9 @@ func (h *Hive) Run(log *slog.Logger) error { var errs error if err := h.Start(log, startCtx); err != nil { + if errors.Is(err, ErrPopulate{}) { + return err + } errs = errors.Join(errs, fmt.Errorf("failed to start: %w", err)) } @@ -326,12 +329,27 @@ func (h *Hive) AppendInvoke(invoke func(*slog.Logger, time.Duration) error) { h.invokes = append(h.invokes, invoke) } +type ErrPopulate struct { + err error +} + +// Error implements error. +func (e ErrPopulate) Error() string { + return fmt.Sprintf("failed to populate object graph: %s", dig.RootCause(e.err)) +} + +func (e ErrPopulate) Pretty() string { + return fmt.Sprintf("Failed to populate object graph:\n%+v", e.err) +} + +var _ error = ErrPopulate{} + // Start starts the hive. The context allows cancelling the start. // If context is cancelled and the start hooks do not respect the cancellation // then after 5 more seconds the process will be terminated forcefully. func (h *Hive) Start(log *slog.Logger, ctx context.Context) error { if err := h.Populate(log); err != nil { - return err + return ErrPopulate{err} } defer close(h.fatalOnTimeout(ctx)) @@ -393,9 +411,11 @@ func (h *Hive) Shutdown(opts ...ShutdownOption) { } } -func (h *Hive) PrintObjects(w io.Writer, log *slog.Logger) { +func (h *Hive) PrintObjects(w io.Writer, log *slog.Logger) error { if err := h.Populate(log); err != nil { - panic(fmt.Sprintf("Failed to populate object graph: %s", err)) + err := ErrPopulate{err} + fmt.Fprintf(w, "ERROR: %s\n", err.Pretty()) + return err } fmt.Fprintf(w, "Cells:\n\n") @@ -405,16 +425,16 @@ func (h *Hive) PrintObjects(w io.Writer, log *slog.Logger) { fmt.Fprintln(w) } h.lifecycle.PrintHooks(w) + return nil } -func (h *Hive) PrintDotGraph() { +func (h *Hive) PrintDotGraph() error { if err := h.Populate(slog.Default()); err != nil { - panic(fmt.Sprintf("Failed to populate object graph: %s", err)) - } - - if err := dig.Visualize(h.container, os.Stdout); err != nil { - panic(fmt.Sprintf("Failed to dig.Visualize(): %s", err)) + err := ErrPopulate{err} + fmt.Fprintf(os.Stderr, "ERROR: %s\n", err.Pretty()) + return err } + return dig.Visualize(h.container, os.Stdout) } // getEnvName returns the environment variable to be used for the given option name. @@ -434,10 +454,10 @@ func (h *Hive) ScriptCommands(log *slog.Logger) (map[string]script.Cmd, error) { m["hive/stop"] = hiveStopCmd(h, log) // Gather the commands from the hive. - h.container.Invoke(func(sc ScriptCmds) { + err := h.container.Invoke(func(sc ScriptCmds) { for name, cmd := range sc.Map() { m[name] = cmd } }) - return m, nil + return m, err } diff --git a/vendor/github.com/cilium/hive/hivetest/doc.go b/vendor/github.com/cilium/hive/hivetest/doc.go new file mode 100644 index 0000000000..36d75e4b5e --- /dev/null +++ b/vendor/github.com/cilium/hive/hivetest/doc.go @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Authors of Cilium + +// Package hivetest makes testing components using hive easier. +package hivetest diff --git a/vendor/github.com/cilium/hive/hivetest/lifecycle.go b/vendor/github.com/cilium/hive/hivetest/lifecycle.go new file mode 100644 index 0000000000..40e50607a5 --- /dev/null +++ b/vendor/github.com/cilium/hive/hivetest/lifecycle.go @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright Authors of Cilium + +package hivetest + +import ( + "context" + "io" + "log/slog" + "testing" + + "github.com/cilium/hive/cell" +) + +// lifecycle implements [cell.Lifecycle] for testing purposes. +type lifecycle struct { + tb testing.TB +} + +var _ (cell.Lifecycle) = (*lifecycle)(nil) + +// Lifecycle returns a [cell.Lifecycle] which executes start hooks immediately +// and queues stop hooks for the end of the test. +func Lifecycle(tb testing.TB) *lifecycle { + return &lifecycle{tb} +} + +func (lc *lifecycle) Append(hook cell.HookInterface) { + lc.tb.Helper() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + if err := hook.Start(ctx); err != nil { + lc.tb.Fatal("Execute start hook:", err) + } + + lc.tb.Cleanup(func() { + lc.tb.Helper() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + if err := hook.Stop(ctx); err != nil && !lc.tb.Failed() { + lc.tb.Fatal("Execute stop hook:", err) + } + }) +} + +// PrintHooks implements cell.Lifecycle. +func (*lifecycle) PrintHooks(io.Writer) { + panic("unimplemented") +} + +// Start implements cell.Lifecycle. +func (*lifecycle) Start(*slog.Logger, context.Context) error { + panic("unimplemented") +} + +// Stop implements cell.Lifecycle. +func (*lifecycle) Stop(*slog.Logger, context.Context) error { + panic("unimplemented") +} diff --git a/vendor/github.com/cilium/hive/hivetest/slog.go b/vendor/github.com/cilium/hive/hivetest/slog.go new file mode 100644 index 0000000000..67df23893f --- /dev/null +++ b/vendor/github.com/cilium/hive/hivetest/slog.go @@ -0,0 +1,109 @@ +package hivetest + +import ( + "bytes" + "context" + "fmt" + "log/slog" + "os" + "slices" + "sync/atomic" + "testing" +) + +// Logger returns a logger which forwards structured logs to t. +func Logger(t testing.TB, opts ...LogOption) *slog.Logger { + // We need to make sure that we don't try to log to `t` after the test has + // completed since that leads to a panic. + var tDone atomic.Bool + t.Cleanup(func() { + tDone.Store(true) + }) + + th := &testHandler{ + tDone: &tDone, + t: t, + } + for _, o := range opts { + o(th) + } + return slog.New(th) +} + +type LogOption func(*testHandler) + +func LogLevel(l slog.Level) LogOption { + return func(th *testHandler) { + th.level = l + } +} + +// Will be accessed by multiple goroutines - needs to appear immutable. +type testHandler struct { + t testing.TB + tDone *atomic.Bool + level slog.Level + fields []groupOrAttr +} + +type groupOrAttr struct { + group string + attrs []slog.Attr +} + +func (th *testHandler) Handle(ctx context.Context, r slog.Record) error { + // This doesn't do the right thing (as we're called from slog), but the API + // doesn't allow us to skip more callframes. + th.t.Helper() + + // This is pretty inefficient if the handler has accumulated a lot of + // context, but necessary to make it trivially thread-safe. If it becomes + // necessary, we can always cache the handler we create here. + var buf bytes.Buffer + var h slog.Handler + h = slog.NewTextHandler(&buf, &slog.HandlerOptions{ + AddSource: true, + }) + for _, ga := range th.fields { + if ga.group != "" { + h = h.WithGroup(ga.group) + } else { + h = h.WithAttrs(ga.attrs) + } + } + if err := h.Handle(ctx, r); err != nil { + return err + } + + defer func() { + if r := recover(); r != nil { + fmt.Fprintf(os.Stderr, "failed to log (likely because the test is no longer running): %v", r) + } + }() + + // This is still TOCTOU racy with the call to log below, but we catch the + // potential panic above. + if th.tDone.Load() { + return fmt.Errorf("cannot log to finished test case") + } + // Chomp the newline. + th.t.Log(string(buf.Bytes()[:buf.Len()-1])) + + return nil +} + +func (th *testHandler) WithGroup(g string) slog.Handler { + nt := *th + nt.fields = append(slices.Clone(th.fields), groupOrAttr{group: g}) + return &nt +} + +func (th *testHandler) WithAttrs(as []slog.Attr) slog.Handler { + nt := *th + nt.fields = append(slices.Clone(th.fields), groupOrAttr{attrs: as}) + return &nt +} + +func (th *testHandler) Enabled(ctx context.Context, l slog.Level) bool { + return th.level <= l +} diff --git a/vendor/github.com/cilium/hive/script.go b/vendor/github.com/cilium/hive/script.go index f4a2a0f211..1289af2dd4 100644 --- a/vendor/github.com/cilium/hive/script.go +++ b/vendor/github.com/cilium/hive/script.go @@ -82,8 +82,8 @@ func hiveScriptCmd(h *Hive, log *slog.Logger) script.Cmd { defer cancel() return nil, h.Stop(log, ctx) default: - h.PrintObjects(s.LogWriter(), log) - return nil, nil + err := h.PrintObjects(s.LogWriter(), log) + return nil, err } }, ) diff --git a/vendor/github.com/cilium/hive/script/cmds.go b/vendor/github.com/cilium/hive/script/cmds.go index 75fb7fbf35..ba98365b9c 100644 --- a/vendor/github.com/cilium/hive/script/cmds.go +++ b/vendor/github.com/cilium/hive/script/cmds.go @@ -256,9 +256,10 @@ func doCompare(s *State, env bool, args ...string) error { } if text1 != text2 { - if s.DoUpdate { - // Updates requested, store the file contents and - // ignore mismatches. + if s.DoUpdate && s.RetryCount > 0 { + // Updates requested and we've already retried at least once + // (and given time for things to settle down). + // Store the file contents and ignore the mismatch. s.FileUpdates[name1] = text2 s.FileUpdates[name2] = text1 return nil diff --git a/vendor/github.com/cilium/hive/script/engine.go b/vendor/github.com/cilium/hive/script/engine.go index b919bb469d..cafa072aee 100644 --- a/vendor/github.com/cilium/hive/script/engine.go +++ b/vendor/github.com/cilium/hive/script/engine.go @@ -198,6 +198,10 @@ func (e *Engine) Execute(s *State, file string, script *bufio.Reader, log io.Wri if retryInterval == 0 { retryInterval = defaultRetryInterval } + maxRetryInterval := e.MaxRetryInterval + if maxRetryInterval == 0 { + maxRetryInterval = defaultMaxRetryInterval + } var ( sectionStart time.Time @@ -331,32 +335,43 @@ func (e *Engine) Execute(s *State, file string, script *bufio.Reader, log io.Wri } cmd.origArgs = expandArgs(s, cmd.rawArgs, regexpArgs) cmd.args = cmd.origArgs + s.RetryCount = 0 // Run the command. err = e.runCommand(s, cmd, impl) if err != nil { if cmd.want == successRetryOnFailure || cmd.want == failureRetryOnSuccess { + retryStart := sectionStart + + // Clear the section start to avoid the deferred endSection() printing a timestamp. + sectionStart = time.Time{} + + // Append the new line that section start omitted and flush the log. + io.WriteString(log, "\n") + s.FlushLog() + // Command wants retries. Retry the whole section - numRetries := 0 - backoff := exponentialBackoff{max: e.MaxRetryInterval, interval: e.RetryInterval} + backoff := exponentialBackoff{max: maxRetryInterval, interval: retryInterval} for err != nil { - s.FlushLog() retryDuration := backoff.get() - s.Logf("(command %q failed, retrying in %s...)\n", line, retryDuration) + fmt.Fprintf(log, "(command %q failed, retrying in %s...)\n", line, retryDuration) select { case <-s.Context().Done(): + s.RetryCount = 0 return lineErr(s.Context().Err()) case <-time.After(retryDuration): } - numRetries++ + s.RetryCount++ for _, cmd := range sectionCmds { impl := e.Cmds[cmd.name] if err = e.runCommand(s, cmd, impl); err != nil { break } } + s.FlushLog() } - s.Logf("(command %q succeeded after %d retries)\n", line, numRetries) + fmt.Fprintf(log, "(command %q succeeded after %d retries in %.3fs)\n", line, s.RetryCount, time.Since(retryStart).Seconds()) + s.RetryCount = 0 } else { if stop := (stopError{}); errors.As(err, &stop) { // Since the 'stop' command halts execution of the entire script, diff --git a/vendor/github.com/cilium/hive/script/state.go b/vendor/github.com/cilium/hive/script/state.go index 44c80cdd56..c512f20391 100644 --- a/vendor/github.com/cilium/hive/script/state.go +++ b/vendor/github.com/cilium/hive/script/state.go @@ -40,6 +40,7 @@ type State struct { Flags *pflag.FlagSet DoUpdate bool FileUpdates map[string]string + RetryCount int BreakOnError bool diff --git a/vendor/github.com/cilium/proxy/go/cilium/api/bpf_metadata.pb.go b/vendor/github.com/cilium/proxy/go/cilium/api/bpf_metadata.pb.go index 9bcd3dc9f2..3e2be1b2a0 100644 --- a/vendor/github.com/cilium/proxy/go/cilium/api/bpf_metadata.pb.go +++ b/vendor/github.com/cilium/proxy/go/cilium/api/bpf_metadata.pb.go @@ -68,6 +68,9 @@ type BpfMetadata struct { // network policy update took longer // Deprecated, has no effect. PolicyUpdateWarningLimit *durationpb.Duration `protobuf:"bytes,9,opt,name=policy_update_warning_limit,json=policyUpdateWarningLimit,proto3" json:"policy_update_warning_limit,omitempty"` + // l7lb_policy_name is the name of the L7LB policy that is enforced on the listener. + // This is optional field. + L7LbPolicyName string `protobuf:"bytes,10,opt,name=l7lb_policy_name,json=l7lbPolicyName,proto3" json:"l7lb_policy_name,omitempty"` } func (x *BpfMetadata) Reset() { @@ -165,6 +168,13 @@ func (x *BpfMetadata) GetPolicyUpdateWarningLimit() *durationpb.Duration { return nil } +func (x *BpfMetadata) GetL7LbPolicyName() string { + if x != nil { + return x.L7LbPolicyName + } + return "" +} + var File_cilium_api_bpf_metadata_proto protoreflect.FileDescriptor var file_cilium_api_bpf_metadata_proto_rawDesc = []byte{ @@ -172,7 +182,7 @@ var file_cilium_api_bpf_metadata_proto_rawDesc = []byte{ 0x5f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x63, 0x69, 0x6c, 0x69, 0x75, 0x6d, 0x1a, 0x1e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa9, 0x03, 0x0a, 0x0b, 0x42, 0x70, 0x66, 0x4d, + 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xd3, 0x03, 0x0a, 0x0b, 0x42, 0x70, 0x66, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x19, 0x0a, 0x08, 0x62, 0x70, 0x66, 0x5f, 0x72, 0x6f, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x62, 0x70, 0x66, 0x52, 0x6f, 0x6f, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x69, 0x73, 0x5f, 0x69, 0x6e, 0x67, 0x72, 0x65, 0x73, 0x73, @@ -199,10 +209,13 @@ var file_cilium_api_bpf_metadata_proto_rawDesc = []byte{ 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x18, 0x70, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x57, 0x61, 0x72, 0x6e, 0x69, 0x6e, 0x67, 0x4c, 0x69, - 0x6d, 0x69, 0x74, 0x42, 0x2e, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x63, 0x69, 0x6c, 0x69, 0x75, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x67, - 0x6f, 0x2f, 0x63, 0x69, 0x6c, 0x69, 0x75, 0x6d, 0x2f, 0x61, 0x70, 0x69, 0x3b, 0x63, 0x69, 0x6c, - 0x69, 0x75, 0x6d, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6d, 0x69, 0x74, 0x12, 0x28, 0x0a, 0x10, 0x6c, 0x37, 0x6c, 0x62, 0x5f, 0x70, 0x6f, 0x6c, 0x69, + 0x63, 0x79, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x6c, + 0x37, 0x6c, 0x62, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x4e, 0x61, 0x6d, 0x65, 0x42, 0x2e, 0x5a, + 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x69, 0x6c, 0x69, + 0x75, 0x6d, 0x2f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x67, 0x6f, 0x2f, 0x63, 0x69, 0x6c, 0x69, + 0x75, 0x6d, 0x2f, 0x61, 0x70, 0x69, 0x3b, 0x63, 0x69, 0x6c, 0x69, 0x75, 0x6d, 0x62, 0x06, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/vendor/github.com/cilium/proxy/go/cilium/api/bpf_metadata.pb.validate.go b/vendor/github.com/cilium/proxy/go/cilium/api/bpf_metadata.pb.validate.go index 6759e89e77..09ee33d097 100644 --- a/vendor/github.com/cilium/proxy/go/cilium/api/bpf_metadata.pb.validate.go +++ b/vendor/github.com/cilium/proxy/go/cilium/api/bpf_metadata.pb.validate.go @@ -102,6 +102,8 @@ func (m *BpfMetadata) validate(all bool) error { } } + // no validation rules for L7LbPolicyName + if len(errors) > 0 { return BpfMetadataMultiError(errors) } diff --git a/vendor/github.com/cilium/statedb/any_table.go b/vendor/github.com/cilium/statedb/any_table.go index 36b40a30a3..cbf561b982 100644 --- a/vendor/github.com/cilium/statedb/any_table.go +++ b/vendor/github.com/cilium/statedb/any_table.go @@ -69,8 +69,7 @@ func (t AnyTable) Get(txn ReadTxn, index string, key string) (any, Revision, boo if !ok { break } - secondary, _ := decodeNonUniqueKey(k) - if len(secondary) == len(rawKey) { + if nonUniqueKey(k).secondaryLen() == len(rawKey) { return obj.data, obj.revision, true, nil } } diff --git a/vendor/github.com/cilium/statedb/http.go b/vendor/github.com/cilium/statedb/http.go index 96b1a651fd..80d1dc5556 100644 --- a/vendor/github.com/cilium/statedb/http.go +++ b/vendor/github.com/cilium/statedb/http.go @@ -124,6 +124,9 @@ type QueryResponse struct { func runQuery(indexTxn indexReadTxn, lowerbound bool, queryKey []byte, onObject func(object) error) { var iter *part.Iterator[object] + if !indexTxn.unique { + queryKey = encodeNonUniqueBytes(queryKey) + } if lowerbound { iter = indexTxn.LowerBound(queryKey) } else { @@ -137,8 +140,7 @@ func runQuery(indexTxn indexReadTxn, lowerbound bool, queryKey []byte, onObject match = func(k []byte) bool { return len(k) == len(queryKey) } default: match = func(k []byte) bool { - secondary, _ := decodeNonUniqueKey(k) - return len(secondary) == len(queryKey) + return nonUniqueKey(k).secondaryLen() == len(queryKey) } } for key, obj, ok := iter.Next(); ok; key, obj, ok = iter.Next() { diff --git a/vendor/github.com/cilium/statedb/iterator.go b/vendor/github.com/cilium/statedb/iterator.go index 301ce330d5..903a406bc4 100644 --- a/vendor/github.com/cilium/statedb/iterator.go +++ b/vendor/github.com/cilium/statedb/iterator.go @@ -113,18 +113,21 @@ func nonUniqueSeq[Obj any](iter *part.Iterator[object], prefixSearch bool, searc break } - secondary, primary := decodeNonUniqueKey(key) + nuk := nonUniqueKey(key) + secondaryLen := nuk.secondaryLen() switch { - case !prefixSearch && len(secondary) != len(searchKey): + case !prefixSearch && secondaryLen != len(searchKey): // This a List(), thus secondary key must match length exactly. continue - case prefixSearch && len(secondary) < len(searchKey): + case prefixSearch && secondaryLen < len(searchKey): // This is Prefix(), thus key must be equal or longer to search key. continue } if prefixSearch { + primary := nuk.encodedPrimary() + // When doing a prefix search on a non-unique index we may see the // same object multiple times since multiple keys may point it. // Skip if we've already seen this object. @@ -157,8 +160,10 @@ func nonUniqueLowerBoundSeq[Obj any](iter *part.Iterator[object], searchKey []by // With a non-unique index we have a composite key . // This means we need to check every key that it's larger or equal to the search key. // Just seeking to the first one isn't enough as the secondary key length may vary. - secondary, primary := decodeNonUniqueKey(key) + nuk := nonUniqueKey(key) + secondary := nuk.encodedSecondary() if bytes.Compare(secondary, searchKey) >= 0 { + primary := nuk.encodedPrimary() if _, found := visited[string(primary)]; found { continue } diff --git a/vendor/github.com/cilium/statedb/table.go b/vendor/github.com/cilium/statedb/table.go index c7d41fb1f6..98e84f1e1e 100644 --- a/vendor/github.com/cilium/statedb/table.go +++ b/vendor/github.com/cilium/statedb/table.go @@ -321,8 +321,7 @@ func (t *genTable[Obj]) GetWatch(txn ReadTxn, q Query[Obj]) (obj Obj, revision u } // Check that we have a full match on the key - secondary, _ := decodeNonUniqueKey(key) - if len(secondary) == len(q.key) { + if nonUniqueKey(key).secondaryLen() == len(q.key) { break } } diff --git a/vendor/github.com/cilium/statedb/txn.go b/vendor/github.com/cilium/statedb/txn.go index 6c75c88232..a1a436b684 100644 --- a/vendor/github.com/cilium/statedb/txn.go +++ b/vendor/github.com/cilium/statedb/txn.go @@ -46,8 +46,6 @@ type indexTxn struct { unique bool } -var zeroTxn = txn{} - // txn fulfills the ReadTxn/WriteTxn interface. func (txn *txn) getTxn() *txn { return txn @@ -361,38 +359,57 @@ func (txn *txn) delete(meta TableMeta, guardRevision Revision, data any) (object } const ( - nonUniqueSeparator = 0x0 - nonUniqueSubstitute = 0xfe - nonUniqueSubstitute2 = 0xfd + // nonUniqueSeparator is the byte that delimits the secondary and primary keys. + // It has to be 0x00 for correct ordering, e.g. if secondary prefix is "ab", + // then it must hold that "ab" < "abc", which is only possible if sep=0x00. + nonUniqueSeparator = 0x00 + + // nonUniqueSubstitute is the byte that is used to escape 0x00 and 0x01 in + // order to make sure the non-unique key has only a single 0x00 byte that is + // the separator. + nonUniqueSubstitute = 0x01 ) -// appendEncodePrimary encodes the 'src' (primary key) into 'dst'. -func appendEncodePrimary(dst, src []byte) []byte { +// appendEncode encodes the 'src' into 'dst'. +func appendEncode(dst, src []byte) (int, []byte) { + n := 0 for _, b := range src { switch b { case nonUniqueSeparator: - dst = append(dst, nonUniqueSubstitute) + dst = append(dst, nonUniqueSubstitute, 0x01) + n += 2 case nonUniqueSubstitute: - dst = append(dst, nonUniqueSubstitute2, 0x00) - case nonUniqueSubstitute2: - dst = append(dst, nonUniqueSubstitute2, 0x01) + dst = append(dst, nonUniqueSubstitute, 0x02) + n += 2 default: dst = append(dst, b) + n++ + } + } + return n, dst +} + +func encodedLength(src []byte) int { + n := len(src) + for _, b := range src { + if b == nonUniqueSeparator || b == nonUniqueSubstitute { + n++ } } - return dst + return n +} + +func encodeNonUniqueBytes(src []byte) []byte { + n := encodedLength(src) + if n == len(src) { + // No substitutions needed. + return src + } + _, out := appendEncode(make([]byte, 0, n), src) + return out } // encodeNonUniqueKey constructs the internal key to use with non-unique indexes. -// The key is constructed by concatenating the secondary key with the primary key -// along with the secondary key length. The secondary and primary key are separated -// with by a 0x0 to ensure ordering is defined by the secondary key. To make sure the -// separator does not appear in the primary key it is encoded using this schema: -// -// 0x0 => 0xfe, 0xfe => 0xfd00, 0xfd => 0xfd01 -// -// The schema tries to avoid expansion for encoded small integers, e.g. 0x0000 becomes 0xfefe. -// The length at the end is encoded as unsigned 16-bit big endian. // // This schema allows looking up from the non-unique index with the secondary key by // doing a prefix search. The length is used to safe-guard against indexers that don't @@ -400,27 +417,38 @@ func appendEncodePrimary(dst, src []byte) []byte { // "foobar" to match). func encodeNonUniqueKey(primary, secondary index.Key) []byte { key := make([]byte, 0, - len(secondary)+1 /* separator */ + - len(primary)+ - 2 /* space for few substitutions */ + - 2 /* length */) - key = append(key, secondary...) - key = append(key, nonUniqueSeparator) - key = appendEncodePrimary(key, primary) - // KeySet limits size of key to 16 bits. - return binary.BigEndian.AppendUint16(key, uint16(len(secondary))) + encodedLength(secondary)+ + 1 /* delimiter */ + + encodedLength(primary)+ + 2 /* primary length */) + + _, key = appendEncode(key, secondary) + key = append(key, 0x00) + primaryLen, key := appendEncode(key, primary) + return binary.BigEndian.AppendUint16(key, uint16(primaryLen)) } -func decodeNonUniqueKey(key []byte) (secondary []byte, encPrimary []byte) { - // Non-unique key is [, '\xfe', , ] - if len(key) < 2 { - return nil, nil - } - secondaryLength := int(binary.BigEndian.Uint16(key[len(key)-2:])) - if len(key) < secondaryLength { - return nil, nil +type nonUniqueKey []byte + +func (k nonUniqueKey) primaryLen() int { + // Non-unique key is [, 0x00, , ] + if len(k) <= 3 { + return 0 } - return key[:secondaryLength], key[secondaryLength+1 : len(key)-2] + return int(binary.BigEndian.Uint16(k[len(k)-2:])) +} + +func (k nonUniqueKey) secondaryLen() int { + return len(k) - k.primaryLen() - 3 +} + +func (k nonUniqueKey) encodedPrimary() []byte { + primaryLen := k.primaryLen() + return k[len(k)-2-primaryLen : len(k)-2] +} + +func (k nonUniqueKey) encodedSecondary() []byte { + return k[:k.secondaryLen()] } func (txn *txn) Abort() { diff --git a/vendor/github.com/cilium/statedb/types.go b/vendor/github.com/cilium/statedb/types.go index 418e597322..13d712f06b 100644 --- a/vendor/github.com/cilium/statedb/types.go +++ b/vendor/github.com/cilium/statedb/types.go @@ -344,6 +344,7 @@ func (i Index[Obj, Key]) fromString(s string) (index.Key, error) { return index.Key{}, errFromStringNil } k, err := i.FromString(s) + k = i.encodeKey(k) return k, err } @@ -352,23 +353,30 @@ func (i Index[Obj, Key]) isUnique() bool { return i.Unique } +func (i Index[Obj, Key]) encodeKey(key []byte) []byte { + if !i.Unique { + return encodeNonUniqueBytes(key) + } + return key +} + // Query constructs a query against this index from a key. func (i Index[Obj, Key]) Query(key Key) Query[Obj] { return Query[Obj]{ index: i.Name, - key: i.FromKey(key), + key: i.encodeKey(i.FromKey(key)), } } func (i Index[Obj, Key]) QueryFromObject(obj Obj) Query[Obj] { return Query[Obj]{ index: i.Name, - key: i.FromObject(obj).First(), + key: i.encodeKey(i.FromObject(obj).First()), } } func (i Index[Obj, Key]) ObjectToKey(obj Obj) index.Key { - return i.FromObject(obj).First() + return i.encodeKey(i.FromObject(obj).First()) } // Indexer is the "FromObject" subset of Index[Obj, Key] diff --git a/vendor/github.com/cilium/workerpool/.golangci.yml b/vendor/github.com/cilium/workerpool/.golangci.yml index 267f1c400f..96a7addbce 100644 --- a/vendor/github.com/cilium/workerpool/.golangci.yml +++ b/vendor/github.com/cilium/workerpool/.golangci.yml @@ -1,8 +1,7 @@ # See https://golangci-lint.run/usage/configuration/ for available options. -# Also https://github.com/cilium/cilium/blob/master/.golangci.yaml as a +# Also https://github.com/cilium/cilium/blob/main/.golangci.yaml as a # reference. run: - go: '1.20' timeout: 1m linters: disable-all: true @@ -13,28 +12,28 @@ linters: - bodyclose - containedctx - contextcheck + - copyloopvar - cyclop - decorder - dogsled - dupl - dupword - durationcheck + - err113 - errcheck - errname - errorlint + - exptostd - exhaustive - - exportloopref - forcetypeassert - gocheckcompilerdirectives - gocognit - goconst - gocritic - godot - - goerr113 - gofmt - goheader - goimports - - gomnd - goprintffuncname - gosec - gosimple @@ -42,8 +41,10 @@ linters: - grouper - ineffassign - interfacebloat + - intrange - ireturn - makezero + - mirror - misspell - musttag - nakedret @@ -52,13 +53,16 @@ linters: - nilnil - noctx - nosprintfhostport + - perfsprint - prealloc - predeclared - reassign - revive - rowserrcheck + - sloglint - staticcheck - - tenv + - tagalign + - testifylint - thelper - tparallel - typecheck @@ -66,15 +70,36 @@ linters: - unparam - unused - usestdlibvars + - usetesting - wastedassign - linters-settings: cyclop: skip-tests: true - goheader: template: |- SPDX-License-Identifier: Apache-2.0 Copyright Authors of Cilium govet: enable-all: true + perfsprint: + strconcat: false + sloglint: + no-mixed-args: true + static-msg: true + no-global: "all" + key-naming-case: kebab # be consistent with key names + forbidden-keys: # let's no use reserved log keys + - level + - msg + - source + - time +issues: + # Maximum issues count per one linter. + # Set to 0 to disable (default is 50) + max-issues-per-linter: 0 + # Maximum count of issues with the same text. + # Set to 0 to disable (default is 3) + max-same-issues: 0 + fix: true # fix found issues (if it's supported by the linter). + exclude-use-default: false # default rules exclude doc comments check :( + exclude-generated: strict diff --git a/vendor/github.com/cilium/workerpool/README.md b/vendor/github.com/cilium/workerpool/README.md index 5df02ff0ff..b073964a8e 100644 --- a/vendor/github.com/cilium/workerpool/README.md +++ b/vendor/github.com/cilium/workerpool/README.md @@ -53,7 +53,6 @@ func IsPrime(n int64) bool { func main() { wp := workerpool.New(runtime.NumCPU()) for i, n := 0, int64(1_000_000_000_000_000_000); n < 1_000_000_000_000_000_100; i, n = i+1, n+1 { - n := n // https://golang.org/doc/faq#closures_and_goroutines id := fmt.Sprintf("task #%d", i) // Use Submit to submit tasks for processing. Submit blocks when no // worker is available to pick up the task. diff --git a/vendor/github.com/cilium/workerpool/workerpool.go b/vendor/github.com/cilium/workerpool/workerpool.go index c78af835e0..802acfe617 100644 --- a/vendor/github.com/cilium/workerpool/workerpool.go +++ b/vendor/github.com/cilium/workerpool/workerpool.go @@ -171,7 +171,7 @@ func (wp *WorkerPool) Close() error { // only be called once during the lifetime of a WorkerPool. func (wp *WorkerPool) run(ctx context.Context) { for t := range wp.tasks { - t := t + result := taskResult{id: t.id} wp.results = append(wp.results, &result) wp.workers <- struct{}{} diff --git a/vendor/github.com/containerd/containerd/errdefs/grpc.go b/vendor/github.com/containerd/containerd/errdefs/grpc.go index 7a9b33e05a..11091b1db0 100644 --- a/vendor/github.com/containerd/containerd/errdefs/grpc.go +++ b/vendor/github.com/containerd/containerd/errdefs/grpc.go @@ -45,21 +45,21 @@ func ToGRPC(err error) error { switch { case IsInvalidArgument(err): - return status.Errorf(codes.InvalidArgument, err.Error()) + return status.Error(codes.InvalidArgument, err.Error()) case IsNotFound(err): - return status.Errorf(codes.NotFound, err.Error()) + return status.Error(codes.NotFound, err.Error()) case IsAlreadyExists(err): - return status.Errorf(codes.AlreadyExists, err.Error()) + return status.Error(codes.AlreadyExists, err.Error()) case IsFailedPrecondition(err): - return status.Errorf(codes.FailedPrecondition, err.Error()) + return status.Error(codes.FailedPrecondition, err.Error()) case IsUnavailable(err): - return status.Errorf(codes.Unavailable, err.Error()) + return status.Error(codes.Unavailable, err.Error()) case IsNotImplemented(err): - return status.Errorf(codes.Unimplemented, err.Error()) + return status.Error(codes.Unimplemented, err.Error()) case IsCanceled(err): - return status.Errorf(codes.Canceled, err.Error()) + return status.Error(codes.Canceled, err.Error()) case IsDeadlineExceeded(err): - return status.Errorf(codes.DeadlineExceeded, err.Error()) + return status.Error(codes.DeadlineExceeded, err.Error()) } return err diff --git a/vendor/github.com/containerd/containerd/filters/parser.go b/vendor/github.com/containerd/containerd/filters/parser.go index 32767909b1..790597aaf2 100644 --- a/vendor/github.com/containerd/containerd/filters/parser.go +++ b/vendor/github.com/containerd/containerd/filters/parser.go @@ -121,7 +121,7 @@ loop: case tokenEOF: break loop default: - return nil, p.mkerr(p.scanner.ppos, "unexpected input: %v", string(tok)) + return nil, p.mkerrf(p.scanner.ppos, "unexpected input: %v", string(tok)) } } @@ -226,7 +226,7 @@ func (p *parser) operator() (operator, error) { case "~=": return operatorMatches, nil default: - return 0, p.mkerr(pos, "unsupported operator %q", s) + return 0, p.mkerrf(pos, "unsupported operator %q", s) } case tokenIllegal: return 0, p.mkerr(pos, p.scanner.err) @@ -257,7 +257,7 @@ func (p *parser) unquote(pos int, s string, allowAlts bool) (string, error) { uq, err := unquote(s) if err != nil { - return "", p.mkerr(pos, "unquoting failed: %v", err) + return "", p.mkerrf(pos, "unquoting failed: %v", err) } return uq, nil @@ -281,10 +281,14 @@ func (pe parseError) Error() string { return fmt.Sprintf("[%s]: %v", pe.input, pe.msg) } -func (p *parser) mkerr(pos int, format string, args ...interface{}) error { +func (p *parser) mkerrf(pos int, format string, args ...interface{}) error { + return p.mkerr(pos, fmt.Sprintf(format, args...)) +} + +func (p *parser) mkerr(pos int, msg string) error { return fmt.Errorf("parse error: %w", parseError{ input: p.input, pos: pos, - msg: fmt.Sprintf(format, args...), + msg: msg, }) } diff --git a/vendor/github.com/containerd/containerd/images/image.go b/vendor/github.com/containerd/containerd/images/image.go index b934e34961..8bebae19b3 100644 --- a/vendor/github.com/containerd/containerd/images/image.go +++ b/vendor/github.com/containerd/containerd/images/image.go @@ -378,7 +378,7 @@ func Children(ctx context.Context, provider content.Provider, desc ocispec.Descr descs = append(descs, index.Manifests...) default: - if IsLayerType(desc.MediaType) || IsKnownConfig(desc.MediaType) { + if IsLayerType(desc.MediaType) || IsKnownConfig(desc.MediaType) || IsAttestationType(desc.MediaType) { // childless data types. return nil, nil } diff --git a/vendor/github.com/containerd/containerd/images/mediatypes.go b/vendor/github.com/containerd/containerd/images/mediatypes.go index d3b28d42dc..49d2a5b1c5 100644 --- a/vendor/github.com/containerd/containerd/images/mediatypes.go +++ b/vendor/github.com/containerd/containerd/images/mediatypes.go @@ -57,6 +57,9 @@ const ( MediaTypeImageLayerEncrypted = ocispec.MediaTypeImageLayer + "+encrypted" MediaTypeImageLayerGzipEncrypted = ocispec.MediaTypeImageLayerGzip + "+encrypted" + + // In-toto attestation + MediaTypeInToto = "application/vnd.in-toto+json" ) // DiffCompression returns the compression as defined by the layer diff media @@ -186,6 +189,16 @@ func IsKnownConfig(mt string) bool { return false } +// IsAttestationType returns true if the media type is an attestation type +func IsAttestationType(mt string) bool { + switch mt { + case MediaTypeInToto: + return true + default: + return false + } +} + // ChildGCLabels returns the label for a given descriptor to reference it func ChildGCLabels(desc ocispec.Descriptor) []string { mt := desc.MediaType diff --git a/vendor/github.com/containerd/containerd/remotes/handlers.go b/vendor/github.com/containerd/containerd/remotes/handlers.go index 365ff5fc00..14af02769c 100644 --- a/vendor/github.com/containerd/containerd/remotes/handlers.go +++ b/vendor/github.com/containerd/containerd/remotes/handlers.go @@ -81,6 +81,8 @@ func MakeRefKey(ctx context.Context, desc ocispec.Descriptor) string { return "layer-" + key case images.IsKnownConfig(mt): return "config-" + key + case images.IsAttestationType(desc.MediaType): + return "attestation-" + key default: log.G(ctx).Warnf("reference for unknown type: %s", mt) return "unknown-" + key diff --git a/vendor/github.com/containerd/containerd/version/version.go b/vendor/github.com/containerd/containerd/version/version.go index b83e759641..e806164cab 100644 --- a/vendor/github.com/containerd/containerd/version/version.go +++ b/vendor/github.com/containerd/containerd/version/version.go @@ -23,7 +23,7 @@ var ( Package = "github.com/containerd/containerd" // Version holds the complete version number. Filled in at linking time. - Version = "1.7.24+unknown" + Version = "1.7.27+unknown" // Revision is filled with the VCS (e.g. git) revision being used to build // the program at linking time. diff --git a/vendor/github.com/docker/docker/AUTHORS b/vendor/github.com/docker/docker/AUTHORS index 5f93eeb4e8..88032defe7 100644 --- a/vendor/github.com/docker/docker/AUTHORS +++ b/vendor/github.com/docker/docker/AUTHORS @@ -2,7 +2,9 @@ # This file lists all contributors to the repository. # See hack/generate-authors.sh to make modifications. +7sunarni <710720732@qq.com> Aanand Prasad +Aarni Koskela Aaron Davidson Aaron Feng Aaron Hnatiw @@ -11,6 +13,7 @@ Aaron L. Xu Aaron Lehmann Aaron Welch Aaron Yoshitake +Abdur Rehman Abel Muiño Abhijeet Kasurde Abhinandan Prativadi @@ -24,9 +27,11 @@ Adam Avilla Adam Dobrawy Adam Eijdenberg Adam Kunk +Adam Lamers Adam Miller Adam Mills Adam Pointer +Adam Simon Adam Singer Adam Thornton Adam Walz @@ -119,6 +124,7 @@ amangoel Amen Belayneh Ameya Gawde Amir Goldstein +AmirBuddy Amit Bakshi Amit Krishnan Amit Shukla @@ -168,6 +174,7 @@ Andrey Kolomentsev Andrey Petrov Andrey Stolbovsky André Martins +Andrés Maldonado Andy Chambers andy diller Andy Goldstein @@ -219,6 +226,7 @@ Artur Meyster Arun Gupta Asad Saeeduddin Asbjørn Enge +Ashly Mathew Austin Vazquez averagehuman Avi Das @@ -345,6 +353,7 @@ Chance Zibolski Chander Govindarajan Chanhun Jeong Chao Wang +Charity Kathure Charles Chan Charles Hooper Charles Law @@ -480,6 +489,7 @@ Daniel Farrell Daniel Garcia Daniel Gasienica Daniel Grunwell +Daniel Guns Daniel Helfand Daniel Hiltgen Daniel J Walsh @@ -763,6 +773,7 @@ Frank Macreery Frank Rosquin Frank Villaro-Dixon Frank Yang +François Scala Fred Lifton Frederick F. Kautz IV Frederico F. de Oliveira @@ -798,6 +809,7 @@ GennadySpb Geoff Levand Geoffrey Bachelet Geon Kim +George Adams George Kontridze George Ma George MacRorie @@ -826,6 +838,7 @@ Gopikannan Venugopalsamy Gosuke Miyashita Gou Rao Govinda Fichtner +Grace Choi Grant Millar Grant Reaber Graydon Hoare @@ -966,6 +979,7 @@ James Nugent James Sanders James Turnbull James Watkins-Harvey +Jameson Hyde Jamie Hannaford Jamshid Afshar Jan Breig @@ -1064,13 +1078,16 @@ Jim Perrin Jimmy Cuadra Jimmy Puckett Jimmy Song +jinjiadu Jinsoo Park Jintao Zhang Jiri Appl Jiri Popelka Jiuyue Ma Jiří Župka +jjimbo137 <115816493+jjimbo137@users.noreply.github.com> Joakim Roubert +Joan Grau Joao Fernandes Joao Trindade Joe Beda @@ -1155,6 +1172,7 @@ Josiah Kiehl José Tomás Albornoz Joyce Jang JP +JSchltggr Julian Taylor Julien Barbier Julien Bisconti @@ -1289,6 +1307,7 @@ Laura Brehm Laura Frank Laurent Bernaille Laurent Erignoux +Laurent Goderre Laurie Voss Leandro Motta Barros Leandro Siqueira @@ -1369,6 +1388,7 @@ Madhan Raj Mookkandy Madhav Puri Madhu Venugopal Mageee +maggie44 <64841595+maggie44@users.noreply.github.com> Mahesh Tiyyagura malnick Malte Janduda @@ -1579,6 +1599,7 @@ Muayyad Alsadi Muhammad Zohaib Aslam Mustafa Akın Muthukumar R +Myeongjoon Kim Máximo Cuadros Médi-Rémi Hashim Nace Oroz @@ -1593,6 +1614,7 @@ Natasha Jarus Nate Brennand Nate Eagleson Nate Jones +Nathan Baulch Nathan Carlson Nathan Herald Nathan Hsieh @@ -1655,6 +1677,7 @@ Nuutti Kotivuori nzwsch O.S. Tezer objectified +Octol1ttle Odin Ugedal Oguz Bilgic Oh Jinkyun @@ -1763,6 +1786,7 @@ Pierre Carrier Pierre Dal-Pra Pierre Wacrenier Pierre-Alain RIVIERE +pinglanlu Piotr Bogdan Piotr Karbowski Porjo @@ -1790,6 +1814,7 @@ Quentin Tayssier r0n22 Rachit Sharma Radostin Stoyanov +Rafael Fernández López Rafal Jeczalik Rafe Colton Raghavendra K T @@ -1856,7 +1881,7 @@ Robin Speekenbrink Robin Thoni robpc Rodolfo Carvalho -Rodrigo Campos +Rodrigo Campos Rodrigo Vaz Roel Van Nyen Roger Peppe @@ -1995,6 +2020,7 @@ Sevki Hasirci Shane Canon Shane da Silva Shaun Kaasten +Shaun Thompson shaunol Shawn Landden Shawn Siefkas @@ -2013,6 +2039,7 @@ Shijun Qin Shishir Mahajan Shoubhik Bose Shourya Sarcar +Shreenidhi Shedi Shu-Wai Chow shuai-z Shukui Yang @@ -2100,6 +2127,7 @@ Sébastien Stormacq Sören Tempel Tabakhase Tadej Janež +Tadeusz Dudkiewicz Takuto Sato tang0th Tangi Colin @@ -2107,6 +2135,7 @@ Tatsuki Sugiura Tatsushi Inagaki Taylan Isikdemir Taylor Jones +tcpdumppy <847462026@qq.com> Ted M. Young Tehmasp Chaudhri Tejaswini Duggaraju @@ -2391,6 +2420,7 @@ You-Sheng Yang (楊有勝) youcai Youcef YEKHLEF Youfu Zhang +YR Chen Yu Changchun Yu Chengxia Yu Peng diff --git a/vendor/github.com/docker/docker/api/types/filters/errors.go b/vendor/github.com/docker/docker/api/types/filters/errors.go index f52f694408..b8a690d67a 100644 --- a/vendor/github.com/docker/docker/api/types/filters/errors.go +++ b/vendor/github.com/docker/docker/api/types/filters/errors.go @@ -22,16 +22,3 @@ func (e invalidFilter) Error() string { // InvalidParameter marks this error as ErrInvalidParameter func (e invalidFilter) InvalidParameter() {} - -// unreachableCode is an error indicating that the code path was not expected to be reached. -type unreachableCode struct { - Filter string - Value []string -} - -// System marks this error as ErrSystem -func (e unreachableCode) System() {} - -func (e unreachableCode) Error() string { - return fmt.Sprintf("unreachable code reached for filter: %q with values: %s", e.Filter, e.Value) -} diff --git a/vendor/github.com/docker/docker/api/types/filters/parse.go b/vendor/github.com/docker/docker/api/types/filters/parse.go index 0914b2a441..2085ff38f2 100644 --- a/vendor/github.com/docker/docker/api/types/filters/parse.go +++ b/vendor/github.com/docker/docker/api/types/filters/parse.go @@ -200,7 +200,6 @@ func (args Args) Match(field, source string) bool { // Error is not nil only if the filter values are not valid boolean or are conflicting. func (args Args) GetBoolOrDefault(key string, defaultValue bool) (bool, error) { fieldValues, ok := args.fields[key] - if !ok { return defaultValue, nil } @@ -211,20 +210,11 @@ func (args Args) GetBoolOrDefault(key string, defaultValue bool) (bool, error) { isFalse := fieldValues["0"] || fieldValues["false"] isTrue := fieldValues["1"] || fieldValues["true"] - - conflicting := isFalse && isTrue - invalid := !isFalse && !isTrue - - if conflicting || invalid { + if isFalse == isTrue { + // Either no or conflicting truthy/falsy value were provided return defaultValue, &invalidFilter{key, args.Get(key)} - } else if isFalse { - return false, nil - } else if isTrue { - return true, nil } - - // This code shouldn't be reached. - return defaultValue, &unreachableCode{Filter: key, Value: args.Get(key)} + return isTrue, nil } // ExactMatch returns true if the source matches exactly one of the values. diff --git a/vendor/github.com/docker/docker/api/types/registry/authconfig.go b/vendor/github.com/docker/docker/api/types/registry/authconfig.go index 8e383f6e60..ebd5e4b9e2 100644 --- a/vendor/github.com/docker/docker/api/types/registry/authconfig.go +++ b/vendor/github.com/docker/docker/api/types/registry/authconfig.go @@ -1,17 +1,29 @@ package registry // import "github.com/docker/docker/api/types/registry" import ( + "context" "encoding/base64" "encoding/json" + "fmt" "io" "strings" - - "github.com/pkg/errors" ) // AuthHeader is the name of the header used to send encoded registry // authorization credentials for registry operations (push/pull). const AuthHeader = "X-Registry-Auth" +// RequestAuthConfig is a function interface that clients can supply +// to retry operations after getting an authorization error. +// +// The function must return the [AuthHeader] value ([AuthConfig]), encoded +// in base64url format ([RFC4648, section 5]), which can be decoded by +// [DecodeAuthConfig]. +// +// It must return an error if the privilege request fails. +// +// [RFC4648, section 5]: https://tools.ietf.org/html/rfc4648#section-5 +type RequestAuthConfig func(context.Context) (string, error) + // AuthConfig contains authorization information for connecting to a Registry. type AuthConfig struct { Username string `json:"username,omitempty"` @@ -85,7 +97,7 @@ func decodeAuthConfigFromReader(rdr io.Reader) (*AuthConfig, error) { } func invalid(err error) error { - return errInvalidParameter{errors.Wrap(err, "invalid X-Registry-Auth header")} + return errInvalidParameter{fmt.Errorf("invalid X-Registry-Auth header: %w", err)} } type errInvalidParameter struct{ error } diff --git a/vendor/github.com/docker/docker/api/types/registry/registry.go b/vendor/github.com/docker/docker/api/types/registry/registry.go index 75ee07b15f..b0a4d604f5 100644 --- a/vendor/github.com/docker/docker/api/types/registry/registry.go +++ b/vendor/github.com/docker/docker/api/types/registry/registry.go @@ -9,11 +9,29 @@ import ( // ServiceConfig stores daemon registry services configuration. type ServiceConfig struct { - AllowNondistributableArtifactsCIDRs []*NetIPNet - AllowNondistributableArtifactsHostnames []string - InsecureRegistryCIDRs []*NetIPNet `json:"InsecureRegistryCIDRs"` - IndexConfigs map[string]*IndexInfo `json:"IndexConfigs"` - Mirrors []string + AllowNondistributableArtifactsCIDRs []*NetIPNet `json:"AllowNondistributableArtifactsCIDRs,omitempty"` // Deprecated: non-distributable artifacts are deprecated and enabled by default. This field will be removed in the next release. + AllowNondistributableArtifactsHostnames []string `json:"AllowNondistributableArtifactsHostnames,omitempty"` // Deprecated: non-distributable artifacts are deprecated and enabled by default. This field will be removed in the next release. + + InsecureRegistryCIDRs []*NetIPNet `json:"InsecureRegistryCIDRs"` + IndexConfigs map[string]*IndexInfo `json:"IndexConfigs"` + Mirrors []string +} + +// MarshalJSON implements a custom marshaler to include legacy fields +// in API responses. +func (sc ServiceConfig) MarshalJSON() ([]byte, error) { + tmp := map[string]interface{}{ + "InsecureRegistryCIDRs": sc.InsecureRegistryCIDRs, + "IndexConfigs": sc.IndexConfigs, + "Mirrors": sc.Mirrors, + } + if sc.AllowNondistributableArtifactsCIDRs != nil { + tmp["AllowNondistributableArtifactsCIDRs"] = nil + } + if sc.AllowNondistributableArtifactsHostnames != nil { + tmp["AllowNondistributableArtifactsHostnames"] = nil + } + return json.Marshal(tmp) } // NetIPNet is the net.IPNet type, which can be marshalled and diff --git a/vendor/github.com/docker/docker/api/types/registry/search.go b/vendor/github.com/docker/docker/api/types/registry/search.go index a0a1eec544..994ca4c6f9 100644 --- a/vendor/github.com/docker/docker/api/types/registry/search.go +++ b/vendor/github.com/docker/docker/api/types/registry/search.go @@ -10,11 +10,12 @@ import ( type SearchOptions struct { RegistryAuth string - // PrivilegeFunc is a [types.RequestPrivilegeFunc] the client can - // supply to retry operations after getting an authorization error. + // PrivilegeFunc is a function that clients can supply to retry operations + // after getting an authorization error. This function returns the registry + // authentication header value in base64 encoded format, or an error if the + // privilege request fails. // - // It must return the registry authentication header value in base64 - // format, or an error if the privilege request fails. + // For details, refer to [github.com/docker/docker/api/types/registry.RequestAuthConfig]. PrivilegeFunc func(context.Context) (string, error) Filters filters.Args Limit int diff --git a/vendor/github.com/docker/docker/errdefs/helpers.go b/vendor/github.com/docker/docker/errdefs/helpers.go index 042de4b7b8..ab76e62736 100644 --- a/vendor/github.com/docker/docker/errdefs/helpers.go +++ b/vendor/github.com/docker/docker/errdefs/helpers.go @@ -14,7 +14,9 @@ func (e errNotFound) Unwrap() error { return e.error } -// NotFound is a helper to create an error of the class with the same name from any error type +// NotFound creates an [ErrNotFound] error from the given error. +// It returns the error as-is if it is either nil (no error) or already implements +// [ErrNotFound], func NotFound(err error) error { if err == nil || IsNotFound(err) { return err @@ -34,7 +36,9 @@ func (e errInvalidParameter) Unwrap() error { return e.error } -// InvalidParameter is a helper to create an error of the class with the same name from any error type +// InvalidParameter creates an [ErrInvalidParameter] error from the given error. +// It returns the error as-is if it is either nil (no error) or already implements +// [ErrInvalidParameter], func InvalidParameter(err error) error { if err == nil || IsInvalidParameter(err) { return err @@ -54,7 +58,9 @@ func (e errConflict) Unwrap() error { return e.error } -// Conflict is a helper to create an error of the class with the same name from any error type +// Conflict creates an [ErrConflict] error from the given error. +// It returns the error as-is if it is either nil (no error) or already implements +// [ErrConflict], func Conflict(err error) error { if err == nil || IsConflict(err) { return err @@ -74,7 +80,9 @@ func (e errUnauthorized) Unwrap() error { return e.error } -// Unauthorized is a helper to create an error of the class with the same name from any error type +// Unauthorized creates an [ErrUnauthorized] error from the given error. +// It returns the error as-is if it is either nil (no error) or already implements +// [ErrUnauthorized], func Unauthorized(err error) error { if err == nil || IsUnauthorized(err) { return err @@ -94,7 +102,9 @@ func (e errUnavailable) Unwrap() error { return e.error } -// Unavailable is a helper to create an error of the class with the same name from any error type +// Unavailable creates an [ErrUnavailable] error from the given error. +// It returns the error as-is if it is either nil (no error) or already implements +// [ErrUnavailable], func Unavailable(err error) error { if err == nil || IsUnavailable(err) { return err @@ -114,7 +124,9 @@ func (e errForbidden) Unwrap() error { return e.error } -// Forbidden is a helper to create an error of the class with the same name from any error type +// Forbidden creates an [ErrForbidden] error from the given error. +// It returns the error as-is if it is either nil (no error) or already implements +// [ErrForbidden], func Forbidden(err error) error { if err == nil || IsForbidden(err) { return err @@ -134,7 +146,9 @@ func (e errSystem) Unwrap() error { return e.error } -// System is a helper to create an error of the class with the same name from any error type +// System creates an [ErrSystem] error from the given error. +// It returns the error as-is if it is either nil (no error) or already implements +// [ErrSystem], func System(err error) error { if err == nil || IsSystem(err) { return err @@ -154,7 +168,9 @@ func (e errNotModified) Unwrap() error { return e.error } -// NotModified is a helper to create an error of the class with the same name from any error type +// NotModified creates an [ErrNotModified] error from the given error. +// It returns the error as-is if it is either nil (no error) or already implements +// [NotModified], func NotModified(err error) error { if err == nil || IsNotModified(err) { return err @@ -174,7 +190,9 @@ func (e errNotImplemented) Unwrap() error { return e.error } -// NotImplemented is a helper to create an error of the class with the same name from any error type +// NotImplemented creates an [ErrNotImplemented] error from the given error. +// It returns the error as-is if it is either nil (no error) or already implements +// [ErrNotImplemented], func NotImplemented(err error) error { if err == nil || IsNotImplemented(err) { return err @@ -194,7 +212,9 @@ func (e errUnknown) Unwrap() error { return e.error } -// Unknown is a helper to create an error of the class with the same name from any error type +// Unknown creates an [ErrUnknown] error from the given error. +// It returns the error as-is if it is either nil (no error) or already implements +// [ErrUnknown], func Unknown(err error) error { if err == nil || IsUnknown(err) { return err @@ -214,7 +234,9 @@ func (e errCancelled) Unwrap() error { return e.error } -// Cancelled is a helper to create an error of the class with the same name from any error type +// Cancelled creates an [ErrCancelled] error from the given error. +// It returns the error as-is if it is either nil (no error) or already implements +// [ErrCancelled], func Cancelled(err error) error { if err == nil || IsCancelled(err) { return err @@ -234,7 +256,9 @@ func (e errDeadline) Unwrap() error { return e.error } -// Deadline is a helper to create an error of the class with the same name from any error type +// Deadline creates an [ErrDeadline] error from the given error. +// It returns the error as-is if it is either nil (no error) or already implements +// [ErrDeadline], func Deadline(err error) error { if err == nil || IsDeadline(err) { return err @@ -254,7 +278,9 @@ func (e errDataLoss) Unwrap() error { return e.error } -// DataLoss is a helper to create an error of the class with the same name from any error type +// DataLoss creates an [ErrDataLoss] error from the given error. +// It returns the error as-is if it is either nil (no error) or already implements +// [ErrDataLoss], func DataLoss(err error) error { if err == nil || IsDataLoss(err) { return err diff --git a/vendor/github.com/docker/docker/errdefs/http_helpers.go b/vendor/github.com/docker/docker/errdefs/http_helpers.go index ebcd789302..0a8fadd48f 100644 --- a/vendor/github.com/docker/docker/errdefs/http_helpers.go +++ b/vendor/github.com/docker/docker/errdefs/http_helpers.go @@ -11,36 +11,37 @@ func FromStatusCode(err error, statusCode int) error { } switch statusCode { case http.StatusNotFound: - err = NotFound(err) + return NotFound(err) case http.StatusBadRequest: - err = InvalidParameter(err) + return InvalidParameter(err) case http.StatusConflict: - err = Conflict(err) + return Conflict(err) case http.StatusUnauthorized: - err = Unauthorized(err) + return Unauthorized(err) case http.StatusServiceUnavailable: - err = Unavailable(err) + return Unavailable(err) case http.StatusForbidden: - err = Forbidden(err) + return Forbidden(err) case http.StatusNotModified: - err = NotModified(err) + return NotModified(err) case http.StatusNotImplemented: - err = NotImplemented(err) + return NotImplemented(err) case http.StatusInternalServerError: - if !IsSystem(err) && !IsUnknown(err) && !IsDataLoss(err) && !IsDeadline(err) && !IsCancelled(err) { - err = System(err) + if IsCancelled(err) || IsSystem(err) || IsUnknown(err) || IsDataLoss(err) || IsDeadline(err) { + return err } + return System(err) default: switch { case statusCode >= 200 && statusCode < 400: // it's a client error + return err case statusCode >= 400 && statusCode < 500: - err = InvalidParameter(err) + return InvalidParameter(err) case statusCode >= 500 && statusCode < 600: - err = System(err) + return System(err) default: - err = Unknown(err) + return Unknown(err) } } - return err } diff --git a/vendor/github.com/docker/docker/errdefs/is.go b/vendor/github.com/docker/docker/errdefs/is.go index f94034cbd7..30ea7e6fec 100644 --- a/vendor/github.com/docker/docker/errdefs/is.go +++ b/vendor/github.com/docker/docker/errdefs/is.go @@ -39,79 +39,79 @@ func getImplementer(err error) error { } } -// IsNotFound returns if the passed in error is an ErrNotFound +// IsNotFound returns if the passed in error is an [ErrNotFound], func IsNotFound(err error) bool { _, ok := getImplementer(err).(ErrNotFound) return ok } -// IsInvalidParameter returns if the passed in error is an ErrInvalidParameter +// IsInvalidParameter returns if the passed in error is an [ErrInvalidParameter]. func IsInvalidParameter(err error) bool { _, ok := getImplementer(err).(ErrInvalidParameter) return ok } -// IsConflict returns if the passed in error is an ErrConflict +// IsConflict returns if the passed in error is an [ErrConflict]. func IsConflict(err error) bool { _, ok := getImplementer(err).(ErrConflict) return ok } -// IsUnauthorized returns if the passed in error is an ErrUnauthorized +// IsUnauthorized returns if the passed in error is an [ErrUnauthorized]. func IsUnauthorized(err error) bool { _, ok := getImplementer(err).(ErrUnauthorized) return ok } -// IsUnavailable returns if the passed in error is an ErrUnavailable +// IsUnavailable returns if the passed in error is an [ErrUnavailable]. func IsUnavailable(err error) bool { _, ok := getImplementer(err).(ErrUnavailable) return ok } -// IsForbidden returns if the passed in error is an ErrForbidden +// IsForbidden returns if the passed in error is an [ErrForbidden]. func IsForbidden(err error) bool { _, ok := getImplementer(err).(ErrForbidden) return ok } -// IsSystem returns if the passed in error is an ErrSystem +// IsSystem returns if the passed in error is an [ErrSystem]. func IsSystem(err error) bool { _, ok := getImplementer(err).(ErrSystem) return ok } -// IsNotModified returns if the passed in error is a NotModified error +// IsNotModified returns if the passed in error is an [ErrNotModified]. func IsNotModified(err error) bool { _, ok := getImplementer(err).(ErrNotModified) return ok } -// IsNotImplemented returns if the passed in error is an ErrNotImplemented +// IsNotImplemented returns if the passed in error is an [ErrNotImplemented]. func IsNotImplemented(err error) bool { _, ok := getImplementer(err).(ErrNotImplemented) return ok } -// IsUnknown returns if the passed in error is an ErrUnknown +// IsUnknown returns if the passed in error is an [ErrUnknown]. func IsUnknown(err error) bool { _, ok := getImplementer(err).(ErrUnknown) return ok } -// IsCancelled returns if the passed in error is an ErrCancelled +// IsCancelled returns if the passed in error is an [ErrCancelled]. func IsCancelled(err error) bool { _, ok := getImplementer(err).(ErrCancelled) return ok } -// IsDeadline returns if the passed in error is an ErrDeadline +// IsDeadline returns if the passed in error is an [ErrDeadline]. func IsDeadline(err error) bool { _, ok := getImplementer(err).(ErrDeadline) return ok } -// IsDataLoss returns if the passed in error is an ErrDataLoss +// IsDataLoss returns if the passed in error is an [ErrDataLoss]. func IsDataLoss(err error) bool { _, ok := getImplementer(err).(ErrDataLoss) return ok diff --git a/vendor/github.com/docker/docker/internal/lazyregexp/lazyregexp.go b/vendor/github.com/docker/docker/internal/lazyregexp/lazyregexp.go new file mode 100644 index 0000000000..6334edb60d --- /dev/null +++ b/vendor/github.com/docker/docker/internal/lazyregexp/lazyregexp.go @@ -0,0 +1,90 @@ +// Copyright 2018 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. + +// Code below was largely copied from golang.org/x/mod@v0.22; +// https://github.com/golang/mod/blob/v0.22.0/internal/lazyregexp/lazyre.go +// with some additional methods added. + +// Package lazyregexp is a thin wrapper over regexp, allowing the use of global +// regexp variables without forcing them to be compiled at init. +package lazyregexp + +import ( + "os" + "regexp" + "strings" + "sync" +) + +// Regexp is a wrapper around [regexp.Regexp], where the underlying regexp will be +// compiled the first time it is needed. +type Regexp struct { + str string + once sync.Once + rx *regexp.Regexp +} + +func (r *Regexp) re() *regexp.Regexp { + r.once.Do(r.build) + return r.rx +} + +func (r *Regexp) build() { + r.rx = regexp.MustCompile(r.str) + r.str = "" +} + +func (r *Regexp) FindSubmatch(s []byte) [][]byte { + return r.re().FindSubmatch(s) +} + +func (r *Regexp) FindAllStringSubmatch(s string, n int) [][]string { + return r.re().FindAllStringSubmatch(s, n) +} + +func (r *Regexp) FindStringSubmatch(s string) []string { + return r.re().FindStringSubmatch(s) +} + +func (r *Regexp) FindStringSubmatchIndex(s string) []int { + return r.re().FindStringSubmatchIndex(s) +} + +func (r *Regexp) ReplaceAllString(src, repl string) string { + return r.re().ReplaceAllString(src, repl) +} + +func (r *Regexp) FindString(s string) string { + return r.re().FindString(s) +} + +func (r *Regexp) FindAllString(s string, n int) []string { + return r.re().FindAllString(s, n) +} + +func (r *Regexp) MatchString(s string) bool { + return r.re().MatchString(s) +} + +func (r *Regexp) ReplaceAllStringFunc(src string, repl func(string) string) string { + return r.re().ReplaceAllStringFunc(src, repl) +} + +func (r *Regexp) SubexpNames() []string { + return r.re().SubexpNames() +} + +var inTest = len(os.Args) > 0 && strings.HasSuffix(strings.TrimSuffix(os.Args[0], ".exe"), ".test") + +// New creates a new lazy regexp, delaying the compiling work until it is first +// needed. If the code is being run as part of tests, the regexp compiling will +// happen immediately. +func New(str string) *Regexp { + lr := &Regexp{str: str} + if inTest { + // In tests, always compile the regexps early. + lr.re() + } + return lr +} diff --git a/vendor/github.com/docker/docker/pkg/ioutils/buffer.go b/vendor/github.com/docker/docker/pkg/ioutils/buffer.go deleted file mode 100644 index 466f79294b..0000000000 --- a/vendor/github.com/docker/docker/pkg/ioutils/buffer.go +++ /dev/null @@ -1,51 +0,0 @@ -package ioutils // import "github.com/docker/docker/pkg/ioutils" - -import ( - "errors" - "io" -) - -var errBufferFull = errors.New("buffer is full") - -type fixedBuffer struct { - buf []byte - pos int - lastRead int -} - -func (b *fixedBuffer) Write(p []byte) (int, error) { - n := copy(b.buf[b.pos:cap(b.buf)], p) - b.pos += n - - if n < len(p) { - if b.pos == cap(b.buf) { - return n, errBufferFull - } - return n, io.ErrShortWrite - } - return n, nil -} - -func (b *fixedBuffer) Read(p []byte) (int, error) { - n := copy(p, b.buf[b.lastRead:b.pos]) - b.lastRead += n - return n, nil -} - -func (b *fixedBuffer) Len() int { - return b.pos - b.lastRead -} - -func (b *fixedBuffer) Cap() int { - return cap(b.buf) -} - -func (b *fixedBuffer) Reset() { - b.pos = 0 - b.lastRead = 0 - b.buf = b.buf[:0] -} - -func (b *fixedBuffer) String() string { - return string(b.buf[b.lastRead:b.pos]) -} diff --git a/vendor/github.com/docker/docker/pkg/ioutils/bytespipe.go b/vendor/github.com/docker/docker/pkg/ioutils/bytespipe.go deleted file mode 100644 index 85450bf6b3..0000000000 --- a/vendor/github.com/docker/docker/pkg/ioutils/bytespipe.go +++ /dev/null @@ -1,193 +0,0 @@ -package ioutils // import "github.com/docker/docker/pkg/ioutils" - -import ( - "errors" - "io" - "sync" -) - -// maxCap is the highest capacity to use in byte slices that buffer data. -const maxCap = 1e6 - -// minCap is the lowest capacity to use in byte slices that buffer data -const minCap = 64 - -// blockThreshold is the minimum number of bytes in the buffer which will cause -// a write to BytesPipe to block when allocating a new slice. -const blockThreshold = 1e6 - -var ( - // ErrClosed is returned when Write is called on a closed BytesPipe. - // - // Deprecated: this type is only used internally, and will be removed in the next release. - ErrClosed = errors.New("write to closed BytesPipe") - - bufPools = make(map[int]*sync.Pool) - bufPoolsLock sync.Mutex -) - -// BytesPipe is io.ReadWriteCloser which works similarly to pipe(queue). -// All written data may be read at most once. Also, BytesPipe allocates -// and releases new byte slices to adjust to current needs, so the buffer -// won't be overgrown after peak loads. -// -// Deprecated: this type is only used internally, and will be removed in the next release. -type BytesPipe struct { - mu sync.Mutex - wait *sync.Cond - buf []*fixedBuffer - bufLen int - closeErr error // error to return from next Read. set to nil if not closed. - readBlock bool // check read BytesPipe is Wait() or not -} - -// NewBytesPipe creates new BytesPipe, initialized by specified slice. -// If buf is nil, then it will be initialized with slice which cap is 64. -// buf will be adjusted in a way that len(buf) == 0, cap(buf) == cap(buf). -// -// Deprecated: this function is only used internally, and will be removed in the next release. -func NewBytesPipe() *BytesPipe { - bp := &BytesPipe{} - bp.buf = append(bp.buf, getBuffer(minCap)) - bp.wait = sync.NewCond(&bp.mu) - return bp -} - -// Write writes p to BytesPipe. -// It can allocate new []byte slices in a process of writing. -func (bp *BytesPipe) Write(p []byte) (int, error) { - bp.mu.Lock() - defer bp.mu.Unlock() - - written := 0 -loop0: - for { - if bp.closeErr != nil { - return written, ErrClosed - } - - if len(bp.buf) == 0 { - bp.buf = append(bp.buf, getBuffer(64)) - } - // get the last buffer - b := bp.buf[len(bp.buf)-1] - - n, err := b.Write(p) - written += n - bp.bufLen += n - - // errBufferFull is an error we expect to get if the buffer is full - if err != nil && err != errBufferFull { - bp.wait.Broadcast() - return written, err - } - - // if there was enough room to write all then break - if len(p) == n { - break - } - - // more data: write to the next slice - p = p[n:] - - // make sure the buffer doesn't grow too big from this write - for bp.bufLen >= blockThreshold { - if bp.readBlock { - bp.wait.Broadcast() - } - bp.wait.Wait() - if bp.closeErr != nil { - continue loop0 - } - } - - // add new byte slice to the buffers slice and continue writing - nextCap := b.Cap() * 2 - if nextCap > maxCap { - nextCap = maxCap - } - bp.buf = append(bp.buf, getBuffer(nextCap)) - } - bp.wait.Broadcast() - return written, nil -} - -// CloseWithError causes further reads from a BytesPipe to return immediately. -func (bp *BytesPipe) CloseWithError(err error) error { - bp.mu.Lock() - if err != nil { - bp.closeErr = err - } else { - bp.closeErr = io.EOF - } - bp.wait.Broadcast() - bp.mu.Unlock() - return nil -} - -// Close causes further reads from a BytesPipe to return immediately. -func (bp *BytesPipe) Close() error { - return bp.CloseWithError(nil) -} - -// Read reads bytes from BytesPipe. -// Data could be read only once. -func (bp *BytesPipe) Read(p []byte) (n int, err error) { - bp.mu.Lock() - defer bp.mu.Unlock() - if bp.bufLen == 0 { - if bp.closeErr != nil { - return 0, bp.closeErr - } - bp.readBlock = true - bp.wait.Wait() - bp.readBlock = false - if bp.bufLen == 0 && bp.closeErr != nil { - return 0, bp.closeErr - } - } - - for bp.bufLen > 0 { - b := bp.buf[0] - read, _ := b.Read(p) // ignore error since fixedBuffer doesn't really return an error - n += read - bp.bufLen -= read - - if b.Len() == 0 { - // it's empty so return it to the pool and move to the next one - returnBuffer(b) - bp.buf[0] = nil - bp.buf = bp.buf[1:] - } - - if len(p) == read { - break - } - - p = p[read:] - } - - bp.wait.Broadcast() - return -} - -func returnBuffer(b *fixedBuffer) { - b.Reset() - bufPoolsLock.Lock() - pool := bufPools[b.Cap()] - bufPoolsLock.Unlock() - if pool != nil { - pool.Put(b) - } -} - -func getBuffer(size int) *fixedBuffer { - bufPoolsLock.Lock() - pool, ok := bufPools[size] - if !ok { - pool = &sync.Pool{New: func() interface{} { return &fixedBuffer{buf: make([]byte, 0, size)} }} - bufPools[size] = pool - } - bufPoolsLock.Unlock() - return pool.Get().(*fixedBuffer) -} diff --git a/vendor/github.com/docker/docker/pkg/ioutils/fswriters.go b/vendor/github.com/docker/docker/pkg/ioutils/fswriters.go deleted file mode 100644 index 05da97b0e4..0000000000 --- a/vendor/github.com/docker/docker/pkg/ioutils/fswriters.go +++ /dev/null @@ -1,163 +0,0 @@ -package ioutils // import "github.com/docker/docker/pkg/ioutils" - -import ( - "io" - "os" - "path/filepath" -) - -// NewAtomicFileWriter returns WriteCloser so that writing to it writes to a -// temporary file and closing it atomically changes the temporary file to -// destination path. Writing and closing concurrently is not allowed. -// NOTE: umask is not considered for the file's permissions. -func NewAtomicFileWriter(filename string, perm os.FileMode) (io.WriteCloser, error) { - f, err := os.CreateTemp(filepath.Dir(filename), ".tmp-"+filepath.Base(filename)) - if err != nil { - return nil, err - } - - abspath, err := filepath.Abs(filename) - if err != nil { - return nil, err - } - return &atomicFileWriter{ - f: f, - fn: abspath, - perm: perm, - }, nil -} - -// AtomicWriteFile atomically writes data to a file named by filename and with the specified permission bits. -// NOTE: umask is not considered for the file's permissions. -func AtomicWriteFile(filename string, data []byte, perm os.FileMode) error { - f, err := NewAtomicFileWriter(filename, perm) - if err != nil { - return err - } - n, err := f.Write(data) - if err == nil && n < len(data) { - err = io.ErrShortWrite - f.(*atomicFileWriter).writeErr = err - } - if err1 := f.Close(); err == nil { - err = err1 - } - return err -} - -type atomicFileWriter struct { - f *os.File - fn string - writeErr error - perm os.FileMode -} - -func (w *atomicFileWriter) Write(dt []byte) (int, error) { - n, err := w.f.Write(dt) - if err != nil { - w.writeErr = err - } - return n, err -} - -func (w *atomicFileWriter) Close() (retErr error) { - defer func() { - if retErr != nil || w.writeErr != nil { - os.Remove(w.f.Name()) - } - }() - if err := w.f.Sync(); err != nil { - w.f.Close() - return err - } - if err := w.f.Close(); err != nil { - return err - } - if err := os.Chmod(w.f.Name(), w.perm); err != nil { - return err - } - if w.writeErr == nil { - return os.Rename(w.f.Name(), w.fn) - } - return nil -} - -// AtomicWriteSet is used to atomically write a set -// of files and ensure they are visible at the same time. -// Must be committed to a new directory. -type AtomicWriteSet struct { - root string -} - -// NewAtomicWriteSet creates a new atomic write set to -// atomically create a set of files. The given directory -// is used as the base directory for storing files before -// commit. If no temporary directory is given the system -// default is used. -func NewAtomicWriteSet(tmpDir string) (*AtomicWriteSet, error) { - td, err := os.MkdirTemp(tmpDir, "write-set-") - if err != nil { - return nil, err - } - - return &AtomicWriteSet{ - root: td, - }, nil -} - -// WriteFile writes a file to the set, guaranteeing the file -// has been synced. -func (ws *AtomicWriteSet) WriteFile(filename string, data []byte, perm os.FileMode) error { - f, err := ws.FileWriter(filename, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, perm) - if err != nil { - return err - } - n, err := f.Write(data) - if err == nil && n < len(data) { - err = io.ErrShortWrite - } - if err1 := f.Close(); err == nil { - err = err1 - } - return err -} - -type syncFileCloser struct { - *os.File -} - -func (w syncFileCloser) Close() error { - err := w.File.Sync() - if err1 := w.File.Close(); err == nil { - err = err1 - } - return err -} - -// FileWriter opens a file writer inside the set. The file -// should be synced and closed before calling commit. -func (ws *AtomicWriteSet) FileWriter(name string, flag int, perm os.FileMode) (io.WriteCloser, error) { - f, err := os.OpenFile(filepath.Join(ws.root, name), flag, perm) - if err != nil { - return nil, err - } - return syncFileCloser{f}, nil -} - -// Cancel cancels the set and removes all temporary data -// created in the set. -func (ws *AtomicWriteSet) Cancel() error { - return os.RemoveAll(ws.root) -} - -// Commit moves all created files to the target directory. The -// target directory must not exist and the parent of the target -// directory must exist. -func (ws *AtomicWriteSet) Commit(target string) error { - return os.Rename(ws.root, target) -} - -// String returns the location the set is writing to. -func (ws *AtomicWriteSet) String() string { - return ws.root -} diff --git a/vendor/github.com/docker/docker/pkg/ioutils/readers.go b/vendor/github.com/docker/docker/pkg/ioutils/readers.go deleted file mode 100644 index e03d3fee75..0000000000 --- a/vendor/github.com/docker/docker/pkg/ioutils/readers.go +++ /dev/null @@ -1,172 +0,0 @@ -package ioutils // import "github.com/docker/docker/pkg/ioutils" - -import ( - "context" - "io" - "runtime/debug" - "sync/atomic" - - // make sure crypto.SHA256, crypto.sha512 and crypto.SHA384 are registered - // TODO remove once https://github.com/opencontainers/go-digest/pull/64 is merged. - _ "crypto/sha256" - _ "crypto/sha512" - - "github.com/containerd/log" -) - -// ReadCloserWrapper wraps an io.Reader, and implements an io.ReadCloser -// It calls the given callback function when closed. It should be constructed -// with NewReadCloserWrapper -type ReadCloserWrapper struct { - io.Reader - closer func() error - closed atomic.Bool -} - -// Close calls back the passed closer function -func (r *ReadCloserWrapper) Close() error { - if !r.closed.CompareAndSwap(false, true) { - subsequentCloseWarn("ReadCloserWrapper") - return nil - } - return r.closer() -} - -// NewReadCloserWrapper returns a new io.ReadCloser. -func NewReadCloserWrapper(r io.Reader, closer func() error) io.ReadCloser { - return &ReadCloserWrapper{ - Reader: r, - closer: closer, - } -} - -type readerErrWrapper struct { - reader io.Reader - closer func() -} - -func (r *readerErrWrapper) Read(p []byte) (int, error) { - n, err := r.reader.Read(p) - if err != nil { - r.closer() - } - return n, err -} - -// NewReaderErrWrapper returns a new io.Reader. -func NewReaderErrWrapper(r io.Reader, closer func()) io.Reader { - return &readerErrWrapper{ - reader: r, - closer: closer, - } -} - -// OnEOFReader wraps an io.ReadCloser and a function -// the function will run at the end of file or close the file. -type OnEOFReader struct { - Rc io.ReadCloser - Fn func() -} - -func (r *OnEOFReader) Read(p []byte) (n int, err error) { - n, err = r.Rc.Read(p) - if err == io.EOF { - r.runFunc() - } - return -} - -// Close closes the file and run the function. -func (r *OnEOFReader) Close() error { - err := r.Rc.Close() - r.runFunc() - return err -} - -func (r *OnEOFReader) runFunc() { - if fn := r.Fn; fn != nil { - fn() - r.Fn = nil - } -} - -// cancelReadCloser wraps an io.ReadCloser with a context for cancelling read -// operations. -type cancelReadCloser struct { - cancel func() - pR *io.PipeReader // Stream to read from - pW *io.PipeWriter - closed atomic.Bool -} - -// NewCancelReadCloser creates a wrapper that closes the ReadCloser when the -// context is cancelled. The returned io.ReadCloser must be closed when it is -// no longer needed. -func NewCancelReadCloser(ctx context.Context, in io.ReadCloser) io.ReadCloser { - pR, pW := io.Pipe() - - // Create a context used to signal when the pipe is closed - doneCtx, cancel := context.WithCancel(context.Background()) - - p := &cancelReadCloser{ - cancel: cancel, - pR: pR, - pW: pW, - } - - go func() { - _, err := io.Copy(pW, in) - select { - case <-ctx.Done(): - // If the context was closed, p.closeWithError - // was already called. Calling it again would - // change the error that Read returns. - default: - p.closeWithError(err) - } - in.Close() - }() - go func() { - for { - select { - case <-ctx.Done(): - p.closeWithError(ctx.Err()) - case <-doneCtx.Done(): - return - } - } - }() - - return p -} - -// Read wraps the Read method of the pipe that provides data from the wrapped -// ReadCloser. -func (p *cancelReadCloser) Read(buf []byte) (n int, err error) { - return p.pR.Read(buf) -} - -// closeWithError closes the wrapper and its underlying reader. It will -// cause future calls to Read to return err. -func (p *cancelReadCloser) closeWithError(err error) { - p.pW.CloseWithError(err) - p.cancel() -} - -// Close closes the wrapper its underlying reader. It will cause -// future calls to Read to return io.EOF. -func (p *cancelReadCloser) Close() error { - if !p.closed.CompareAndSwap(false, true) { - subsequentCloseWarn("cancelReadCloser") - return nil - } - p.closeWithError(io.EOF) - return nil -} - -func subsequentCloseWarn(name string) { - log.G(context.TODO()).Error("subsequent attempt to close " + name) - if log.GetLevel() >= log.DebugLevel { - log.G(context.TODO()).Errorf("stack trace: %s", string(debug.Stack())) - } -} diff --git a/vendor/github.com/docker/docker/pkg/ioutils/writeflusher.go b/vendor/github.com/docker/docker/pkg/ioutils/writeflusher.go deleted file mode 100644 index d8a8893ff1..0000000000 --- a/vendor/github.com/docker/docker/pkg/ioutils/writeflusher.go +++ /dev/null @@ -1,98 +0,0 @@ -package ioutils // import "github.com/docker/docker/pkg/ioutils" - -import ( - "io" - "sync" -) - -// WriteFlusher wraps the Write and Flush operation ensuring that every write -// is a flush. In addition, the Close method can be called to intercept -// Read/Write calls if the targets lifecycle has already ended. -type WriteFlusher struct { - w io.Writer - flusher flusher - flushed chan struct{} - flushedOnce sync.Once - closed chan struct{} - closeLock sync.Mutex -} - -type flusher interface { - Flush() -} - -var errWriteFlusherClosed = io.EOF - -func (wf *WriteFlusher) Write(b []byte) (n int, err error) { - select { - case <-wf.closed: - return 0, errWriteFlusherClosed - default: - } - - n, err = wf.w.Write(b) - wf.Flush() // every write is a flush. - return n, err -} - -// Flush the stream immediately. -func (wf *WriteFlusher) Flush() { - select { - case <-wf.closed: - return - default: - } - - wf.flushedOnce.Do(func() { - close(wf.flushed) - }) - wf.flusher.Flush() -} - -// Flushed returns the state of flushed. -// If it's flushed, return true, or else it return false. -func (wf *WriteFlusher) Flushed() bool { - // BUG(stevvooe): Remove this method. Its use is inherently racy. Seems to - // be used to detect whether or a response code has been issued or not. - // Another hook should be used instead. - var flushed bool - select { - case <-wf.flushed: - flushed = true - default: - } - return flushed -} - -// Close closes the write flusher, disallowing any further writes to the -// target. After the flusher is closed, all calls to write or flush will -// result in an error. -func (wf *WriteFlusher) Close() error { - wf.closeLock.Lock() - defer wf.closeLock.Unlock() - - select { - case <-wf.closed: - return errWriteFlusherClosed - default: - close(wf.closed) - } - return nil -} - -// nopFlusher represents a type which flush operation is nop. -type nopFlusher struct{} - -// Flush is a nop operation. -func (f *nopFlusher) Flush() {} - -// NewWriteFlusher returns a new WriteFlusher. -func NewWriteFlusher(w io.Writer) *WriteFlusher { - var fl flusher - if f, ok := w.(flusher); ok { - fl = f - } else { - fl = &nopFlusher{} - } - return &WriteFlusher{w: w, flusher: fl, closed: make(chan struct{}), flushed: make(chan struct{})} -} diff --git a/vendor/github.com/docker/docker/pkg/ioutils/writers.go b/vendor/github.com/docker/docker/pkg/ioutils/writers.go deleted file mode 100644 index aec8b4c03e..0000000000 --- a/vendor/github.com/docker/docker/pkg/ioutils/writers.go +++ /dev/null @@ -1,81 +0,0 @@ -package ioutils // import "github.com/docker/docker/pkg/ioutils" - -import ( - "io" - "sync/atomic" -) - -// NopWriter represents a type which write operation is nop. -// -// Deprecated: use [io.Discard] instead. This type will be removed in the next release. -type NopWriter struct{} - -func (*NopWriter) Write(buf []byte) (int, error) { - return len(buf), nil -} - -type nopWriteCloser struct { - io.Writer -} - -func (w *nopWriteCloser) Close() error { return nil } - -// NopWriteCloser returns a nopWriteCloser. -// -// Deprecated: This function is no longer used and will be removed in the next release. -func NopWriteCloser(w io.Writer) io.WriteCloser { - return &nopWriteCloser{w} -} - -// NopFlusher represents a type which flush operation is nop. -// -// Deprecated: NopFlusher is only used internally and will be removed in the next release. -type NopFlusher = nopFlusher - -type writeCloserWrapper struct { - io.Writer - closer func() error - closed atomic.Bool -} - -func (r *writeCloserWrapper) Close() error { - if !r.closed.CompareAndSwap(false, true) { - subsequentCloseWarn("WriteCloserWrapper") - return nil - } - return r.closer() -} - -// NewWriteCloserWrapper returns a new io.WriteCloser. -func NewWriteCloserWrapper(r io.Writer, closer func() error) io.WriteCloser { - return &writeCloserWrapper{ - Writer: r, - closer: closer, - } -} - -// WriteCounter wraps a concrete io.Writer and hold a count of the number -// of bytes written to the writer during a "session". -// This can be convenient when write return is masked -// (e.g., json.Encoder.Encode()) -// -// Deprecated: this type is no longer used and will be removed in the next release. -type WriteCounter struct { - Count int64 - Writer io.Writer -} - -// NewWriteCounter returns a new WriteCounter. -// -// Deprecated: this function is no longer used and will be removed in the next release. -func NewWriteCounter(w io.Writer) *WriteCounter { - return &WriteCounter{ - Writer: w, - } -} - -func (wc *WriteCounter) Write(p []byte) (count int, err error) { - count, err = wc.Writer.Write(p) - wc.Count += int64(count) - return -} diff --git a/vendor/github.com/docker/docker/registry/auth.go b/vendor/github.com/docker/docker/registry/auth.go index 905ccf5f51..8c62b83c07 100644 --- a/vendor/github.com/docker/docker/registry/auth.go +++ b/vendor/github.com/docker/docker/registry/auth.go @@ -66,23 +66,23 @@ func (scs staticCredentialStore) SetRefreshToken(*url.URL, string, string) { // loginV2 tries to login to the v2 registry server. The given registry // endpoint will be pinged to get authorization challenges. These challenges // will be used to authenticate against the registry to validate credentials. -func loginV2(authConfig *registry.AuthConfig, endpoint APIEndpoint, userAgent string) (string, string, error) { - var ( - endpointStr = strings.TrimRight(endpoint.URL.String(), "/") + "/v2/" - modifiers = Headers(userAgent, nil) - authTransport = transport.NewTransport(newTransport(endpoint.TLSConfig), modifiers...) - credentialAuthConfig = *authConfig - creds = loginCredentialStore{authConfig: &credentialAuthConfig} - ) - +func loginV2(authConfig *registry.AuthConfig, endpoint APIEndpoint, userAgent string) (status string, token string, _ error) { + endpointStr := strings.TrimRight(endpoint.URL.String(), "/") + "/v2/" log.G(context.TODO()).Debugf("attempting v2 login to registry endpoint %s", endpointStr) - loginClient, err := v2AuthHTTPClient(endpoint.URL, authTransport, modifiers, creds, nil) + req, err := http.NewRequest(http.MethodGet, endpointStr, nil) if err != nil { return "", "", err } - req, err := http.NewRequest(http.MethodGet, endpointStr, nil) + var ( + modifiers = Headers(userAgent, nil) + authTrans = transport.NewTransport(newTransport(endpoint.TLSConfig), modifiers...) + credentialAuthConfig = *authConfig + creds = loginCredentialStore{authConfig: &credentialAuthConfig} + ) + + loginClient, err := v2AuthHTTPClient(endpoint.URL, authTrans, modifiers, creds, nil) if err != nil { return "", "", err } @@ -133,12 +133,13 @@ func v2AuthHTTPClient(endpoint *url.URL, authTransport http.RoundTripper, modifi // files). func ConvertToHostname(url string) string { stripped := url - if strings.HasPrefix(url, "http://") { - stripped = strings.TrimPrefix(url, "http://") - } else if strings.HasPrefix(url, "https://") { - stripped = strings.TrimPrefix(url, "https://") + if strings.HasPrefix(stripped, "http://") { + stripped = strings.TrimPrefix(stripped, "http://") + } else if strings.HasPrefix(stripped, "https://") { + stripped = strings.TrimPrefix(stripped, "https://") } - return strings.SplitN(stripped, "/", 2)[0] + stripped, _, _ = strings.Cut(stripped, "/") + return stripped } // ResolveAuthConfig matches an auth configuration to a server address or a URL diff --git a/vendor/github.com/docker/docker/registry/config.go b/vendor/github.com/docker/docker/registry/config.go index e1b0a0ca14..f8d94ce806 100644 --- a/vendor/github.com/docker/docker/registry/config.go +++ b/vendor/github.com/docker/docker/registry/config.go @@ -4,20 +4,21 @@ import ( "context" "net" "net/url" - "regexp" "strconv" "strings" "github.com/containerd/log" "github.com/distribution/reference" "github.com/docker/docker/api/types/registry" + "github.com/docker/docker/internal/lazyregexp" ) // ServiceOptions holds command line options. type ServiceOptions struct { - AllowNondistributableArtifacts []string `json:"allow-nondistributable-artifacts,omitempty"` - Mirrors []string `json:"registry-mirrors,omitempty"` - InsecureRegistries []string `json:"insecure-registries,omitempty"` + AllowNondistributableArtifacts []string `json:"allow-nondistributable-artifacts,omitempty"` // Deprecated: non-distributable artifacts are deprecated and enabled by default. This field will be removed in the next release. + + Mirrors []string `json:"registry-mirrors,omitempty"` + InsecureRegistries []string `json:"insecure-registries,omitempty"` } // serviceConfig holds daemon configuration for the registry service. @@ -56,10 +57,7 @@ var ( } emptyServiceConfig, _ = newServiceConfig(ServiceOptions{}) - validHostPortRegex = regexp.MustCompile(`^` + reference.DomainRegexp.String() + `$`) - - // for mocking in unit tests - lookupIP = net.LookupIP + validHostPortRegex = lazyregexp.New(`^` + reference.DomainRegexp.String() + `$`) // certsDir is used to override defaultCertsDir. certsDir string @@ -83,9 +81,6 @@ func CertsDir() string { // newServiceConfig returns a new instance of ServiceConfig func newServiceConfig(options ServiceOptions) (*serviceConfig, error) { config := &serviceConfig{} - if err := config.loadAllowNondistributableArtifacts(options.AllowNondistributableArtifacts); err != nil { - return nil, err - } if err := config.loadMirrors(options.Mirrors); err != nil { return nil, err } @@ -103,51 +98,12 @@ func (config *serviceConfig) copy() *registry.ServiceConfig { ic[key] = value } return ®istry.ServiceConfig{ - AllowNondistributableArtifactsCIDRs: append([]*registry.NetIPNet(nil), config.AllowNondistributableArtifactsCIDRs...), - AllowNondistributableArtifactsHostnames: append([]string(nil), config.AllowNondistributableArtifactsHostnames...), - InsecureRegistryCIDRs: append([]*registry.NetIPNet(nil), config.InsecureRegistryCIDRs...), - IndexConfigs: ic, - Mirrors: append([]string(nil), config.Mirrors...), + InsecureRegistryCIDRs: append([]*registry.NetIPNet(nil), config.InsecureRegistryCIDRs...), + IndexConfigs: ic, + Mirrors: append([]string(nil), config.Mirrors...), } } -// loadAllowNondistributableArtifacts loads allow-nondistributable-artifacts registries into config. -func (config *serviceConfig) loadAllowNondistributableArtifacts(registries []string) error { - cidrs := map[string]*registry.NetIPNet{} - hostnames := map[string]bool{} - - for _, r := range registries { - if _, err := ValidateIndexName(r); err != nil { - return err - } - if hasScheme(r) { - return invalidParamf("allow-nondistributable-artifacts registry %s should not contain '://'", r) - } - - if _, ipnet, err := net.ParseCIDR(r); err == nil { - // Valid CIDR. - cidrs[ipnet.String()] = (*registry.NetIPNet)(ipnet) - } else if err = validateHostPort(r); err == nil { - // Must be `host:port` if not CIDR. - hostnames[r] = true - } else { - return invalidParamWrapf(err, "allow-nondistributable-artifacts registry %s is not valid", r) - } - } - - config.AllowNondistributableArtifactsCIDRs = make([]*registry.NetIPNet, 0, len(cidrs)) - for _, c := range cidrs { - config.AllowNondistributableArtifactsCIDRs = append(config.AllowNondistributableArtifactsCIDRs, c) - } - - config.AllowNondistributableArtifactsHostnames = make([]string, 0, len(hostnames)) - for h := range hostnames { - config.AllowNondistributableArtifactsHostnames = append(config.AllowNondistributableArtifactsHostnames, h) - } - - return nil -} - // loadMirrors loads mirrors to config, after removing duplicates. // Returns an error if mirrors contains an invalid mirror. func (config *serviceConfig) loadMirrors(mirrors []string) error { @@ -184,7 +140,7 @@ func (config *serviceConfig) loadMirrors(mirrors []string) error { func (config *serviceConfig) loadInsecureRegistries(registries []string) error { // Localhost is by default considered as an insecure registry. This is a // stop-gap for people who are running a private registry on localhost. - registries = append(registries, "127.0.0.0/8") + registries = append(registries, "::1/128", "127.0.0.0/8") var ( insecureRegistryCIDRs = make([]*registry.NetIPNet, 0) @@ -245,25 +201,6 @@ skip: return nil } -// allowNondistributableArtifacts returns true if the provided hostname is part of the list of registries -// that allow push of nondistributable artifacts. -// -// The list can contain elements with CIDR notation to specify a whole subnet. If the subnet contains an IP -// of the registry specified by hostname, true is returned. -// -// hostname should be a URL.Host (`host:port` or `host`) where the `host` part can be either a domain name -// or an IP address. If it is a domain name, then it will be resolved to IP addresses for matching. If -// resolution fails, CIDR matching is not performed. -func (config *serviceConfig) allowNondistributableArtifacts(hostname string) bool { - for _, h := range config.AllowNondistributableArtifactsHostnames { - if h == hostname { - return true - } - } - - return isCIDRMatch(config.AllowNondistributableArtifactsCIDRs, hostname) -} - // isSecureIndex returns false if the provided indexName is part of the list of insecure registries // Insecure registries accept HTTP and/or accept HTTPS with certificates from unknown CAs. // @@ -285,30 +222,37 @@ func (config *serviceConfig) isSecureIndex(indexName string) bool { return !isCIDRMatch(config.InsecureRegistryCIDRs, indexName) } +// for mocking in unit tests. +var lookupIP = net.LookupIP + // isCIDRMatch returns true if URLHost matches an element of cidrs. URLHost is a URL.Host (`host:port` or `host`) // where the `host` part can be either a domain name or an IP address. If it is a domain name, then it will be // resolved to IP addresses for matching. If resolution fails, false is returned. func isCIDRMatch(cidrs []*registry.NetIPNet, URLHost string) bool { + if len(cidrs) == 0 { + return false + } + host, _, err := net.SplitHostPort(URLHost) if err != nil { - // Assume URLHost is of the form `host` without the port and go on. + // Assume URLHost is a host without port and go on. host = URLHost } - addrs, err := lookupIP(host) - if err != nil { - ip := net.ParseIP(host) - if ip != nil { - addrs = []net.IP{ip} + var addresses []net.IP + if ip := net.ParseIP(host); ip != nil { + // Host is an IP-address. + addresses = append(addresses, ip) + } else { + // Try to resolve the host's IP-address. + addresses, err = lookupIP(host) + if err != nil { + // We failed to resolve the host; assume there's no match. + return false } - - // if ip == nil, then `host` is neither an IP nor it could be looked up, - // either because the index is unreachable, or because the index is behind an HTTP proxy. - // So, len(addrs) == 0 and we're not aborting. } - // Try CIDR notation only if addrs has any elements, i.e. if `host`'s IP could be determined. - for _, addr := range addrs { + for _, addr := range addresses { for _, ipnet := range cidrs { // check if the addr falls in the subnet if (*net.IPNet)(ipnet).Contains(addr) { diff --git a/vendor/github.com/docker/docker/registry/registry.go b/vendor/github.com/docker/docker/registry/registry.go index 7866dcd0d8..6b079199dd 100644 --- a/vendor/github.com/docker/docker/registry/registry.go +++ b/vendor/github.com/docker/docker/registry/registry.go @@ -14,6 +14,7 @@ import ( "github.com/containerd/log" "github.com/docker/distribution/registry/client/transport" "github.com/docker/go-connections/tlsconfig" + "go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp" ) // HostCertsDir returns the config directory for a specific host. @@ -115,7 +116,7 @@ func Headers(userAgent string, metaHeaders http.Header) []transport.RequestModif // newTransport returns a new HTTP transport. If tlsConfig is nil, it uses the // default TLS configuration. -func newTransport(tlsConfig *tls.Config) *http.Transport { +func newTransport(tlsConfig *tls.Config) http.RoundTripper { if tlsConfig == nil { tlsConfig = tlsconfig.ServerDefault() } @@ -125,12 +126,14 @@ func newTransport(tlsConfig *tls.Config) *http.Transport { KeepAlive: 30 * time.Second, } - return &http.Transport{ - Proxy: http.ProxyFromEnvironment, - DialContext: direct.DialContext, - TLSHandshakeTimeout: 10 * time.Second, - TLSClientConfig: tlsConfig, - // TODO(dmcgowan): Call close idle connections when complete and use keep alive - DisableKeepAlives: true, - } + return otelhttp.NewTransport( + &http.Transport{ + Proxy: http.ProxyFromEnvironment, + DialContext: direct.DialContext, + TLSHandshakeTimeout: 10 * time.Second, + TLSClientConfig: tlsConfig, + // TODO(dmcgowan): Call close idle connections when complete and use keep alive + DisableKeepAlives: true, + }, + ) } diff --git a/vendor/github.com/docker/docker/registry/search_session.go b/vendor/github.com/docker/docker/registry/search_session.go index c334143c6b..a0d25c805e 100644 --- a/vendor/github.com/docker/docker/registry/search_session.go +++ b/vendor/github.com/docker/docker/registry/search_session.go @@ -6,6 +6,7 @@ import ( _ "crypto/sha512" "encoding/json" "fmt" + "io" "net/http" "net/http/cookiejar" "net/url" @@ -15,7 +16,6 @@ import ( "github.com/containerd/log" "github.com/docker/docker/api/types/registry" "github.com/docker/docker/errdefs" - "github.com/docker/docker/pkg/ioutils" "github.com/pkg/errors" ) @@ -76,6 +76,35 @@ func cloneRequest(r *http.Request) *http.Request { return r2 } +// onEOFReader wraps an io.ReadCloser and a function +// the function will run at the end of file or close the file. +type onEOFReader struct { + Rc io.ReadCloser + Fn func() +} + +func (r *onEOFReader) Read(p []byte) (n int, err error) { + n, err = r.Rc.Read(p) + if err == io.EOF { + r.runFunc() + } + return +} + +// Close closes the file and run the function. +func (r *onEOFReader) Close() error { + err := r.Rc.Close() + r.runFunc() + return err +} + +func (r *onEOFReader) runFunc() { + if fn := r.Fn; fn != nil { + fn() + r.Fn = nil + } +} + // RoundTrip changes an HTTP request's headers to add the necessary // authentication-related headers func (tr *authTransport) RoundTrip(orig *http.Request) (*http.Response, error) { @@ -119,7 +148,7 @@ func (tr *authTransport) RoundTrip(orig *http.Request) (*http.Response, error) { if len(resp.Header["X-Docker-Token"]) > 0 { tr.token = resp.Header["X-Docker-Token"] } - resp.Body = &ioutils.OnEOFReader{ + resp.Body = &onEOFReader{ Rc: resp.Body, Fn: func() { tr.mu.Lock() diff --git a/vendor/github.com/docker/docker/registry/service.go b/vendor/github.com/docker/docker/registry/service.go index 6881c11057..4d66523c61 100644 --- a/vendor/github.com/docker/docker/registry/service.go +++ b/vendor/github.com/docker/docker/registry/service.go @@ -68,10 +68,11 @@ func (s *Service) Auth(ctx context.Context, authConfig *registry.AuthConfig, use registryHostName = u.Host } - // Lookup endpoints for authentication using "LookupPushEndpoints", which - // excludes mirrors to prevent sending credentials of the upstream registry - // to a mirror. - endpoints, err := s.LookupPushEndpoints(registryHostName) + // Lookup endpoints for authentication but exclude mirrors to prevent + // sending credentials of the upstream registry to a mirror. + s.mu.RLock() + endpoints, err := s.lookupV2Endpoints(registryHostName, false) + s.mu.RUnlock() if err != nil { return "", "", invalidParam(err) } @@ -103,10 +104,9 @@ func (s *Service) ResolveRepository(name reference.Named) (*RepositoryInfo, erro type APIEndpoint struct { Mirror bool URL *url.URL - Version APIVersion // Deprecated: v1 registries are deprecated, and endpoints are always v2. - AllowNondistributableArtifacts bool + AllowNondistributableArtifacts bool // Deprecated: non-distributable artifacts are deprecated and enabled by default. This field will be removed in the next release. Official bool - TrimHostname bool + TrimHostname bool // Deprecated: hostname is now trimmed unconditionally for remote names. This field will be removed in the next release. TLSConfig *tls.Config } @@ -116,7 +116,7 @@ func (s *Service) LookupPullEndpoints(hostname string) (endpoints []APIEndpoint, s.mu.RLock() defer s.mu.RUnlock() - return s.lookupV2Endpoints(hostname) + return s.lookupV2Endpoints(hostname, true) } // LookupPushEndpoints creates a list of v2 endpoints to try to push to, in order of preference. @@ -125,15 +125,7 @@ func (s *Service) LookupPushEndpoints(hostname string) (endpoints []APIEndpoint, s.mu.RLock() defer s.mu.RUnlock() - allEndpoints, err := s.lookupV2Endpoints(hostname) - if err == nil { - for _, endpoint := range allEndpoints { - if !endpoint.Mirror { - endpoints = append(endpoints, endpoint) - } - } - } - return endpoints, err + return s.lookupV2Endpoints(hostname, false) } // IsInsecureRegistry returns true if the registry at given host is configured as diff --git a/vendor/github.com/docker/docker/registry/service_v2.go b/vendor/github.com/docker/docker/registry/service_v2.go index 5d09e11c9c..43754527a2 100644 --- a/vendor/github.com/docker/docker/registry/service_v2.go +++ b/vendor/github.com/docker/docker/registry/service_v2.go @@ -7,38 +7,33 @@ import ( "github.com/docker/go-connections/tlsconfig" ) -func (s *Service) lookupV2Endpoints(hostname string) (endpoints []APIEndpoint, err error) { - ana := s.config.allowNondistributableArtifacts(hostname) - +func (s *Service) lookupV2Endpoints(hostname string, includeMirrors bool) ([]APIEndpoint, error) { + var endpoints []APIEndpoint if hostname == DefaultNamespace || hostname == IndexHostname { - for _, mirror := range s.config.Mirrors { - if !strings.HasPrefix(mirror, "http://") && !strings.HasPrefix(mirror, "https://") { - mirror = "https://" + mirror - } - mirrorURL, err := url.Parse(mirror) - if err != nil { - return nil, invalidParam(err) - } - mirrorTLSConfig, err := newTLSConfig(mirrorURL.Host, s.config.isSecureIndex(mirrorURL.Host)) - if err != nil { - return nil, err + if includeMirrors { + for _, mirror := range s.config.Mirrors { + if !strings.HasPrefix(mirror, "http://") && !strings.HasPrefix(mirror, "https://") { + mirror = "https://" + mirror + } + mirrorURL, err := url.Parse(mirror) + if err != nil { + return nil, invalidParam(err) + } + mirrorTLSConfig, err := newTLSConfig(mirrorURL.Host, s.config.isSecureIndex(mirrorURL.Host)) + if err != nil { + return nil, err + } + endpoints = append(endpoints, APIEndpoint{ + URL: mirrorURL, + Mirror: true, + TLSConfig: mirrorTLSConfig, + }) } - endpoints = append(endpoints, APIEndpoint{ - URL: mirrorURL, - Version: APIVersion2, //nolint:staticcheck // ignore SA1019 (Version is deprecated) to allow potential consumers to transition. - Mirror: true, - TrimHostname: true, - TLSConfig: mirrorTLSConfig, - }) } endpoints = append(endpoints, APIEndpoint{ - URL: DefaultV2Registry, - Version: APIVersion2, //nolint:staticcheck // ignore SA1019 (Version is deprecated) to allow potential consumers to transition. - Official: true, - TrimHostname: true, - TLSConfig: tlsconfig.ServerDefault(), - - AllowNondistributableArtifacts: ana, + URL: DefaultV2Registry, + Official: true, + TLSConfig: tlsconfig.ServerDefault(), }) return endpoints, nil @@ -55,10 +50,7 @@ func (s *Service) lookupV2Endpoints(hostname string) (endpoints []APIEndpoint, e Scheme: "https", Host: hostname, }, - Version: APIVersion2, //nolint:staticcheck // ignore SA1019 (Version is deprecated) to allow potential consumers to transition. - AllowNondistributableArtifacts: ana, - TrimHostname: true, - TLSConfig: tlsConfig, + TLSConfig: tlsConfig, }, } @@ -68,9 +60,6 @@ func (s *Service) lookupV2Endpoints(hostname string) (endpoints []APIEndpoint, e Scheme: "http", Host: hostname, }, - Version: APIVersion2, //nolint:staticcheck // ignore SA1019 (Version is deprecated) to allow potential consumers to transition. - AllowNondistributableArtifacts: ana, - TrimHostname: true, // used to check if supposed to be secure via InsecureSkipVerify TLSConfig: tlsConfig, }) diff --git a/vendor/github.com/docker/docker/registry/types.go b/vendor/github.com/docker/docker/registry/types.go index 4926580a6c..02d7f4f383 100644 --- a/vendor/github.com/docker/docker/registry/types.go +++ b/vendor/github.com/docker/docker/registry/types.go @@ -5,27 +5,6 @@ import ( "github.com/docker/docker/api/types/registry" ) -// APIVersion is an integral representation of an API version (presently -// either 1 or 2) -// -// Deprecated: v1 registries are deprecated, and endpoints are always v2. -type APIVersion int - -func (av APIVersion) String() string { - return apiVersions[av] -} - -// API Version identifiers. -const ( - APIVersion1 APIVersion = 1 // Deprecated: v1 registries are deprecated, and endpoints are always v2. - APIVersion2 APIVersion = 2 // Deprecated: v1 registries are deprecated, and endpoints are always v2. -) - -var apiVersions = map[APIVersion]string{ - APIVersion1: "v1", - APIVersion2: "v2", -} - // RepositoryInfo describes a repository type RepositoryInfo struct { Name reference.Named diff --git a/vendor/github.com/go-openapi/errors/.golangci.yml b/vendor/github.com/go-openapi/errors/.golangci.yml index cf88ead324..ee8b9bd1f1 100644 --- a/vendor/github.com/go-openapi/errors/.golangci.yml +++ b/vendor/github.com/go-openapi/errors/.golangci.yml @@ -1,12 +1,6 @@ linters-settings: - govet: - check-shadowing: true - golint: - min-confidence: 0 gocyclo: min-complexity: 45 - maligned: - suggest-new: true dupl: threshold: 200 goconst: @@ -16,8 +10,6 @@ linters-settings: linters: enable-all: true disable: - - errname # this repo doesn't follow the convention advised by this linter - - maligned - unparam - lll - gochecknoinits @@ -30,9 +22,6 @@ linters: - wrapcheck - testpackage - nlreturn - - gomnd - - exhaustivestruct - - goerr113 - errorlint - nestif - godot @@ -40,7 +29,6 @@ linters: - paralleltest - tparallel - thelper - - ifshort - exhaustruct - varnamelen - gci @@ -53,10 +41,15 @@ linters: - forcetypeassert - cyclop # deprecated linters - - deadcode - - interfacer - - scopelint - - varcheck - - structcheck - - golint - - nosnakecase + #- deadcode + #- interfacer + #- scopelint + #- varcheck + #- structcheck + #- golint + #- nosnakecase + #- maligned + #- goerr113 + #- ifshort + #- gomnd + #- exhaustivestruct diff --git a/vendor/github.com/go-openapi/errors/api.go b/vendor/github.com/go-openapi/errors/api.go index 5320cb9630..d6f507f42d 100644 --- a/vendor/github.com/go-openapi/errors/api.go +++ b/vendor/github.com/go-openapi/errors/api.go @@ -185,7 +185,7 @@ func ServeError(rw http.ResponseWriter, r *http.Request, err error) { } func asHTTPCode(input int) int { - if input >= 600 { + if input >= maximumValidHTTPCode { return DefaultHTTPCode } return input diff --git a/vendor/github.com/go-openapi/errors/headers.go b/vendor/github.com/go-openapi/errors/headers.go index dfebe8f95f..6ea1151f41 100644 --- a/vendor/github.com/go-openapi/errors/headers.go +++ b/vendor/github.com/go-openapi/errors/headers.go @@ -21,7 +21,7 @@ import ( ) // Validation represents a failure of a precondition -type Validation struct { +type Validation struct { //nolint: errname code int32 Name string In string diff --git a/vendor/github.com/go-openapi/errors/middleware.go b/vendor/github.com/go-openapi/errors/middleware.go index 963472d1f3..67f80386a2 100644 --- a/vendor/github.com/go-openapi/errors/middleware.go +++ b/vendor/github.com/go-openapi/errors/middleware.go @@ -22,7 +22,7 @@ import ( // APIVerificationFailed is an error that contains all the missing info for a mismatched section // between the api registrations and the api spec -type APIVerificationFailed struct { +type APIVerificationFailed struct { //nolint: errname Section string `json:"section,omitempty"` MissingSpecification []string `json:"missingSpecification,omitempty"` MissingRegistration []string `json:"missingRegistration,omitempty"` diff --git a/vendor/github.com/go-openapi/errors/parsing.go b/vendor/github.com/go-openapi/errors/parsing.go index 5096e1ea7b..ce1ef9cb67 100644 --- a/vendor/github.com/go-openapi/errors/parsing.go +++ b/vendor/github.com/go-openapi/errors/parsing.go @@ -17,6 +17,7 @@ package errors import ( "encoding/json" "fmt" + "net/http" ) // ParseError represents a parsing error @@ -68,7 +69,7 @@ func NewParseError(name, in, value string, reason error) *ParseError { msg = fmt.Sprintf(parseErrorTemplContent, name, in, value, reason) } return &ParseError{ - code: 400, + code: http.StatusBadRequest, Name: name, In: in, Value: value, diff --git a/vendor/github.com/go-openapi/errors/schema.go b/vendor/github.com/go-openapi/errors/schema.go index cf7ac2ed4d..8f3239dfd9 100644 --- a/vendor/github.com/go-openapi/errors/schema.go +++ b/vendor/github.com/go-openapi/errors/schema.go @@ -17,6 +17,7 @@ package errors import ( "encoding/json" "fmt" + "net/http" "strings" ) @@ -32,12 +33,12 @@ const ( patternFail = "%s in %s should match '%s'" enumFail = "%s in %s should be one of %v" multipleOfFail = "%s in %s should be a multiple of %v" - maxIncFail = "%s in %s should be less than or equal to %v" - maxExcFail = "%s in %s should be less than %v" + maximumIncFail = "%s in %s should be less than or equal to %v" + maximumExcFail = "%s in %s should be less than %v" minIncFail = "%s in %s should be greater than or equal to %v" minExcFail = "%s in %s should be greater than %v" uniqueFail = "%s in %s shouldn't contain duplicates" - maxItemsFail = "%s in %s should have at most %d items" + maximumItemsFail = "%s in %s should have at most %d items" minItemsFail = "%s in %s should have at least %d items" typeFailNoIn = "%s must be of type %s" typeFailWithDataNoIn = "%s must be of type %s: %q" @@ -49,12 +50,12 @@ const ( patternFailNoIn = "%s should match '%s'" enumFailNoIn = "%s should be one of %v" multipleOfFailNoIn = "%s should be a multiple of %v" - maxIncFailNoIn = "%s should be less than or equal to %v" - maxExcFailNoIn = "%s should be less than %v" + maximumIncFailNoIn = "%s should be less than or equal to %v" + maximumExcFailNoIn = "%s should be less than %v" minIncFailNoIn = "%s should be greater than or equal to %v" minExcFailNoIn = "%s should be greater than %v" uniqueFailNoIn = "%s shouldn't contain duplicates" - maxItemsFailNoIn = "%s should have at most %d items" + maximumItemsFailNoIn = "%s should have at most %d items" minItemsFailNoIn = "%s should have at least %d items" noAdditionalItems = "%s in %s can't have additional items" noAdditionalItemsNoIn = "%s can't have additional items" @@ -69,14 +70,17 @@ const ( multipleOfMustBePositive = "factor MultipleOf declared for %s must be positive: %v" ) +const maximumValidHTTPCode = 600 + // All code responses can be used to differentiate errors for different handling // by the consuming program const ( // CompositeErrorCode remains 422 for backwards-compatibility // and to separate it from validation errors with cause - CompositeErrorCode = 422 + CompositeErrorCode = http.StatusUnprocessableEntity + // InvalidTypeCode is used for any subclass of invalid types - InvalidTypeCode = 600 + iota + InvalidTypeCode = maximumValidHTTPCode + iota RequiredFailCode TooLongFailCode TooShortFailCode @@ -298,10 +302,10 @@ func DuplicateItems(name, in string) *Validation { } // TooManyItems error for when an array contains too many items -func TooManyItems(name, in string, max int64, value interface{}) *Validation { - msg := fmt.Sprintf(maxItemsFail, name, in, max) +func TooManyItems(name, in string, maximum int64, value interface{}) *Validation { + msg := fmt.Sprintf(maximumItemsFail, name, in, maximum) if in == "" { - msg = fmt.Sprintf(maxItemsFailNoIn, name, max) + msg = fmt.Sprintf(maximumItemsFailNoIn, name, maximum) } return &Validation{ @@ -314,10 +318,10 @@ func TooManyItems(name, in string, max int64, value interface{}) *Validation { } // TooFewItems error for when an array contains too few items -func TooFewItems(name, in string, min int64, value interface{}) *Validation { - msg := fmt.Sprintf(minItemsFail, name, in, min) +func TooFewItems(name, in string, minimum int64, value interface{}) *Validation { + msg := fmt.Sprintf(minItemsFail, name, in, minimum) if in == "" { - msg = fmt.Sprintf(minItemsFailNoIn, name, min) + msg = fmt.Sprintf(minItemsFailNoIn, name, minimum) } return &Validation{ code: MinItemsFailCode, @@ -328,21 +332,21 @@ func TooFewItems(name, in string, min int64, value interface{}) *Validation { } } -// ExceedsMaximumInt error for when maximum validation fails -func ExceedsMaximumInt(name, in string, max int64, exclusive bool, value interface{}) *Validation { +// ExceedsMaximumInt error for when maximumimum validation fails +func ExceedsMaximumInt(name, in string, maximum int64, exclusive bool, value interface{}) *Validation { var message string if in == "" { - m := maxIncFailNoIn + m := maximumIncFailNoIn if exclusive { - m = maxExcFailNoIn + m = maximumExcFailNoIn } - message = fmt.Sprintf(m, name, max) + message = fmt.Sprintf(m, name, maximum) } else { - m := maxIncFail + m := maximumIncFail if exclusive { - m = maxExcFail + m = maximumExcFail } - message = fmt.Sprintf(m, name, in, max) + message = fmt.Sprintf(m, name, in, maximum) } return &Validation{ code: MaxFailCode, @@ -353,21 +357,21 @@ func ExceedsMaximumInt(name, in string, max int64, exclusive bool, value interfa } } -// ExceedsMaximumUint error for when maximum validation fails -func ExceedsMaximumUint(name, in string, max uint64, exclusive bool, value interface{}) *Validation { +// ExceedsMaximumUint error for when maximumimum validation fails +func ExceedsMaximumUint(name, in string, maximum uint64, exclusive bool, value interface{}) *Validation { var message string if in == "" { - m := maxIncFailNoIn + m := maximumIncFailNoIn if exclusive { - m = maxExcFailNoIn + m = maximumExcFailNoIn } - message = fmt.Sprintf(m, name, max) + message = fmt.Sprintf(m, name, maximum) } else { - m := maxIncFail + m := maximumIncFail if exclusive { - m = maxExcFail + m = maximumExcFail } - message = fmt.Sprintf(m, name, in, max) + message = fmt.Sprintf(m, name, in, maximum) } return &Validation{ code: MaxFailCode, @@ -378,21 +382,21 @@ func ExceedsMaximumUint(name, in string, max uint64, exclusive bool, value inter } } -// ExceedsMaximum error for when maximum validation fails -func ExceedsMaximum(name, in string, max float64, exclusive bool, value interface{}) *Validation { +// ExceedsMaximum error for when maximumimum validation fails +func ExceedsMaximum(name, in string, maximum float64, exclusive bool, value interface{}) *Validation { var message string if in == "" { - m := maxIncFailNoIn + m := maximumIncFailNoIn if exclusive { - m = maxExcFailNoIn + m = maximumExcFailNoIn } - message = fmt.Sprintf(m, name, max) + message = fmt.Sprintf(m, name, maximum) } else { - m := maxIncFail + m := maximumIncFail if exclusive { - m = maxExcFail + m = maximumExcFail } - message = fmt.Sprintf(m, name, in, max) + message = fmt.Sprintf(m, name, in, maximum) } return &Validation{ code: MaxFailCode, @@ -404,20 +408,20 @@ func ExceedsMaximum(name, in string, max float64, exclusive bool, value interfac } // ExceedsMinimumInt error for when minimum validation fails -func ExceedsMinimumInt(name, in string, min int64, exclusive bool, value interface{}) *Validation { +func ExceedsMinimumInt(name, in string, minimum int64, exclusive bool, value interface{}) *Validation { var message string if in == "" { m := minIncFailNoIn if exclusive { m = minExcFailNoIn } - message = fmt.Sprintf(m, name, min) + message = fmt.Sprintf(m, name, minimum) } else { m := minIncFail if exclusive { m = minExcFail } - message = fmt.Sprintf(m, name, in, min) + message = fmt.Sprintf(m, name, in, minimum) } return &Validation{ code: MinFailCode, @@ -429,20 +433,20 @@ func ExceedsMinimumInt(name, in string, min int64, exclusive bool, value interfa } // ExceedsMinimumUint error for when minimum validation fails -func ExceedsMinimumUint(name, in string, min uint64, exclusive bool, value interface{}) *Validation { +func ExceedsMinimumUint(name, in string, minimum uint64, exclusive bool, value interface{}) *Validation { var message string if in == "" { m := minIncFailNoIn if exclusive { m = minExcFailNoIn } - message = fmt.Sprintf(m, name, min) + message = fmt.Sprintf(m, name, minimum) } else { m := minIncFail if exclusive { m = minExcFail } - message = fmt.Sprintf(m, name, in, min) + message = fmt.Sprintf(m, name, in, minimum) } return &Validation{ code: MinFailCode, @@ -454,20 +458,20 @@ func ExceedsMinimumUint(name, in string, min uint64, exclusive bool, value inter } // ExceedsMinimum error for when minimum validation fails -func ExceedsMinimum(name, in string, min float64, exclusive bool, value interface{}) *Validation { +func ExceedsMinimum(name, in string, minimum float64, exclusive bool, value interface{}) *Validation { var message string if in == "" { m := minIncFailNoIn if exclusive { m = minExcFailNoIn } - message = fmt.Sprintf(m, name, min) + message = fmt.Sprintf(m, name, minimum) } else { m := minIncFail if exclusive { m = minExcFail } - message = fmt.Sprintf(m, name, in, min) + message = fmt.Sprintf(m, name, in, minimum) } return &Validation{ code: MinFailCode, @@ -549,12 +553,12 @@ func ReadOnly(name, in string, value interface{}) *Validation { } // TooLong error for when a string is too long -func TooLong(name, in string, max int64, value interface{}) *Validation { +func TooLong(name, in string, maximum int64, value interface{}) *Validation { var msg string if in == "" { - msg = fmt.Sprintf(tooLongMessageNoIn, name, max) + msg = fmt.Sprintf(tooLongMessageNoIn, name, maximum) } else { - msg = fmt.Sprintf(tooLongMessage, name, in, max) + msg = fmt.Sprintf(tooLongMessage, name, in, maximum) } return &Validation{ code: TooLongFailCode, @@ -566,12 +570,12 @@ func TooLong(name, in string, max int64, value interface{}) *Validation { } // TooShort error for when a string is too short -func TooShort(name, in string, min int64, value interface{}) *Validation { +func TooShort(name, in string, minimum int64, value interface{}) *Validation { var msg string if in == "" { - msg = fmt.Sprintf(tooShortMessageNoIn, name, min) + msg = fmt.Sprintf(tooShortMessageNoIn, name, minimum) } else { - msg = fmt.Sprintf(tooShortMessage, name, in, min) + msg = fmt.Sprintf(tooShortMessage, name, in, minimum) } return &Validation{ diff --git a/vendor/github.com/go-openapi/swag/.golangci.yml b/vendor/github.com/go-openapi/swag/.golangci.yml index 80e2be0042..d2fafb8a2b 100644 --- a/vendor/github.com/go-openapi/swag/.golangci.yml +++ b/vendor/github.com/go-openapi/swag/.golangci.yml @@ -1,22 +1,17 @@ linters-settings: - govet: - check-shadowing: true - golint: - min-confidence: 0 gocyclo: min-complexity: 45 - maligned: - suggest-new: true dupl: threshold: 200 goconst: - min-len: 3 + min-len: 2 min-occurrences: 3 linters: enable-all: true disable: - - maligned + - recvcheck + - unparam - lll - gochecknoinits - gochecknoglobals @@ -28,9 +23,6 @@ linters: - wrapcheck - testpackage - nlreturn - - gomnd - - exhaustivestruct - - goerr113 - errorlint - nestif - godot @@ -38,7 +30,6 @@ linters: - paralleltest - tparallel - thelper - - ifshort - exhaustruct - varnamelen - gci @@ -51,10 +42,15 @@ linters: - forcetypeassert - cyclop # deprecated linters - - deadcode - - interfacer - - scopelint - - varcheck - - structcheck - - golint - - nosnakecase + #- deadcode + #- interfacer + #- scopelint + #- varcheck + #- structcheck + #- golint + #- nosnakecase + #- maligned + #- goerr113 + #- ifshort + #- gomnd + #- exhaustivestruct diff --git a/vendor/github.com/go-openapi/swag/errors.go b/vendor/github.com/go-openapi/swag/errors.go new file mode 100644 index 0000000000..6c67fbf92e --- /dev/null +++ b/vendor/github.com/go-openapi/swag/errors.go @@ -0,0 +1,15 @@ +package swag + +type swagError string + +const ( + // ErrYAML is an error raised by YAML utilities + ErrYAML swagError = "yaml error" + + // ErrLoader is an error raised by the file loader utility + ErrLoader swagError = "loader error" +) + +func (e swagError) Error() string { + return string(e) +} diff --git a/vendor/github.com/go-openapi/swag/json.go b/vendor/github.com/go-openapi/swag/json.go index 7e9902ca31..c7caa9908f 100644 --- a/vendor/github.com/go-openapi/swag/json.go +++ b/vendor/github.com/go-openapi/swag/json.go @@ -126,7 +126,8 @@ func ConcatJSON(blobs ...[]byte) []byte { continue // don't know how to concatenate non container objects } - if len(b) < 3 { // yep empty but also the last one, so closing this thing + const minLengthIfNotEmpty = 3 + if len(b) < minLengthIfNotEmpty { // yep empty but also the last one, so closing this thing if i == last && a > 0 { if err := buf.WriteByte(closing); err != nil { log.Println(err) diff --git a/vendor/github.com/go-openapi/swag/loading.go b/vendor/github.com/go-openapi/swag/loading.go index 783442fddf..658a24b789 100644 --- a/vendor/github.com/go-openapi/swag/loading.go +++ b/vendor/github.com/go-openapi/swag/loading.go @@ -168,7 +168,7 @@ func loadHTTPBytes(timeout time.Duration) func(path string) ([]byte, error) { } if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("could not access document at %q [%s] ", path, resp.Status) + return nil, fmt.Errorf("could not access document at %q [%s]: %w", path, resp.Status, ErrLoader) } return io.ReadAll(resp.Body) diff --git a/vendor/github.com/go-openapi/swag/yaml.go b/vendor/github.com/go-openapi/swag/yaml.go index f59e025932..575346539a 100644 --- a/vendor/github.com/go-openapi/swag/yaml.go +++ b/vendor/github.com/go-openapi/swag/yaml.go @@ -16,7 +16,6 @@ package swag import ( "encoding/json" - "errors" "fmt" "path/filepath" "reflect" @@ -51,7 +50,7 @@ func BytesToYAMLDoc(data []byte) (interface{}, error) { return nil, err } if document.Kind != yaml.DocumentNode || len(document.Content) != 1 || document.Content[0].Kind != yaml.MappingNode { - return nil, errors.New("only YAML documents that are objects are supported") + return nil, fmt.Errorf("only YAML documents that are objects are supported: %w", ErrYAML) } return &document, nil } @@ -69,31 +68,32 @@ func yamlNode(root *yaml.Node) (interface{}, error) { case yaml.AliasNode: return yamlNode(root.Alias) default: - return nil, fmt.Errorf("unsupported YAML node type: %v", root.Kind) + return nil, fmt.Errorf("unsupported YAML node type: %v: %w", root.Kind, ErrYAML) } } func yamlDocument(node *yaml.Node) (interface{}, error) { if len(node.Content) != 1 { - return nil, fmt.Errorf("unexpected YAML Document node content length: %d", len(node.Content)) + return nil, fmt.Errorf("unexpected YAML Document node content length: %d: %w", len(node.Content), ErrYAML) } return yamlNode(node.Content[0]) } func yamlMapping(node *yaml.Node) (interface{}, error) { - m := make(JSONMapSlice, len(node.Content)/2) + const sensibleAllocDivider = 2 + m := make(JSONMapSlice, len(node.Content)/sensibleAllocDivider) var j int for i := 0; i < len(node.Content); i += 2 { var nmi JSONMapItem k, err := yamlStringScalarC(node.Content[i]) if err != nil { - return nil, fmt.Errorf("unable to decode YAML map key: %w", err) + return nil, fmt.Errorf("unable to decode YAML map key: %w: %w", err, ErrYAML) } nmi.Key = k v, err := yamlNode(node.Content[i+1]) if err != nil { - return nil, fmt.Errorf("unable to process YAML map value for key %q: %w", k, err) + return nil, fmt.Errorf("unable to process YAML map value for key %q: %w: %w", k, err, ErrYAML) } nmi.Value = v m[j] = nmi @@ -109,7 +109,7 @@ func yamlSequence(node *yaml.Node) (interface{}, error) { v, err := yamlNode(node.Content[i]) if err != nil { - return nil, fmt.Errorf("unable to decode YAML sequence value: %w", err) + return nil, fmt.Errorf("unable to decode YAML sequence value: %w: %w", err, ErrYAML) } s = append(s, v) } @@ -132,19 +132,19 @@ func yamlScalar(node *yaml.Node) (interface{}, error) { case yamlBoolScalar: b, err := strconv.ParseBool(node.Value) if err != nil { - return nil, fmt.Errorf("unable to process scalar node. Got %q. Expecting bool content: %w", node.Value, err) + return nil, fmt.Errorf("unable to process scalar node. Got %q. Expecting bool content: %w: %w", node.Value, err, ErrYAML) } return b, nil case yamlIntScalar: i, err := strconv.ParseInt(node.Value, 10, 64) if err != nil { - return nil, fmt.Errorf("unable to process scalar node. Got %q. Expecting integer content: %w", node.Value, err) + return nil, fmt.Errorf("unable to process scalar node. Got %q. Expecting integer content: %w: %w", node.Value, err, ErrYAML) } return i, nil case yamlFloatScalar: f, err := strconv.ParseFloat(node.Value, 64) if err != nil { - return nil, fmt.Errorf("unable to process scalar node. Got %q. Expecting float content: %w", node.Value, err) + return nil, fmt.Errorf("unable to process scalar node. Got %q. Expecting float content: %w: %w", node.Value, err, ErrYAML) } return f, nil case yamlTimestamp: @@ -152,19 +152,19 @@ func yamlScalar(node *yaml.Node) (interface{}, error) { case yamlNull: return nil, nil //nolint:nilnil default: - return nil, fmt.Errorf("YAML tag %q is not supported", node.LongTag()) + return nil, fmt.Errorf("YAML tag %q is not supported: %w", node.LongTag(), ErrYAML) } } func yamlStringScalarC(node *yaml.Node) (string, error) { if node.Kind != yaml.ScalarNode { - return "", fmt.Errorf("expecting a string scalar but got %q", node.Kind) + return "", fmt.Errorf("expecting a string scalar but got %q: %w", node.Kind, ErrYAML) } switch node.LongTag() { case yamlStringScalar, yamlIntScalar, yamlFloatScalar: return node.Value, nil default: - return "", fmt.Errorf("YAML tag %q is not supported as map key", node.LongTag()) + return "", fmt.Errorf("YAML tag %q is not supported as map key: %w", node.LongTag(), ErrYAML) } } @@ -349,7 +349,7 @@ func json2yaml(item interface{}) (*yaml.Node, error) { Value: strconv.FormatBool(val), }, nil default: - return nil, fmt.Errorf("unhandled type: %T", val) + return nil, fmt.Errorf("unhandled type: %T: %w", val, ErrYAML) } } @@ -416,7 +416,7 @@ func transformData(input interface{}) (out interface{}, err error) { case int64: return strconv.FormatInt(k, 10), nil default: - return "", fmt.Errorf("unexpected map key type, got: %T", k) + return "", fmt.Errorf("unexpected map key type, got: %T: %w", k, ErrYAML) } } diff --git a/vendor/github.com/sagikazarmark/slog-shim/.editorconfig b/vendor/github.com/go-viper/mapstructure/v2/.editorconfig similarity index 86% rename from vendor/github.com/sagikazarmark/slog-shim/.editorconfig rename to vendor/github.com/go-viper/mapstructure/v2/.editorconfig index 1fb0e1bec6..1f664d13a5 100644 --- a/vendor/github.com/sagikazarmark/slog-shim/.editorconfig +++ b/vendor/github.com/go-viper/mapstructure/v2/.editorconfig @@ -8,11 +8,11 @@ indent_style = space insert_final_newline = true trim_trailing_whitespace = true -[*.nix] -indent_size = 2 +[*.go] +indent_style = tab [{Makefile,*.mk}] indent_style = tab -[Taskfile.yaml] +[*.nix] indent_size = 2 diff --git a/vendor/github.com/go-viper/mapstructure/v2/.gitignore b/vendor/github.com/go-viper/mapstructure/v2/.gitignore new file mode 100644 index 0000000000..470e7ca2bd --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/.gitignore @@ -0,0 +1,6 @@ +/.devenv/ +/.direnv/ +/.pre-commit-config.yaml +/bin/ +/build/ +/var/ diff --git a/vendor/github.com/go-viper/mapstructure/v2/.golangci.yaml b/vendor/github.com/go-viper/mapstructure/v2/.golangci.yaml new file mode 100644 index 0000000000..763143aa77 --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/.golangci.yaml @@ -0,0 +1,23 @@ +run: + timeout: 5m + +linters-settings: + gci: + sections: + - standard + - default + - prefix(github.com/go-viper/mapstructure) + golint: + min-confidence: 0 + goimports: + local-prefixes: github.com/go-viper/maptstructure + +linters: + disable-all: true + enable: + - gci + - gofmt + - gofumpt + - goimports + - staticcheck + # - stylecheck diff --git a/vendor/github.com/go-viper/mapstructure/v2/CHANGELOG.md b/vendor/github.com/go-viper/mapstructure/v2/CHANGELOG.md new file mode 100644 index 0000000000..afd44e5f5f --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/CHANGELOG.md @@ -0,0 +1,104 @@ +> [!WARNING] +> As of v2 of this library, change log can be found in GitHub releases. + +## 1.5.1 + +* Wrap errors so they're compatible with `errors.Is` and `errors.As` [GH-282] +* Fix map of slices not decoding properly in certain cases. [GH-266] + +## 1.5.0 + +* New option `IgnoreUntaggedFields` to ignore decoding to any fields + without `mapstructure` (or the configured tag name) set [GH-277] +* New option `ErrorUnset` which makes it an error if any fields + in a target struct are not set by the decoding process. [GH-225] +* New function `OrComposeDecodeHookFunc` to help compose decode hooks. [GH-240] +* Decoding to slice from array no longer crashes [GH-265] +* Decode nested struct pointers to map [GH-271] +* Fix issue where `,squash` was ignored if `Squash` option was set. [GH-280] +* Fix issue where fields with `,omitempty` would sometimes decode + into a map with an empty string key [GH-281] + +## 1.4.3 + +* Fix cases where `json.Number` didn't decode properly [GH-261] + +## 1.4.2 + +* Custom name matchers to support any sort of casing, formatting, etc. for + field names. [GH-250] +* Fix possible panic in ComposeDecodeHookFunc [GH-251] + +## 1.4.1 + +* Fix regression where `*time.Time` value would be set to empty and not be sent + to decode hooks properly [GH-232] + +## 1.4.0 + +* A new decode hook type `DecodeHookFuncValue` has been added that has + access to the full values. [GH-183] +* Squash is now supported with embedded fields that are struct pointers [GH-205] +* Empty strings will convert to 0 for all numeric types when weakly decoding [GH-206] + +## 1.3.3 + +* Decoding maps from maps creates a settable value for decode hooks [GH-203] + +## 1.3.2 + +* Decode into interface type with a struct value is supported [GH-187] + +## 1.3.1 + +* Squash should only squash embedded structs. [GH-194] + +## 1.3.0 + +* Added `",omitempty"` support. This will ignore zero values in the source + structure when encoding. [GH-145] + +## 1.2.3 + +* Fix duplicate entries in Keys list with pointer values. [GH-185] + +## 1.2.2 + +* Do not add unsettable (unexported) values to the unused metadata key + or "remain" value. [GH-150] + +## 1.2.1 + +* Go modules checksum mismatch fix + +## 1.2.0 + +* Added support to capture unused values in a field using the `",remain"` value + in the mapstructure tag. There is an example to showcase usage. +* Added `DecoderConfig` option to always squash embedded structs +* `json.Number` can decode into `uint` types +* Empty slices are preserved and not replaced with nil slices +* Fix panic that can occur in when decoding a map into a nil slice of structs +* Improved package documentation for godoc + +## 1.1.2 + +* Fix error when decode hook decodes interface implementation into interface + type. [GH-140] + +## 1.1.1 + +* Fix panic that can happen in `decodePtr` + +## 1.1.0 + +* Added `StringToIPHookFunc` to convert `string` to `net.IP` and `net.IPNet` [GH-133] +* Support struct to struct decoding [GH-137] +* If source map value is nil, then destination map value is nil (instead of empty) +* If source slice value is nil, then destination slice value is nil (instead of empty) +* If source pointer is nil, then destination pointer is set to nil (instead of + allocated zero value of type) + +## 1.0.0 + +* Initial tagged stable release. diff --git a/vendor/github.com/go-viper/mapstructure/v2/LICENSE b/vendor/github.com/go-viper/mapstructure/v2/LICENSE new file mode 100644 index 0000000000..f9c841a51e --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013 Mitchell Hashimoto + +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/go-viper/mapstructure/v2/README.md b/vendor/github.com/go-viper/mapstructure/v2/README.md new file mode 100644 index 0000000000..dd5ec69ddf --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/README.md @@ -0,0 +1,80 @@ +# mapstructure + +[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/go-viper/mapstructure/ci.yaml?branch=main&style=flat-square)](https://github.com/go-viper/mapstructure/actions?query=workflow%3ACI) +[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/mod/github.com/go-viper/mapstructure/v2) +![Go Version](https://img.shields.io/badge/go%20version-%3E=1.18-61CFDD.svg?style=flat-square) + +mapstructure is a Go library for decoding generic map values to structures +and vice versa, while providing helpful error handling. + +This library is most useful when decoding values from some data stream (JSON, +Gob, etc.) where you don't _quite_ know the structure of the underlying data +until you read a part of it. You can therefore read a `map[string]interface{}` +and use this library to decode it into the proper underlying native Go +structure. + +## Installation + +```shell +go get github.com/go-viper/mapstructure/v2 +``` + +## Migrating from `github.com/mitchellh/mapstructure` + +[@mitchehllh](https://github.com/mitchellh) announced his intent to archive some of his unmaintained projects (see [here](https://gist.github.com/mitchellh/90029601268e59a29e64e55bab1c5bdc) and [here](https://github.com/mitchellh/mapstructure/issues/349)). This is a repository achieved the "blessed fork" status. + +You can migrate to this package by changing your import paths in your Go files to `github.com/go-viper/mapstructure/v2`. +The API is the same, so you don't need to change anything else. + +Here is a script that can help you with the migration: + +```shell +sed -i 's/github.com\/mitchellh\/mapstructure/github.com\/go-viper\/mapstructure\/v2/g' $(find . -type f -name '*.go') +``` + +If you need more time to migrate your code, that is absolutely fine. + +Some of the latest fixes are backported to the v1 release branch of this package, so you can use the Go modules `replace` feature until you are ready to migrate: + +```shell +replace github.com/mitchellh/mapstructure => github.com/go-viper/mapstructure v1.6.0 +``` + +## Usage & Example + +For usage and examples see the [documentation](https://pkg.go.dev/mod/github.com/go-viper/mapstructure/v2). + +The `Decode` function has examples associated with it there. + +## But Why?! + +Go offers fantastic standard libraries for decoding formats such as JSON. +The standard method is to have a struct pre-created, and populate that struct +from the bytes of the encoded format. This is great, but the problem is if +you have configuration or an encoding that changes slightly depending on +specific fields. For example, consider this JSON: + +```json +{ + "type": "person", + "name": "Mitchell" +} +``` + +Perhaps we can't populate a specific structure without first reading +the "type" field from the JSON. We could always do two passes over the +decoding of the JSON (reading the "type" first, and the rest later). +However, it is much simpler to just decode this into a `map[string]interface{}` +structure, read the "type" key, then use something like this library +to decode it into the proper structure. + +## Credits + +Mapstructure was originally created by [@mitchellh](https://github.com/mitchellh). +This is a maintained fork of the original library. + +Read more about the reasons for the fork [here](https://github.com/mitchellh/mapstructure/issues/349). + +## License + +The project is licensed under the [MIT License](LICENSE). diff --git a/vendor/github.com/go-viper/mapstructure/v2/decode_hooks.go b/vendor/github.com/go-viper/mapstructure/v2/decode_hooks.go new file mode 100644 index 0000000000..1f3c69d4b8 --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/decode_hooks.go @@ -0,0 +1,630 @@ +package mapstructure + +import ( + "encoding" + "errors" + "fmt" + "net" + "net/netip" + "net/url" + "reflect" + "strconv" + "strings" + "time" +) + +// typedDecodeHook takes a raw DecodeHookFunc (an interface{}) and turns +// it into the proper DecodeHookFunc type, such as DecodeHookFuncType. +func typedDecodeHook(h DecodeHookFunc) DecodeHookFunc { + // Create variables here so we can reference them with the reflect pkg + var f1 DecodeHookFuncType + var f2 DecodeHookFuncKind + var f3 DecodeHookFuncValue + + // Fill in the variables into this interface and the rest is done + // automatically using the reflect package. + potential := []interface{}{f1, f2, f3} + + v := reflect.ValueOf(h) + vt := v.Type() + for _, raw := range potential { + pt := reflect.ValueOf(raw).Type() + if vt.ConvertibleTo(pt) { + return v.Convert(pt).Interface() + } + } + + return nil +} + +// cachedDecodeHook takes a raw DecodeHookFunc (an interface{}) and turns +// it into a closure to be used directly +// if the type fails to convert we return a closure always erroring to keep the previous behaviour +func cachedDecodeHook(raw DecodeHookFunc) func(from reflect.Value, to reflect.Value) (interface{}, error) { + switch f := typedDecodeHook(raw).(type) { + case DecodeHookFuncType: + return func(from reflect.Value, to reflect.Value) (interface{}, error) { + return f(from.Type(), to.Type(), from.Interface()) + } + case DecodeHookFuncKind: + return func(from reflect.Value, to reflect.Value) (interface{}, error) { + return f(from.Kind(), to.Kind(), from.Interface()) + } + case DecodeHookFuncValue: + return func(from reflect.Value, to reflect.Value) (interface{}, error) { + return f(from, to) + } + default: + return func(from reflect.Value, to reflect.Value) (interface{}, error) { + return nil, errors.New("invalid decode hook signature") + } + } +} + +// DecodeHookExec executes the given decode hook. This should be used +// since it'll naturally degrade to the older backwards compatible DecodeHookFunc +// that took reflect.Kind instead of reflect.Type. +func DecodeHookExec( + raw DecodeHookFunc, + from reflect.Value, to reflect.Value, +) (interface{}, error) { + switch f := typedDecodeHook(raw).(type) { + case DecodeHookFuncType: + return f(from.Type(), to.Type(), from.Interface()) + case DecodeHookFuncKind: + return f(from.Kind(), to.Kind(), from.Interface()) + case DecodeHookFuncValue: + return f(from, to) + default: + return nil, errors.New("invalid decode hook signature") + } +} + +// ComposeDecodeHookFunc creates a single DecodeHookFunc that +// automatically composes multiple DecodeHookFuncs. +// +// The composed funcs are called in order, with the result of the +// previous transformation. +func ComposeDecodeHookFunc(fs ...DecodeHookFunc) DecodeHookFunc { + cached := make([]func(from reflect.Value, to reflect.Value) (interface{}, error), 0, len(fs)) + for _, f := range fs { + cached = append(cached, cachedDecodeHook(f)) + } + return func(f reflect.Value, t reflect.Value) (interface{}, error) { + var err error + data := f.Interface() + + newFrom := f + for _, c := range cached { + data, err = c(newFrom, t) + if err != nil { + return nil, err + } + newFrom = reflect.ValueOf(data) + } + + return data, nil + } +} + +// OrComposeDecodeHookFunc executes all input hook functions until one of them returns no error. In that case its value is returned. +// If all hooks return an error, OrComposeDecodeHookFunc returns an error concatenating all error messages. +func OrComposeDecodeHookFunc(ff ...DecodeHookFunc) DecodeHookFunc { + cached := make([]func(from reflect.Value, to reflect.Value) (interface{}, error), 0, len(ff)) + for _, f := range ff { + cached = append(cached, cachedDecodeHook(f)) + } + return func(a, b reflect.Value) (interface{}, error) { + var allErrs string + var out interface{} + var err error + + for _, c := range cached { + out, err = c(a, b) + if err != nil { + allErrs += err.Error() + "\n" + continue + } + + return out, nil + } + + return nil, errors.New(allErrs) + } +} + +// StringToSliceHookFunc returns a DecodeHookFunc that converts +// string to []string by splitting on the given sep. +func StringToSliceHookFunc(sep string) DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data interface{}, + ) (interface{}, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.SliceOf(f) { + return data, nil + } + + raw := data.(string) + if raw == "" { + return []string{}, nil + } + + return strings.Split(raw, sep), nil + } +} + +// StringToTimeDurationHookFunc returns a DecodeHookFunc that converts +// strings to time.Duration. +func StringToTimeDurationHookFunc() DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data interface{}, + ) (interface{}, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.TypeOf(time.Duration(5)) { + return data, nil + } + + // Convert it by parsing + return time.ParseDuration(data.(string)) + } +} + +// StringToURLHookFunc returns a DecodeHookFunc that converts +// strings to *url.URL. +func StringToURLHookFunc() DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data interface{}, + ) (interface{}, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.TypeOf(&url.URL{}) { + return data, nil + } + + // Convert it by parsing + return url.Parse(data.(string)) + } +} + +// StringToIPHookFunc returns a DecodeHookFunc that converts +// strings to net.IP +func StringToIPHookFunc() DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data interface{}, + ) (interface{}, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.TypeOf(net.IP{}) { + return data, nil + } + + // Convert it by parsing + ip := net.ParseIP(data.(string)) + if ip == nil { + return net.IP{}, fmt.Errorf("failed parsing ip %v", data) + } + + return ip, nil + } +} + +// StringToIPNetHookFunc returns a DecodeHookFunc that converts +// strings to net.IPNet +func StringToIPNetHookFunc() DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data interface{}, + ) (interface{}, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.TypeOf(net.IPNet{}) { + return data, nil + } + + // Convert it by parsing + _, net, err := net.ParseCIDR(data.(string)) + return net, err + } +} + +// StringToTimeHookFunc returns a DecodeHookFunc that converts +// strings to time.Time. +func StringToTimeHookFunc(layout string) DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data interface{}, + ) (interface{}, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.TypeOf(time.Time{}) { + return data, nil + } + + // Convert it by parsing + return time.Parse(layout, data.(string)) + } +} + +// WeaklyTypedHook is a DecodeHookFunc which adds support for weak typing to +// the decoder. +// +// Note that this is significantly different from the WeaklyTypedInput option +// of the DecoderConfig. +func WeaklyTypedHook( + f reflect.Kind, + t reflect.Kind, + data interface{}, +) (interface{}, error) { + dataVal := reflect.ValueOf(data) + switch t { + case reflect.String: + switch f { + case reflect.Bool: + if dataVal.Bool() { + return "1", nil + } + return "0", nil + case reflect.Float32: + return strconv.FormatFloat(dataVal.Float(), 'f', -1, 64), nil + case reflect.Int: + return strconv.FormatInt(dataVal.Int(), 10), nil + case reflect.Slice: + dataType := dataVal.Type() + elemKind := dataType.Elem().Kind() + if elemKind == reflect.Uint8 { + return string(dataVal.Interface().([]uint8)), nil + } + case reflect.Uint: + return strconv.FormatUint(dataVal.Uint(), 10), nil + } + } + + return data, nil +} + +func RecursiveStructToMapHookFunc() DecodeHookFunc { + return func(f reflect.Value, t reflect.Value) (interface{}, error) { + if f.Kind() != reflect.Struct { + return f.Interface(), nil + } + + var i interface{} = struct{}{} + if t.Type() != reflect.TypeOf(&i).Elem() { + return f.Interface(), nil + } + + m := make(map[string]interface{}) + t.Set(reflect.ValueOf(m)) + + return f.Interface(), nil + } +} + +// TextUnmarshallerHookFunc returns a DecodeHookFunc that applies +// strings to the UnmarshalText function, when the target type +// implements the encoding.TextUnmarshaler interface +func TextUnmarshallerHookFunc() DecodeHookFuncType { + return func( + f reflect.Type, + t reflect.Type, + data interface{}, + ) (interface{}, error) { + if f.Kind() != reflect.String { + return data, nil + } + result := reflect.New(t).Interface() + unmarshaller, ok := result.(encoding.TextUnmarshaler) + if !ok { + return data, nil + } + str, ok := data.(string) + if !ok { + str = reflect.Indirect(reflect.ValueOf(&data)).Elem().String() + } + if err := unmarshaller.UnmarshalText([]byte(str)); err != nil { + return nil, err + } + return result, nil + } +} + +// StringToNetIPAddrHookFunc returns a DecodeHookFunc that converts +// strings to netip.Addr. +func StringToNetIPAddrHookFunc() DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data interface{}, + ) (interface{}, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.TypeOf(netip.Addr{}) { + return data, nil + } + + // Convert it by parsing + return netip.ParseAddr(data.(string)) + } +} + +// StringToNetIPAddrPortHookFunc returns a DecodeHookFunc that converts +// strings to netip.AddrPort. +func StringToNetIPAddrPortHookFunc() DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data interface{}, + ) (interface{}, error) { + if f.Kind() != reflect.String { + return data, nil + } + if t != reflect.TypeOf(netip.AddrPort{}) { + return data, nil + } + + // Convert it by parsing + return netip.ParseAddrPort(data.(string)) + } +} + +// StringToBasicTypeHookFunc returns a DecodeHookFunc that converts +// strings to basic types. +// int8, uint8, int16, uint16, int32, uint32, int64, uint64, int, uint, float32, float64, bool, byte, rune, complex64, complex128 +func StringToBasicTypeHookFunc() DecodeHookFunc { + return ComposeDecodeHookFunc( + StringToInt8HookFunc(), + StringToUint8HookFunc(), + StringToInt16HookFunc(), + StringToUint16HookFunc(), + StringToInt32HookFunc(), + StringToUint32HookFunc(), + StringToInt64HookFunc(), + StringToUint64HookFunc(), + StringToIntHookFunc(), + StringToUintHookFunc(), + StringToFloat32HookFunc(), + StringToFloat64HookFunc(), + StringToBoolHookFunc(), + // byte and rune are aliases for uint8 and int32 respectively + // StringToByteHookFunc(), + // StringToRuneHookFunc(), + StringToComplex64HookFunc(), + StringToComplex128HookFunc(), + ) +} + +// StringToInt8HookFunc returns a DecodeHookFunc that converts +// strings to int8. +func StringToInt8HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Int8 { + return data, nil + } + + // Convert it by parsing + i64, err := strconv.ParseInt(data.(string), 0, 8) + return int8(i64), err + } +} + +// StringToUint8HookFunc returns a DecodeHookFunc that converts +// strings to uint8. +func StringToUint8HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Uint8 { + return data, nil + } + + // Convert it by parsing + u64, err := strconv.ParseUint(data.(string), 0, 8) + return uint8(u64), err + } +} + +// StringToInt16HookFunc returns a DecodeHookFunc that converts +// strings to int16. +func StringToInt16HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Int16 { + return data, nil + } + + // Convert it by parsing + i64, err := strconv.ParseInt(data.(string), 0, 16) + return int16(i64), err + } +} + +// StringToUint16HookFunc returns a DecodeHookFunc that converts +// strings to uint16. +func StringToUint16HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Uint16 { + return data, nil + } + + // Convert it by parsing + u64, err := strconv.ParseUint(data.(string), 0, 16) + return uint16(u64), err + } +} + +// StringToInt32HookFunc returns a DecodeHookFunc that converts +// strings to int32. +func StringToInt32HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Int32 { + return data, nil + } + + // Convert it by parsing + i64, err := strconv.ParseInt(data.(string), 0, 32) + return int32(i64), err + } +} + +// StringToUint32HookFunc returns a DecodeHookFunc that converts +// strings to uint32. +func StringToUint32HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Uint32 { + return data, nil + } + + // Convert it by parsing + u64, err := strconv.ParseUint(data.(string), 0, 32) + return uint32(u64), err + } +} + +// StringToInt64HookFunc returns a DecodeHookFunc that converts +// strings to int64. +func StringToInt64HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Int64 { + return data, nil + } + + // Convert it by parsing + return strconv.ParseInt(data.(string), 0, 64) + } +} + +// StringToUint64HookFunc returns a DecodeHookFunc that converts +// strings to uint64. +func StringToUint64HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Uint64 { + return data, nil + } + + // Convert it by parsing + return strconv.ParseUint(data.(string), 0, 64) + } +} + +// StringToIntHookFunc returns a DecodeHookFunc that converts +// strings to int. +func StringToIntHookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Int { + return data, nil + } + + // Convert it by parsing + i64, err := strconv.ParseInt(data.(string), 0, 0) + return int(i64), err + } +} + +// StringToUintHookFunc returns a DecodeHookFunc that converts +// strings to uint. +func StringToUintHookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Uint { + return data, nil + } + + // Convert it by parsing + u64, err := strconv.ParseUint(data.(string), 0, 0) + return uint(u64), err + } +} + +// StringToFloat32HookFunc returns a DecodeHookFunc that converts +// strings to float32. +func StringToFloat32HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Float32 { + return data, nil + } + + // Convert it by parsing + f64, err := strconv.ParseFloat(data.(string), 32) + return float32(f64), err + } +} + +// StringToFloat64HookFunc returns a DecodeHookFunc that converts +// strings to float64. +func StringToFloat64HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Float64 { + return data, nil + } + + // Convert it by parsing + return strconv.ParseFloat(data.(string), 64) + } +} + +// StringToBoolHookFunc returns a DecodeHookFunc that converts +// strings to bool. +func StringToBoolHookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Bool { + return data, nil + } + + // Convert it by parsing + return strconv.ParseBool(data.(string)) + } +} + +// StringToByteHookFunc returns a DecodeHookFunc that converts +// strings to byte. +func StringToByteHookFunc() DecodeHookFunc { + return StringToUint8HookFunc() +} + +// StringToRuneHookFunc returns a DecodeHookFunc that converts +// strings to rune. +func StringToRuneHookFunc() DecodeHookFunc { + return StringToInt32HookFunc() +} + +// StringToComplex64HookFunc returns a DecodeHookFunc that converts +// strings to complex64. +func StringToComplex64HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Complex64 { + return data, nil + } + + // Convert it by parsing + c128, err := strconv.ParseComplex(data.(string), 64) + return complex64(c128), err + } +} + +// StringToComplex128HookFunc returns a DecodeHookFunc that converts +// strings to complex128. +func StringToComplex128HookFunc() DecodeHookFunc { + return func(f reflect.Type, t reflect.Type, data interface{}) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Complex128 { + return data, nil + } + + // Convert it by parsing + return strconv.ParseComplex(data.(string), 128) + } +} diff --git a/vendor/github.com/go-viper/mapstructure/v2/flake.lock b/vendor/github.com/go-viper/mapstructure/v2/flake.lock new file mode 100644 index 0000000000..4bea8154e0 --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/flake.lock @@ -0,0 +1,472 @@ +{ + "nodes": { + "cachix": { + "inputs": { + "devenv": "devenv_2", + "flake-compat": [ + "devenv", + "flake-compat" + ], + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "pre-commit-hooks": [ + "devenv", + "pre-commit-hooks" + ] + }, + "locked": { + "lastModified": 1712055811, + "narHash": "sha256-7FcfMm5A/f02yyzuavJe06zLa9hcMHsagE28ADcmQvk=", + "owner": "cachix", + "repo": "cachix", + "rev": "02e38da89851ec7fec3356a5c04bc8349cae0e30", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "cachix", + "type": "github" + } + }, + "devenv": { + "inputs": { + "cachix": "cachix", + "flake-compat": "flake-compat_2", + "nix": "nix_2", + "nixpkgs": "nixpkgs_2", + "pre-commit-hooks": "pre-commit-hooks" + }, + "locked": { + "lastModified": 1717245169, + "narHash": "sha256-+mW3rTBjGU8p1THJN0lX/Dd/8FbnF+3dB+mJuSaxewE=", + "owner": "cachix", + "repo": "devenv", + "rev": "c3f9f053c077c6f88a3de5276d9178c62baa3fc3", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "devenv_2": { + "inputs": { + "flake-compat": [ + "devenv", + "cachix", + "flake-compat" + ], + "nix": "nix", + "nixpkgs": "nixpkgs", + "poetry2nix": "poetry2nix", + "pre-commit-hooks": [ + "devenv", + "cachix", + "pre-commit-hooks" + ] + }, + "locked": { + "lastModified": 1708704632, + "narHash": "sha256-w+dOIW60FKMaHI1q5714CSibk99JfYxm0CzTinYWr+Q=", + "owner": "cachix", + "repo": "devenv", + "rev": "2ee4450b0f4b95a1b90f2eb5ffea98b90e48c196", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "python-rewrite", + "repo": "devenv", + "type": "github" + } + }, + "flake-compat": { + "flake": false, + "locked": { + "lastModified": 1673956053, + "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-compat_2": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, + "flake-parts": { + "inputs": { + "nixpkgs-lib": "nixpkgs-lib" + }, + "locked": { + "lastModified": 1717285511, + "narHash": "sha256-iKzJcpdXih14qYVcZ9QC9XuZYnPc6T8YImb6dX166kw=", + "owner": "hercules-ci", + "repo": "flake-parts", + "rev": "2a55567fcf15b1b1c7ed712a2c6fadaec7412ea8", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "flake-parts", + "type": "github" + } + }, + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1689068808, + "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "gitignore": { + "inputs": { + "nixpkgs": [ + "devenv", + "pre-commit-hooks", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", + "owner": "hercules-ci", + "repo": "gitignore.nix", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", + "type": "github" + }, + "original": { + "owner": "hercules-ci", + "repo": "gitignore.nix", + "type": "github" + } + }, + "nix": { + "inputs": { + "flake-compat": "flake-compat", + "nixpkgs": [ + "devenv", + "cachix", + "devenv", + "nixpkgs" + ], + "nixpkgs-regression": "nixpkgs-regression" + }, + "locked": { + "lastModified": 1712911606, + "narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=", + "owner": "domenkozar", + "repo": "nix", + "rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "devenv-2.21", + "repo": "nix", + "type": "github" + } + }, + "nix-github-actions": { + "inputs": { + "nixpkgs": [ + "devenv", + "cachix", + "devenv", + "poetry2nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1688870561, + "narHash": "sha256-4UYkifnPEw1nAzqqPOTL2MvWtm3sNGw1UTYTalkTcGY=", + "owner": "nix-community", + "repo": "nix-github-actions", + "rev": "165b1650b753316aa7f1787f3005a8d2da0f5301", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nix-github-actions", + "type": "github" + } + }, + "nix_2": { + "inputs": { + "flake-compat": [ + "devenv", + "flake-compat" + ], + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "nixpkgs-regression": "nixpkgs-regression_2" + }, + "locked": { + "lastModified": 1712911606, + "narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=", + "owner": "domenkozar", + "repo": "nix", + "rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12", + "type": "github" + }, + "original": { + "owner": "domenkozar", + "ref": "devenv-2.21", + "repo": "nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1692808169, + "narHash": "sha256-x9Opq06rIiwdwGeK2Ykj69dNc2IvUH1fY55Wm7atwrE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "9201b5ff357e781bf014d0330d18555695df7ba8", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs-lib": { + "locked": { + "lastModified": 1717284937, + "narHash": "sha256-lIbdfCsf8LMFloheeE6N31+BMIeixqyQWbSr2vk79EQ=", + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/eb9ceca17df2ea50a250b6b27f7bf6ab0186f198.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/eb9ceca17df2ea50a250b6b27f7bf6ab0186f198.tar.gz" + } + }, + "nixpkgs-regression": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + } + }, + "nixpkgs-regression_2": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + }, + "original": { + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", + "type": "github" + } + }, + "nixpkgs-stable": { + "locked": { + "lastModified": 1710695816, + "narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "614b4613980a522ba49f0d194531beddbb7220d3", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-23.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1713361204, + "narHash": "sha256-TA6EDunWTkc5FvDCqU3W2T3SFn0gRZqh6D/hJnM02MM=", + "owner": "cachix", + "repo": "devenv-nixpkgs", + "rev": "285676e87ad9f0ca23d8714a6ab61e7e027020c6", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "rolling", + "repo": "devenv-nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1717112898, + "narHash": "sha256-7R2ZvOnvd9h8fDd65p0JnB7wXfUvreox3xFdYWd1BnY=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "6132b0f6e344ce2fe34fc051b72fb46e34f668e0", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixpkgs-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "poetry2nix": { + "inputs": { + "flake-utils": "flake-utils", + "nix-github-actions": "nix-github-actions", + "nixpkgs": [ + "devenv", + "cachix", + "devenv", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1692876271, + "narHash": "sha256-IXfZEkI0Mal5y1jr6IRWMqK8GW2/f28xJenZIPQqkY0=", + "owner": "nix-community", + "repo": "poetry2nix", + "rev": "d5006be9c2c2417dafb2e2e5034d83fabd207ee3", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "poetry2nix", + "type": "github" + } + }, + "pre-commit-hooks": { + "inputs": { + "flake-compat": [ + "devenv", + "flake-compat" + ], + "flake-utils": "flake-utils_2", + "gitignore": "gitignore", + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "nixpkgs-stable": "nixpkgs-stable" + }, + "locked": { + "lastModified": 1713775815, + "narHash": "sha256-Wu9cdYTnGQQwtT20QQMg7jzkANKQjwBD9iccfGKkfls=", + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "rev": "2ac4dcbf55ed43f3be0bae15e181f08a57af24a4", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "pre-commit-hooks.nix", + "type": "github" + } + }, + "root": { + "inputs": { + "devenv": "devenv", + "flake-parts": "flake-parts", + "nixpkgs": "nixpkgs_3" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/vendor/github.com/sagikazarmark/slog-shim/flake.nix b/vendor/github.com/go-viper/mapstructure/v2/flake.nix similarity index 54% rename from vendor/github.com/sagikazarmark/slog-shim/flake.nix rename to vendor/github.com/go-viper/mapstructure/v2/flake.nix index 7239bbc2ec..4ed0f53311 100644 --- a/vendor/github.com/sagikazarmark/slog-shim/flake.nix +++ b/vendor/github.com/go-viper/mapstructure/v2/flake.nix @@ -1,7 +1,6 @@ { inputs = { - # nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; - nixpkgs.url = "github:NixOS/nixpkgs/master"; + nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable"; flake-parts.url = "github:hercules-ci/flake-parts"; devenv.url = "github:cachix/devenv"; }; @@ -19,38 +18,21 @@ default = { languages = { go.enable = true; - go.package = pkgs.lib.mkDefault pkgs.go_1_21; }; - # https://github.com/cachix/devenv/issues/528#issuecomment-1556108767 - containers = pkgs.lib.mkForce { }; - }; - - ci = devenv.shells.default; - - ci_1_19 = { - imports = [ devenv.shells.ci ]; - - languages = { - go.package = pkgs.go_1_19; + pre-commit.hooks = { + nixpkgs-fmt.enable = true; }; - }; - ci_1_20 = { - imports = [ devenv.shells.ci ]; + packages = with pkgs; [ + golangci-lint + ]; - languages = { - go.package = pkgs.go_1_20; - }; + # https://github.com/cachix/devenv/issues/528#issuecomment-1556108767 + containers = pkgs.lib.mkForce { }; }; - ci_1_21 = { - imports = [ devenv.shells.ci ]; - - languages = { - go.package = pkgs.go_1_21; - }; - }; + ci = devenv.shells.default; }; }; }; diff --git a/vendor/github.com/go-viper/mapstructure/v2/internal/errors/errors.go b/vendor/github.com/go-viper/mapstructure/v2/internal/errors/errors.go new file mode 100644 index 0000000000..d1c15e474f --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/internal/errors/errors.go @@ -0,0 +1,11 @@ +package errors + +import "errors" + +func New(text string) error { + return errors.New(text) +} + +func As(err error, target interface{}) bool { + return errors.As(err, target) +} diff --git a/vendor/github.com/go-viper/mapstructure/v2/internal/errors/join.go b/vendor/github.com/go-viper/mapstructure/v2/internal/errors/join.go new file mode 100644 index 0000000000..d74e3a0b5a --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/internal/errors/join.go @@ -0,0 +1,9 @@ +//go:build go1.20 + +package errors + +import "errors" + +func Join(errs ...error) error { + return errors.Join(errs...) +} diff --git a/vendor/github.com/go-viper/mapstructure/v2/internal/errors/join_go1_19.go b/vendor/github.com/go-viper/mapstructure/v2/internal/errors/join_go1_19.go new file mode 100644 index 0000000000..700b40229c --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/internal/errors/join_go1_19.go @@ -0,0 +1,61 @@ +//go:build !go1.20 + +// Copyright 2022 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package errors + +// Join returns an error that wraps the given errors. +// Any nil error values are discarded. +// Join returns nil if every value in errs is nil. +// The error formats as the concatenation of the strings obtained +// by calling the Error method of each element of errs, with a newline +// between each string. +// +// A non-nil error returned by Join implements the Unwrap() []error method. +func Join(errs ...error) error { + n := 0 + for _, err := range errs { + if err != nil { + n++ + } + } + if n == 0 { + return nil + } + e := &joinError{ + errs: make([]error, 0, n), + } + for _, err := range errs { + if err != nil { + e.errs = append(e.errs, err) + } + } + return e +} + +type joinError struct { + errs []error +} + +func (e *joinError) Error() string { + // Since Join returns nil if every value in errs is nil, + // e.errs cannot be empty. + if len(e.errs) == 1 { + return e.errs[0].Error() + } + + b := []byte(e.errs[0].Error()) + for _, err := range e.errs[1:] { + b = append(b, '\n') + b = append(b, err.Error()...) + } + // At this point, b has at least one byte '\n'. + // return unsafe.String(&b[0], len(b)) + return string(b) +} + +func (e *joinError) Unwrap() []error { + return e.errs +} diff --git a/vendor/github.com/go-viper/mapstructure/v2/mapstructure.go b/vendor/github.com/go-viper/mapstructure/v2/mapstructure.go new file mode 100644 index 0000000000..e77e63ba38 --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/mapstructure.go @@ -0,0 +1,1620 @@ +// Package mapstructure exposes functionality to convert one arbitrary +// Go type into another, typically to convert a map[string]interface{} +// into a native Go structure. +// +// The Go structure can be arbitrarily complex, containing slices, +// other structs, etc. and the decoder will properly decode nested +// maps and so on into the proper structures in the native Go struct. +// See the examples to see what the decoder is capable of. +// +// The simplest function to start with is Decode. +// +// # Field Tags +// +// When decoding to a struct, mapstructure will use the field name by +// default to perform the mapping. For example, if a struct has a field +// "Username" then mapstructure will look for a key in the source value +// of "username" (case insensitive). +// +// type User struct { +// Username string +// } +// +// You can change the behavior of mapstructure by using struct tags. +// The default struct tag that mapstructure looks for is "mapstructure" +// but you can customize it using DecoderConfig. +// +// # Renaming Fields +// +// To rename the key that mapstructure looks for, use the "mapstructure" +// tag and set a value directly. For example, to change the "username" example +// above to "user": +// +// type User struct { +// Username string `mapstructure:"user"` +// } +// +// # Embedded Structs and Squashing +// +// Embedded structs are treated as if they're another field with that name. +// By default, the two structs below are equivalent when decoding with +// mapstructure: +// +// type Person struct { +// Name string +// } +// +// type Friend struct { +// Person +// } +// +// type Friend struct { +// Person Person +// } +// +// This would require an input that looks like below: +// +// map[string]interface{}{ +// "person": map[string]interface{}{"name": "alice"}, +// } +// +// If your "person" value is NOT nested, then you can append ",squash" to +// your tag value and mapstructure will treat it as if the embedded struct +// were part of the struct directly. Example: +// +// type Friend struct { +// Person `mapstructure:",squash"` +// } +// +// Now the following input would be accepted: +// +// map[string]interface{}{ +// "name": "alice", +// } +// +// When decoding from a struct to a map, the squash tag squashes the struct +// fields into a single map. Using the example structs from above: +// +// Friend{Person: Person{Name: "alice"}} +// +// Will be decoded into a map: +// +// map[string]interface{}{ +// "name": "alice", +// } +// +// DecoderConfig has a field that changes the behavior of mapstructure +// to always squash embedded structs. +// +// # Remainder Values +// +// If there are any unmapped keys in the source value, mapstructure by +// default will silently ignore them. You can error by setting ErrorUnused +// in DecoderConfig. If you're using Metadata you can also maintain a slice +// of the unused keys. +// +// You can also use the ",remain" suffix on your tag to collect all unused +// values in a map. The field with this tag MUST be a map type and should +// probably be a "map[string]interface{}" or "map[interface{}]interface{}". +// See example below: +// +// type Friend struct { +// Name string +// Other map[string]interface{} `mapstructure:",remain"` +// } +// +// Given the input below, Other would be populated with the other +// values that weren't used (everything but "name"): +// +// map[string]interface{}{ +// "name": "bob", +// "address": "123 Maple St.", +// } +// +// # Omit Empty Values +// +// When decoding from a struct to any other value, you may use the +// ",omitempty" suffix on your tag to omit that value if it equates to +// the zero value. The zero value of all types is specified in the Go +// specification. +// +// For example, the zero type of a numeric type is zero ("0"). If the struct +// field value is zero and a numeric type, the field is empty, and it won't +// be encoded into the destination type. +// +// type Source struct { +// Age int `mapstructure:",omitempty"` +// } +// +// # Unexported fields +// +// Since unexported (private) struct fields cannot be set outside the package +// where they are defined, the decoder will simply skip them. +// +// For this output type definition: +// +// type Exported struct { +// private string // this unexported field will be skipped +// Public string +// } +// +// Using this map as input: +// +// map[string]interface{}{ +// "private": "I will be ignored", +// "Public": "I made it through!", +// } +// +// The following struct will be decoded: +// +// type Exported struct { +// private: "" // field is left with an empty string (zero value) +// Public: "I made it through!" +// } +// +// # Other Configuration +// +// mapstructure is highly configurable. See the DecoderConfig struct +// for other features and options that are supported. +package mapstructure + +import ( + "encoding/json" + "fmt" + "reflect" + "sort" + "strconv" + "strings" + + "github.com/go-viper/mapstructure/v2/internal/errors" +) + +// DecodeHookFunc is the callback function that can be used for +// data transformations. See "DecodeHook" in the DecoderConfig +// struct. +// +// The type must be one of DecodeHookFuncType, DecodeHookFuncKind, or +// DecodeHookFuncValue. +// Values are a superset of Types (Values can return types), and Types are a +// superset of Kinds (Types can return Kinds) and are generally a richer thing +// to use, but Kinds are simpler if you only need those. +// +// The reason DecodeHookFunc is multi-typed is for backwards compatibility: +// we started with Kinds and then realized Types were the better solution, +// but have a promise to not break backwards compat so we now support +// both. +type DecodeHookFunc interface{} + +// DecodeHookFuncType is a DecodeHookFunc which has complete information about +// the source and target types. +type DecodeHookFuncType func(reflect.Type, reflect.Type, interface{}) (interface{}, error) + +// DecodeHookFuncKind is a DecodeHookFunc which knows only the Kinds of the +// source and target types. +type DecodeHookFuncKind func(reflect.Kind, reflect.Kind, interface{}) (interface{}, error) + +// DecodeHookFuncValue is a DecodeHookFunc which has complete access to both the source and target +// values. +type DecodeHookFuncValue func(from reflect.Value, to reflect.Value) (interface{}, error) + +// DecoderConfig is the configuration that is used to create a new decoder +// and allows customization of various aspects of decoding. +type DecoderConfig struct { + // DecodeHook, if set, will be called before any decoding and any + // type conversion (if WeaklyTypedInput is on). This lets you modify + // the values before they're set down onto the resulting struct. The + // DecodeHook is called for every map and value in the input. This means + // that if a struct has embedded fields with squash tags the decode hook + // is called only once with all of the input data, not once for each + // embedded struct. + // + // If an error is returned, the entire decode will fail with that error. + DecodeHook DecodeHookFunc + + // If ErrorUnused is true, then it is an error for there to exist + // keys in the original map that were unused in the decoding process + // (extra keys). + ErrorUnused bool + + // If ErrorUnset is true, then it is an error for there to exist + // fields in the result that were not set in the decoding process + // (extra fields). This only applies to decoding to a struct. This + // will affect all nested structs as well. + ErrorUnset bool + + // ZeroFields, if set to true, will zero fields before writing them. + // For example, a map will be emptied before decoded values are put in + // it. If this is false, a map will be merged. + ZeroFields bool + + // If WeaklyTypedInput is true, the decoder will make the following + // "weak" conversions: + // + // - bools to string (true = "1", false = "0") + // - numbers to string (base 10) + // - bools to int/uint (true = 1, false = 0) + // - strings to int/uint (base implied by prefix) + // - int to bool (true if value != 0) + // - string to bool (accepts: 1, t, T, TRUE, true, True, 0, f, F, + // FALSE, false, False. Anything else is an error) + // - empty array = empty map and vice versa + // - negative numbers to overflowed uint values (base 10) + // - slice of maps to a merged map + // - single values are converted to slices if required. Each + // element is weakly decoded. For example: "4" can become []int{4} + // if the target type is an int slice. + // + WeaklyTypedInput bool + + // Squash will squash embedded structs. A squash tag may also be + // added to an individual struct field using a tag. For example: + // + // type Parent struct { + // Child `mapstructure:",squash"` + // } + Squash bool + + // Metadata is the struct that will contain extra metadata about + // the decoding. If this is nil, then no metadata will be tracked. + Metadata *Metadata + + // Result is a pointer to the struct that will contain the decoded + // value. + Result interface{} + + // The tag name that mapstructure reads for field names. This + // defaults to "mapstructure" + TagName string + + // The option of the value in the tag that indicates a field should + // be squashed. This defaults to "squash". + SquashTagOption string + + // IgnoreUntaggedFields ignores all struct fields without explicit + // TagName, comparable to `mapstructure:"-"` as default behaviour. + IgnoreUntaggedFields bool + + // MatchName is the function used to match the map key to the struct + // field name or tag. Defaults to `strings.EqualFold`. This can be used + // to implement case-sensitive tag values, support snake casing, etc. + MatchName func(mapKey, fieldName string) bool + + // DecodeNil, if set to true, will cause the DecodeHook (if present) to run + // even if the input is nil. This can be used to provide default values. + DecodeNil bool +} + +// A Decoder takes a raw interface value and turns it into structured +// data, keeping track of rich error information along the way in case +// anything goes wrong. Unlike the basic top-level Decode method, you can +// more finely control how the Decoder behaves using the DecoderConfig +// structure. The top-level Decode method is just a convenience that sets +// up the most basic Decoder. +type Decoder struct { + config *DecoderConfig + cachedDecodeHook func(from reflect.Value, to reflect.Value) (interface{}, error) +} + +// Metadata contains information about decoding a structure that +// is tedious or difficult to get otherwise. +type Metadata struct { + // Keys are the keys of the structure which were successfully decoded + Keys []string + + // Unused is a slice of keys that were found in the raw value but + // weren't decoded since there was no matching field in the result interface + Unused []string + + // Unset is a slice of field names that were found in the result interface + // but weren't set in the decoding process since there was no matching value + // in the input + Unset []string +} + +// Decode takes an input structure and uses reflection to translate it to +// the output structure. output must be a pointer to a map or struct. +func Decode(input interface{}, output interface{}) error { + config := &DecoderConfig{ + Metadata: nil, + Result: output, + } + + decoder, err := NewDecoder(config) + if err != nil { + return err + } + + return decoder.Decode(input) +} + +// WeakDecode is the same as Decode but is shorthand to enable +// WeaklyTypedInput. See DecoderConfig for more info. +func WeakDecode(input, output interface{}) error { + config := &DecoderConfig{ + Metadata: nil, + Result: output, + WeaklyTypedInput: true, + } + + decoder, err := NewDecoder(config) + if err != nil { + return err + } + + return decoder.Decode(input) +} + +// DecodeMetadata is the same as Decode, but is shorthand to +// enable metadata collection. See DecoderConfig for more info. +func DecodeMetadata(input interface{}, output interface{}, metadata *Metadata) error { + config := &DecoderConfig{ + Metadata: metadata, + Result: output, + } + + decoder, err := NewDecoder(config) + if err != nil { + return err + } + + return decoder.Decode(input) +} + +// WeakDecodeMetadata is the same as Decode, but is shorthand to +// enable both WeaklyTypedInput and metadata collection. See +// DecoderConfig for more info. +func WeakDecodeMetadata(input interface{}, output interface{}, metadata *Metadata) error { + config := &DecoderConfig{ + Metadata: metadata, + Result: output, + WeaklyTypedInput: true, + } + + decoder, err := NewDecoder(config) + if err != nil { + return err + } + + return decoder.Decode(input) +} + +// NewDecoder returns a new decoder for the given configuration. Once +// a decoder has been returned, the same configuration must not be used +// again. +func NewDecoder(config *DecoderConfig) (*Decoder, error) { + val := reflect.ValueOf(config.Result) + if val.Kind() != reflect.Ptr { + return nil, errors.New("result must be a pointer") + } + + val = val.Elem() + if !val.CanAddr() { + return nil, errors.New("result must be addressable (a pointer)") + } + + if config.Metadata != nil { + if config.Metadata.Keys == nil { + config.Metadata.Keys = make([]string, 0) + } + + if config.Metadata.Unused == nil { + config.Metadata.Unused = make([]string, 0) + } + + if config.Metadata.Unset == nil { + config.Metadata.Unset = make([]string, 0) + } + } + + if config.TagName == "" { + config.TagName = "mapstructure" + } + + if config.SquashTagOption == "" { + config.SquashTagOption = "squash" + } + + if config.MatchName == nil { + config.MatchName = strings.EqualFold + } + + result := &Decoder{ + config: config, + } + if config.DecodeHook != nil { + result.cachedDecodeHook = cachedDecodeHook(config.DecodeHook) + } + + return result, nil +} + +// Decode decodes the given raw interface to the target pointer specified +// by the configuration. +func (d *Decoder) Decode(input interface{}) error { + err := d.decode("", input, reflect.ValueOf(d.config.Result).Elem()) + + // Retain some of the original behavior when multiple errors ocurr + var joinedErr interface{ Unwrap() []error } + if errors.As(err, &joinedErr) { + return fmt.Errorf("decoding failed due to the following error(s):\n\n%w", err) + } + + return err +} + +// isNil returns true if the input is nil or a typed nil pointer. +func isNil(input interface{}) bool { + if input == nil { + return true + } + val := reflect.ValueOf(input) + return val.Kind() == reflect.Ptr && val.IsNil() +} + +// Decodes an unknown data type into a specific reflection value. +func (d *Decoder) decode(name string, input interface{}, outVal reflect.Value) error { + var ( + inputVal = reflect.ValueOf(input) + outputKind = getKind(outVal) + decodeNil = d.config.DecodeNil && d.cachedDecodeHook != nil + ) + if isNil(input) { + // Typed nils won't match the "input == nil" below, so reset input. + input = nil + } + if input == nil { + // If the data is nil, then we don't set anything, unless ZeroFields is set + // to true. + if d.config.ZeroFields { + outVal.Set(reflect.Zero(outVal.Type())) + + if d.config.Metadata != nil && name != "" { + d.config.Metadata.Keys = append(d.config.Metadata.Keys, name) + } + } + if !decodeNil { + return nil + } + } + if !inputVal.IsValid() { + if !decodeNil { + // If the input value is invalid, then we just set the value + // to be the zero value. + outVal.Set(reflect.Zero(outVal.Type())) + if d.config.Metadata != nil && name != "" { + d.config.Metadata.Keys = append(d.config.Metadata.Keys, name) + } + return nil + } + // Hooks need a valid inputVal, so reset it to zero value of outVal type. + switch outputKind { + case reflect.Struct, reflect.Map: + var mapVal map[string]interface{} + inputVal = reflect.ValueOf(mapVal) // create nil map pointer + case reflect.Slice, reflect.Array: + var sliceVal []interface{} + inputVal = reflect.ValueOf(sliceVal) // create nil slice pointer + default: + inputVal = reflect.Zero(outVal.Type()) + } + } + + if d.cachedDecodeHook != nil { + // We have a DecodeHook, so let's pre-process the input. + var err error + input, err = d.cachedDecodeHook(inputVal, outVal) + if err != nil { + return fmt.Errorf("error decoding '%s': %w", name, err) + } + } + if isNil(input) { + return nil + } + + var err error + addMetaKey := true + switch outputKind { + case reflect.Bool: + err = d.decodeBool(name, input, outVal) + case reflect.Interface: + err = d.decodeBasic(name, input, outVal) + case reflect.String: + err = d.decodeString(name, input, outVal) + case reflect.Int: + err = d.decodeInt(name, input, outVal) + case reflect.Uint: + err = d.decodeUint(name, input, outVal) + case reflect.Float32: + err = d.decodeFloat(name, input, outVal) + case reflect.Complex64: + err = d.decodeComplex(name, input, outVal) + case reflect.Struct: + err = d.decodeStruct(name, input, outVal) + case reflect.Map: + err = d.decodeMap(name, input, outVal) + case reflect.Ptr: + addMetaKey, err = d.decodePtr(name, input, outVal) + case reflect.Slice: + err = d.decodeSlice(name, input, outVal) + case reflect.Array: + err = d.decodeArray(name, input, outVal) + case reflect.Func: + err = d.decodeFunc(name, input, outVal) + default: + // If we reached this point then we weren't able to decode it + return fmt.Errorf("%s: unsupported type: %s", name, outputKind) + } + + // If we reached here, then we successfully decoded SOMETHING, so + // mark the key as used if we're tracking metainput. + if addMetaKey && d.config.Metadata != nil && name != "" { + d.config.Metadata.Keys = append(d.config.Metadata.Keys, name) + } + + return err +} + +// This decodes a basic type (bool, int, string, etc.) and sets the +// value to "data" of that type. +func (d *Decoder) decodeBasic(name string, data interface{}, val reflect.Value) error { + if val.IsValid() && val.Elem().IsValid() { + elem := val.Elem() + + // If we can't address this element, then its not writable. Instead, + // we make a copy of the value (which is a pointer and therefore + // writable), decode into that, and replace the whole value. + copied := false + if !elem.CanAddr() { + copied = true + + // Make *T + copy := reflect.New(elem.Type()) + + // *T = elem + copy.Elem().Set(elem) + + // Set elem so we decode into it + elem = copy + } + + // Decode. If we have an error then return. We also return right + // away if we're not a copy because that means we decoded directly. + if err := d.decode(name, data, elem); err != nil || !copied { + return err + } + + // If we're a copy, we need to set te final result + val.Set(elem.Elem()) + return nil + } + + dataVal := reflect.ValueOf(data) + + // If the input data is a pointer, and the assigned type is the dereference + // of that exact pointer, then indirect it so that we can assign it. + // Example: *string to string + if dataVal.Kind() == reflect.Ptr && dataVal.Type().Elem() == val.Type() { + dataVal = reflect.Indirect(dataVal) + } + + if !dataVal.IsValid() { + dataVal = reflect.Zero(val.Type()) + } + + dataValType := dataVal.Type() + if !dataValType.AssignableTo(val.Type()) { + return fmt.Errorf( + "'%s' expected type '%s', got '%s'", + name, val.Type(), dataValType) + } + + val.Set(dataVal) + return nil +} + +func (d *Decoder) decodeString(name string, data interface{}, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataKind := getKind(dataVal) + + converted := true + switch { + case dataKind == reflect.String: + val.SetString(dataVal.String()) + case dataKind == reflect.Bool && d.config.WeaklyTypedInput: + if dataVal.Bool() { + val.SetString("1") + } else { + val.SetString("0") + } + case dataKind == reflect.Int && d.config.WeaklyTypedInput: + val.SetString(strconv.FormatInt(dataVal.Int(), 10)) + case dataKind == reflect.Uint && d.config.WeaklyTypedInput: + val.SetString(strconv.FormatUint(dataVal.Uint(), 10)) + case dataKind == reflect.Float32 && d.config.WeaklyTypedInput: + val.SetString(strconv.FormatFloat(dataVal.Float(), 'f', -1, 64)) + case dataKind == reflect.Slice && d.config.WeaklyTypedInput, + dataKind == reflect.Array && d.config.WeaklyTypedInput: + dataType := dataVal.Type() + elemKind := dataType.Elem().Kind() + switch elemKind { + case reflect.Uint8: + var uints []uint8 + if dataKind == reflect.Array { + uints = make([]uint8, dataVal.Len(), dataVal.Len()) + for i := range uints { + uints[i] = dataVal.Index(i).Interface().(uint8) + } + } else { + uints = dataVal.Interface().([]uint8) + } + val.SetString(string(uints)) + default: + converted = false + } + default: + converted = false + } + + if !converted { + return fmt.Errorf( + "'%s' expected type '%s', got unconvertible type '%s', value: '%v'", + name, val.Type(), dataVal.Type(), data) + } + + return nil +} + +func (d *Decoder) decodeInt(name string, data interface{}, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataKind := getKind(dataVal) + dataType := dataVal.Type() + + switch { + case dataKind == reflect.Int: + val.SetInt(dataVal.Int()) + case dataKind == reflect.Uint: + val.SetInt(int64(dataVal.Uint())) + case dataKind == reflect.Float32: + val.SetInt(int64(dataVal.Float())) + case dataKind == reflect.Bool && d.config.WeaklyTypedInput: + if dataVal.Bool() { + val.SetInt(1) + } else { + val.SetInt(0) + } + case dataKind == reflect.String && d.config.WeaklyTypedInput: + str := dataVal.String() + if str == "" { + str = "0" + } + + i, err := strconv.ParseInt(str, 0, val.Type().Bits()) + if err == nil { + val.SetInt(i) + } else { + return fmt.Errorf("cannot parse '%s' as int: %s", name, err) + } + case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number": + jn := data.(json.Number) + i, err := jn.Int64() + if err != nil { + return fmt.Errorf( + "error decoding json.Number into %s: %s", name, err) + } + val.SetInt(i) + default: + return fmt.Errorf( + "'%s' expected type '%s', got unconvertible type '%s', value: '%v'", + name, val.Type(), dataVal.Type(), data) + } + + return nil +} + +func (d *Decoder) decodeUint(name string, data interface{}, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataKind := getKind(dataVal) + dataType := dataVal.Type() + + switch { + case dataKind == reflect.Int: + i := dataVal.Int() + if i < 0 && !d.config.WeaklyTypedInput { + return fmt.Errorf("cannot parse '%s', %d overflows uint", + name, i) + } + val.SetUint(uint64(i)) + case dataKind == reflect.Uint: + val.SetUint(dataVal.Uint()) + case dataKind == reflect.Float32: + f := dataVal.Float() + if f < 0 && !d.config.WeaklyTypedInput { + return fmt.Errorf("cannot parse '%s', %f overflows uint", + name, f) + } + val.SetUint(uint64(f)) + case dataKind == reflect.Bool && d.config.WeaklyTypedInput: + if dataVal.Bool() { + val.SetUint(1) + } else { + val.SetUint(0) + } + case dataKind == reflect.String && d.config.WeaklyTypedInput: + str := dataVal.String() + if str == "" { + str = "0" + } + + i, err := strconv.ParseUint(str, 0, val.Type().Bits()) + if err == nil { + val.SetUint(i) + } else { + return fmt.Errorf("cannot parse '%s' as uint: %s", name, err) + } + case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number": + jn := data.(json.Number) + i, err := strconv.ParseUint(string(jn), 0, 64) + if err != nil { + return fmt.Errorf( + "error decoding json.Number into %s: %s", name, err) + } + val.SetUint(i) + default: + return fmt.Errorf( + "'%s' expected type '%s', got unconvertible type '%s', value: '%v'", + name, val.Type(), dataVal.Type(), data) + } + + return nil +} + +func (d *Decoder) decodeBool(name string, data interface{}, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataKind := getKind(dataVal) + + switch { + case dataKind == reflect.Bool: + val.SetBool(dataVal.Bool()) + case dataKind == reflect.Int && d.config.WeaklyTypedInput: + val.SetBool(dataVal.Int() != 0) + case dataKind == reflect.Uint && d.config.WeaklyTypedInput: + val.SetBool(dataVal.Uint() != 0) + case dataKind == reflect.Float32 && d.config.WeaklyTypedInput: + val.SetBool(dataVal.Float() != 0) + case dataKind == reflect.String && d.config.WeaklyTypedInput: + b, err := strconv.ParseBool(dataVal.String()) + if err == nil { + val.SetBool(b) + } else if dataVal.String() == "" { + val.SetBool(false) + } else { + return fmt.Errorf("cannot parse '%s' as bool: %s", name, err) + } + default: + return fmt.Errorf( + "'%s' expected type '%s', got unconvertible type '%#v', value: '%#v'", + name, val, dataVal, data) + } + + return nil +} + +func (d *Decoder) decodeFloat(name string, data interface{}, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataKind := getKind(dataVal) + dataType := dataVal.Type() + + switch { + case dataKind == reflect.Int: + val.SetFloat(float64(dataVal.Int())) + case dataKind == reflect.Uint: + val.SetFloat(float64(dataVal.Uint())) + case dataKind == reflect.Float32: + val.SetFloat(dataVal.Float()) + case dataKind == reflect.Bool && d.config.WeaklyTypedInput: + if dataVal.Bool() { + val.SetFloat(1) + } else { + val.SetFloat(0) + } + case dataKind == reflect.String && d.config.WeaklyTypedInput: + str := dataVal.String() + if str == "" { + str = "0" + } + + f, err := strconv.ParseFloat(str, val.Type().Bits()) + if err == nil { + val.SetFloat(f) + } else { + return fmt.Errorf("cannot parse '%s' as float: %s", name, err) + } + case dataType.PkgPath() == "encoding/json" && dataType.Name() == "Number": + jn := data.(json.Number) + i, err := jn.Float64() + if err != nil { + return fmt.Errorf( + "error decoding json.Number into %s: %s", name, err) + } + val.SetFloat(i) + default: + return fmt.Errorf( + "'%s' expected type '%s', got unconvertible type '%s', value: '%v'", + name, val.Type(), dataVal.Type(), data) + } + + return nil +} + +func (d *Decoder) decodeComplex(name string, data interface{}, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataKind := getKind(dataVal) + + switch { + case dataKind == reflect.Complex64: + val.SetComplex(dataVal.Complex()) + default: + return fmt.Errorf( + "'%s' expected type '%s', got unconvertible type '%s', value: '%v'", + name, val.Type(), dataVal.Type(), data) + } + + return nil +} + +func (d *Decoder) decodeMap(name string, data interface{}, val reflect.Value) error { + valType := val.Type() + valKeyType := valType.Key() + valElemType := valType.Elem() + + // By default we overwrite keys in the current map + valMap := val + + // If the map is nil or we're purposely zeroing fields, make a new map + if valMap.IsNil() || d.config.ZeroFields { + // Make a new map to hold our result + mapType := reflect.MapOf(valKeyType, valElemType) + valMap = reflect.MakeMap(mapType) + } + + dataVal := reflect.ValueOf(data) + + // Resolve any levels of indirection + for dataVal.Kind() == reflect.Pointer { + dataVal = reflect.Indirect(dataVal) + } + + // Check input type and based on the input type jump to the proper func + switch dataVal.Kind() { + case reflect.Map: + return d.decodeMapFromMap(name, dataVal, val, valMap) + + case reflect.Struct: + return d.decodeMapFromStruct(name, dataVal, val, valMap) + + case reflect.Array, reflect.Slice: + if d.config.WeaklyTypedInput { + return d.decodeMapFromSlice(name, dataVal, val, valMap) + } + + fallthrough + + default: + return fmt.Errorf("'%s' expected a map, got '%s'", name, dataVal.Kind()) + } +} + +func (d *Decoder) decodeMapFromSlice(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error { + // Special case for BC reasons (covered by tests) + if dataVal.Len() == 0 { + val.Set(valMap) + return nil + } + + for i := 0; i < dataVal.Len(); i++ { + err := d.decode( + name+"["+strconv.Itoa(i)+"]", + dataVal.Index(i).Interface(), val) + if err != nil { + return err + } + } + + return nil +} + +func (d *Decoder) decodeMapFromMap(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error { + valType := val.Type() + valKeyType := valType.Key() + valElemType := valType.Elem() + + // Accumulate errors + var errs []error + + // If the input data is empty, then we just match what the input data is. + if dataVal.Len() == 0 { + if dataVal.IsNil() { + if !val.IsNil() { + val.Set(dataVal) + } + } else { + // Set to empty allocated value + val.Set(valMap) + } + + return nil + } + + for _, k := range dataVal.MapKeys() { + fieldName := name + "[" + k.String() + "]" + + // First decode the key into the proper type + currentKey := reflect.Indirect(reflect.New(valKeyType)) + if err := d.decode(fieldName, k.Interface(), currentKey); err != nil { + errs = append(errs, err) + continue + } + + // Next decode the data into the proper type + v := dataVal.MapIndex(k).Interface() + currentVal := reflect.Indirect(reflect.New(valElemType)) + if err := d.decode(fieldName, v, currentVal); err != nil { + errs = append(errs, err) + continue + } + + valMap.SetMapIndex(currentKey, currentVal) + } + + // Set the built up map to the value + val.Set(valMap) + + return errors.Join(errs...) +} + +func (d *Decoder) decodeMapFromStruct(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error { + typ := dataVal.Type() + for i := 0; i < typ.NumField(); i++ { + // Get the StructField first since this is a cheap operation. If the + // field is unexported, then ignore it. + f := typ.Field(i) + if f.PkgPath != "" { + continue + } + + // Next get the actual value of this field and verify it is assignable + // to the map value. + v := dataVal.Field(i) + if !v.Type().AssignableTo(valMap.Type().Elem()) { + return fmt.Errorf("cannot assign type '%s' to map value field of type '%s'", v.Type(), valMap.Type().Elem()) + } + + tagValue := f.Tag.Get(d.config.TagName) + keyName := f.Name + + if tagValue == "" && d.config.IgnoreUntaggedFields { + continue + } + + // If Squash is set in the config, we squash the field down. + squash := d.config.Squash && v.Kind() == reflect.Struct && f.Anonymous + + v = dereferencePtrToStructIfNeeded(v, d.config.TagName) + + // Determine the name of the key in the map + if index := strings.Index(tagValue, ","); index != -1 { + if tagValue[:index] == "-" { + continue + } + // If "omitempty" is specified in the tag, it ignores empty values. + if strings.Index(tagValue[index+1:], "omitempty") != -1 && isEmptyValue(v) { + continue + } + + // If "squash" is specified in the tag, we squash the field down. + squash = squash || strings.Contains(tagValue[index+1:], d.config.SquashTagOption) + if squash { + // When squashing, the embedded type can be a pointer to a struct. + if v.Kind() == reflect.Ptr && v.Elem().Kind() == reflect.Struct { + v = v.Elem() + } + + // The final type must be a struct + if v.Kind() != reflect.Struct { + return fmt.Errorf("cannot squash non-struct type '%s'", v.Type()) + } + } else { + if strings.Index(tagValue[index+1:], "remain") != -1 { + if v.Kind() != reflect.Map { + return fmt.Errorf("error remain-tag field with invalid type: '%s'", v.Type()) + } + + ptr := v.MapRange() + for ptr.Next() { + valMap.SetMapIndex(ptr.Key(), ptr.Value()) + } + continue + } + } + if keyNameTagValue := tagValue[:index]; keyNameTagValue != "" { + keyName = keyNameTagValue + } + } else if len(tagValue) > 0 { + if tagValue == "-" { + continue + } + keyName = tagValue + } + + switch v.Kind() { + // this is an embedded struct, so handle it differently + case reflect.Struct: + x := reflect.New(v.Type()) + x.Elem().Set(v) + + vType := valMap.Type() + vKeyType := vType.Key() + vElemType := vType.Elem() + mType := reflect.MapOf(vKeyType, vElemType) + vMap := reflect.MakeMap(mType) + + // Creating a pointer to a map so that other methods can completely + // overwrite the map if need be (looking at you decodeMapFromMap). The + // indirection allows the underlying map to be settable (CanSet() == true) + // where as reflect.MakeMap returns an unsettable map. + addrVal := reflect.New(vMap.Type()) + reflect.Indirect(addrVal).Set(vMap) + + err := d.decode(keyName, x.Interface(), reflect.Indirect(addrVal)) + if err != nil { + return err + } + + // the underlying map may have been completely overwritten so pull + // it indirectly out of the enclosing value. + vMap = reflect.Indirect(addrVal) + + if squash { + for _, k := range vMap.MapKeys() { + valMap.SetMapIndex(k, vMap.MapIndex(k)) + } + } else { + valMap.SetMapIndex(reflect.ValueOf(keyName), vMap) + } + + default: + valMap.SetMapIndex(reflect.ValueOf(keyName), v) + } + } + + if val.CanAddr() { + val.Set(valMap) + } + + return nil +} + +func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) (bool, error) { + // If the input data is nil, then we want to just set the output + // pointer to be nil as well. + isNil := data == nil + if !isNil { + switch v := reflect.Indirect(reflect.ValueOf(data)); v.Kind() { + case reflect.Chan, + reflect.Func, + reflect.Interface, + reflect.Map, + reflect.Ptr, + reflect.Slice: + isNil = v.IsNil() + } + } + if isNil { + if !val.IsNil() && val.CanSet() { + nilValue := reflect.New(val.Type()).Elem() + val.Set(nilValue) + } + + return true, nil + } + + // Create an element of the concrete (non pointer) type and decode + // into that. Then set the value of the pointer to this type. + valType := val.Type() + valElemType := valType.Elem() + if val.CanSet() { + realVal := val + if realVal.IsNil() || d.config.ZeroFields { + realVal = reflect.New(valElemType) + } + + if err := d.decode(name, data, reflect.Indirect(realVal)); err != nil { + return false, err + } + + val.Set(realVal) + } else { + if err := d.decode(name, data, reflect.Indirect(val)); err != nil { + return false, err + } + } + return false, nil +} + +func (d *Decoder) decodeFunc(name string, data interface{}, val reflect.Value) error { + // Create an element of the concrete (non pointer) type and decode + // into that. Then set the value of the pointer to this type. + dataVal := reflect.Indirect(reflect.ValueOf(data)) + if val.Type() != dataVal.Type() { + return fmt.Errorf( + "'%s' expected type '%s', got unconvertible type '%s', value: '%v'", + name, val.Type(), dataVal.Type(), data) + } + val.Set(dataVal) + return nil +} + +func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataValKind := dataVal.Kind() + valType := val.Type() + valElemType := valType.Elem() + sliceType := reflect.SliceOf(valElemType) + + // If we have a non array/slice type then we first attempt to convert. + if dataValKind != reflect.Array && dataValKind != reflect.Slice { + if d.config.WeaklyTypedInput { + switch { + // Slice and array we use the normal logic + case dataValKind == reflect.Slice, dataValKind == reflect.Array: + break + + // Empty maps turn into empty slices + case dataValKind == reflect.Map: + if dataVal.Len() == 0 { + val.Set(reflect.MakeSlice(sliceType, 0, 0)) + return nil + } + // Create slice of maps of other sizes + return d.decodeSlice(name, []interface{}{data}, val) + + case dataValKind == reflect.String && valElemType.Kind() == reflect.Uint8: + return d.decodeSlice(name, []byte(dataVal.String()), val) + + // All other types we try to convert to the slice type + // and "lift" it into it. i.e. a string becomes a string slice. + default: + // Just re-try this function with data as a slice. + return d.decodeSlice(name, []interface{}{data}, val) + } + } + + return fmt.Errorf( + "'%s': source data must be an array or slice, got %s", name, dataValKind) + } + + // If the input value is nil, then don't allocate since empty != nil + if dataValKind != reflect.Array && dataVal.IsNil() { + return nil + } + + valSlice := val + if valSlice.IsNil() || d.config.ZeroFields { + // Make a new slice to hold our result, same size as the original data. + valSlice = reflect.MakeSlice(sliceType, dataVal.Len(), dataVal.Len()) + } else if valSlice.Len() > dataVal.Len() { + valSlice = valSlice.Slice(0, dataVal.Len()) + } + + // Accumulate any errors + var errs []error + + for i := 0; i < dataVal.Len(); i++ { + currentData := dataVal.Index(i).Interface() + for valSlice.Len() <= i { + valSlice = reflect.Append(valSlice, reflect.Zero(valElemType)) + } + currentField := valSlice.Index(i) + + fieldName := name + "[" + strconv.Itoa(i) + "]" + if err := d.decode(fieldName, currentData, currentField); err != nil { + errs = append(errs, err) + } + } + + // Finally, set the value to the slice we built up + val.Set(valSlice) + + return errors.Join(errs...) +} + +func (d *Decoder) decodeArray(name string, data interface{}, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + dataValKind := dataVal.Kind() + valType := val.Type() + valElemType := valType.Elem() + arrayType := reflect.ArrayOf(valType.Len(), valElemType) + + valArray := val + + if isComparable(valArray) && valArray.Interface() == reflect.Zero(valArray.Type()).Interface() || d.config.ZeroFields { + // Check input type + if dataValKind != reflect.Array && dataValKind != reflect.Slice { + if d.config.WeaklyTypedInput { + switch { + // Empty maps turn into empty arrays + case dataValKind == reflect.Map: + if dataVal.Len() == 0 { + val.Set(reflect.Zero(arrayType)) + return nil + } + + // All other types we try to convert to the array type + // and "lift" it into it. i.e. a string becomes a string array. + default: + // Just re-try this function with data as a slice. + return d.decodeArray(name, []interface{}{data}, val) + } + } + + return fmt.Errorf( + "'%s': source data must be an array or slice, got %s", name, dataValKind) + + } + if dataVal.Len() > arrayType.Len() { + return fmt.Errorf( + "'%s': expected source data to have length less or equal to %d, got %d", name, arrayType.Len(), dataVal.Len()) + } + + // Make a new array to hold our result, same size as the original data. + valArray = reflect.New(arrayType).Elem() + } + + // Accumulate any errors + var errs []error + + for i := 0; i < dataVal.Len(); i++ { + currentData := dataVal.Index(i).Interface() + currentField := valArray.Index(i) + + fieldName := name + "[" + strconv.Itoa(i) + "]" + if err := d.decode(fieldName, currentData, currentField); err != nil { + errs = append(errs, err) + } + } + + // Finally, set the value to the array we built up + val.Set(valArray) + + return errors.Join(errs...) +} + +func (d *Decoder) decodeStruct(name string, data interface{}, val reflect.Value) error { + dataVal := reflect.Indirect(reflect.ValueOf(data)) + + // If the type of the value to write to and the data match directly, + // then we just set it directly instead of recursing into the structure. + if dataVal.Type() == val.Type() { + val.Set(dataVal) + return nil + } + + dataValKind := dataVal.Kind() + switch dataValKind { + case reflect.Map: + return d.decodeStructFromMap(name, dataVal, val) + + case reflect.Struct: + // Not the most efficient way to do this but we can optimize later if + // we want to. To convert from struct to struct we go to map first + // as an intermediary. + + // Make a new map to hold our result + mapType := reflect.TypeOf((map[string]interface{})(nil)) + mval := reflect.MakeMap(mapType) + + // Creating a pointer to a map so that other methods can completely + // overwrite the map if need be (looking at you decodeMapFromMap). The + // indirection allows the underlying map to be settable (CanSet() == true) + // where as reflect.MakeMap returns an unsettable map. + addrVal := reflect.New(mval.Type()) + + reflect.Indirect(addrVal).Set(mval) + if err := d.decodeMapFromStruct(name, dataVal, reflect.Indirect(addrVal), mval); err != nil { + return err + } + + result := d.decodeStructFromMap(name, reflect.Indirect(addrVal), val) + return result + + default: + return fmt.Errorf("'%s' expected a map, got '%s'", name, dataVal.Kind()) + } +} + +func (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) error { + dataValType := dataVal.Type() + if kind := dataValType.Key().Kind(); kind != reflect.String && kind != reflect.Interface { + return fmt.Errorf( + "'%s' needs a map with string keys, has '%s' keys", + name, dataValType.Key().Kind()) + } + + dataValKeys := make(map[reflect.Value]struct{}) + dataValKeysUnused := make(map[interface{}]struct{}) + for _, dataValKey := range dataVal.MapKeys() { + dataValKeys[dataValKey] = struct{}{} + dataValKeysUnused[dataValKey.Interface()] = struct{}{} + } + + targetValKeysUnused := make(map[interface{}]struct{}) + + var errs []error + + // This slice will keep track of all the structs we'll be decoding. + // There can be more than one struct if there are embedded structs + // that are squashed. + structs := make([]reflect.Value, 1, 5) + structs[0] = val + + // Compile the list of all the fields that we're going to be decoding + // from all the structs. + type field struct { + field reflect.StructField + val reflect.Value + } + + // remainField is set to a valid field set with the "remain" tag if + // we are keeping track of remaining values. + var remainField *field + + fields := []field{} + for len(structs) > 0 { + structVal := structs[0] + structs = structs[1:] + + structType := structVal.Type() + + for i := 0; i < structType.NumField(); i++ { + fieldType := structType.Field(i) + fieldVal := structVal.Field(i) + if fieldVal.Kind() == reflect.Ptr && fieldVal.Elem().Kind() == reflect.Struct { + // Handle embedded struct pointers as embedded structs. + fieldVal = fieldVal.Elem() + } + + // If "squash" is specified in the tag, we squash the field down. + squash := d.config.Squash && fieldVal.Kind() == reflect.Struct && fieldType.Anonymous + remain := false + + // We always parse the tags cause we're looking for other tags too + tagParts := strings.Split(fieldType.Tag.Get(d.config.TagName), ",") + for _, tag := range tagParts[1:] { + if tag == d.config.SquashTagOption { + squash = true + break + } + + if tag == "remain" { + remain = true + break + } + } + + if squash { + switch fieldVal.Kind() { + case reflect.Struct: + structs = append(structs, fieldVal) + case reflect.Interface: + if !fieldVal.IsNil() { + structs = append(structs, fieldVal.Elem().Elem()) + } + default: + errs = append(errs, fmt.Errorf("%s: unsupported type for squash: %s", fieldType.Name, fieldVal.Kind())) + } + continue + } + + // Build our field + if remain { + remainField = &field{fieldType, fieldVal} + } else { + // Normal struct field, store it away + fields = append(fields, field{fieldType, fieldVal}) + } + } + } + + // for fieldType, field := range fields { + for _, f := range fields { + field, fieldValue := f.field, f.val + fieldName := field.Name + + tagValue := field.Tag.Get(d.config.TagName) + if tagValue == "" && d.config.IgnoreUntaggedFields { + continue + } + tagValue = strings.SplitN(tagValue, ",", 2)[0] + if tagValue != "" { + fieldName = tagValue + } + + rawMapKey := reflect.ValueOf(fieldName) + rawMapVal := dataVal.MapIndex(rawMapKey) + if !rawMapVal.IsValid() { + // Do a slower search by iterating over each key and + // doing case-insensitive search. + for dataValKey := range dataValKeys { + mK, ok := dataValKey.Interface().(string) + if !ok { + // Not a string key + continue + } + + if d.config.MatchName(mK, fieldName) { + rawMapKey = dataValKey + rawMapVal = dataVal.MapIndex(dataValKey) + break + } + } + + if !rawMapVal.IsValid() { + // There was no matching key in the map for the value in + // the struct. Remember it for potential errors and metadata. + targetValKeysUnused[fieldName] = struct{}{} + continue + } + } + + if !fieldValue.IsValid() { + // This should never happen + panic("field is not valid") + } + + // If we can't set the field, then it is unexported or something, + // and we just continue onwards. + if !fieldValue.CanSet() { + continue + } + + // Delete the key we're using from the unused map so we stop tracking + delete(dataValKeysUnused, rawMapKey.Interface()) + + // If the name is empty string, then we're at the root, and we + // don't dot-join the fields. + if name != "" { + fieldName = name + "." + fieldName + } + + if err := d.decode(fieldName, rawMapVal.Interface(), fieldValue); err != nil { + errs = append(errs, err) + } + } + + // If we have a "remain"-tagged field and we have unused keys then + // we put the unused keys directly into the remain field. + if remainField != nil && len(dataValKeysUnused) > 0 { + // Build a map of only the unused values + remain := map[interface{}]interface{}{} + for key := range dataValKeysUnused { + remain[key] = dataVal.MapIndex(reflect.ValueOf(key)).Interface() + } + + // Decode it as-if we were just decoding this map onto our map. + if err := d.decodeMap(name, remain, remainField.val); err != nil { + errs = append(errs, err) + } + + // Set the map to nil so we have none so that the next check will + // not error (ErrorUnused) + dataValKeysUnused = nil + } + + if d.config.ErrorUnused && len(dataValKeysUnused) > 0 { + keys := make([]string, 0, len(dataValKeysUnused)) + for rawKey := range dataValKeysUnused { + keys = append(keys, rawKey.(string)) + } + sort.Strings(keys) + + err := fmt.Errorf("'%s' has invalid keys: %s", name, strings.Join(keys, ", ")) + errs = append(errs, err) + } + + if d.config.ErrorUnset && len(targetValKeysUnused) > 0 { + keys := make([]string, 0, len(targetValKeysUnused)) + for rawKey := range targetValKeysUnused { + keys = append(keys, rawKey.(string)) + } + sort.Strings(keys) + + err := fmt.Errorf("'%s' has unset fields: %s", name, strings.Join(keys, ", ")) + errs = append(errs, err) + } + + if err := errors.Join(errs...); err != nil { + return err + } + + // Add the unused keys to the list of unused keys if we're tracking metadata + if d.config.Metadata != nil { + for rawKey := range dataValKeysUnused { + key := rawKey.(string) + if name != "" { + key = name + "." + key + } + + d.config.Metadata.Unused = append(d.config.Metadata.Unused, key) + } + for rawKey := range targetValKeysUnused { + key := rawKey.(string) + if name != "" { + key = name + "." + key + } + + d.config.Metadata.Unset = append(d.config.Metadata.Unset, key) + } + } + + return nil +} + +func isEmptyValue(v reflect.Value) bool { + switch getKind(v) { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + return v.Len() == 0 + case reflect.Bool: + return !v.Bool() + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: + return v.Uint() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + case reflect.Interface, reflect.Ptr: + return v.IsNil() + } + return false +} + +func getKind(val reflect.Value) reflect.Kind { + kind := val.Kind() + + switch { + case kind >= reflect.Int && kind <= reflect.Int64: + return reflect.Int + case kind >= reflect.Uint && kind <= reflect.Uint64: + return reflect.Uint + case kind >= reflect.Float32 && kind <= reflect.Float64: + return reflect.Float32 + case kind >= reflect.Complex64 && kind <= reflect.Complex128: + return reflect.Complex64 + default: + return kind + } +} + +func isStructTypeConvertibleToMap(typ reflect.Type, checkMapstructureTags bool, tagName string) bool { + for i := 0; i < typ.NumField(); i++ { + f := typ.Field(i) + if f.PkgPath == "" && !checkMapstructureTags { // check for unexported fields + return true + } + if checkMapstructureTags && f.Tag.Get(tagName) != "" { // check for mapstructure tags inside + return true + } + } + return false +} + +func dereferencePtrToStructIfNeeded(v reflect.Value, tagName string) reflect.Value { + if v.Kind() != reflect.Ptr || v.Elem().Kind() != reflect.Struct { + return v + } + deref := v.Elem() + derefT := deref.Type() + if isStructTypeConvertibleToMap(derefT, true, tagName) { + return deref + } + return v +} diff --git a/vendor/github.com/go-viper/mapstructure/v2/reflect_go1_19.go b/vendor/github.com/go-viper/mapstructure/v2/reflect_go1_19.go new file mode 100644 index 0000000000..d0913fff6c --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/reflect_go1_19.go @@ -0,0 +1,44 @@ +//go:build !go1.20 + +package mapstructure + +import "reflect" + +func isComparable(v reflect.Value) bool { + k := v.Kind() + switch k { + case reflect.Invalid: + return false + + case reflect.Array: + switch v.Type().Elem().Kind() { + case reflect.Interface, reflect.Array, reflect.Struct: + for i := 0; i < v.Type().Len(); i++ { + // if !v.Index(i).Comparable() { + if !isComparable(v.Index(i)) { + return false + } + } + return true + } + return v.Type().Comparable() + + case reflect.Interface: + // return v.Elem().Comparable() + return isComparable(v.Elem()) + + case reflect.Struct: + for i := 0; i < v.NumField(); i++ { + return false + + // if !v.Field(i).Comparable() { + if !isComparable(v.Field(i)) { + return false + } + } + return true + + default: + return v.Type().Comparable() + } +} diff --git a/vendor/github.com/go-viper/mapstructure/v2/reflect_go1_20.go b/vendor/github.com/go-viper/mapstructure/v2/reflect_go1_20.go new file mode 100644 index 0000000000..f8255a1b17 --- /dev/null +++ b/vendor/github.com/go-viper/mapstructure/v2/reflect_go1_20.go @@ -0,0 +1,10 @@ +//go:build go1.20 + +package mapstructure + +import "reflect" + +// TODO: remove once we drop support for Go <1.20 +func isComparable(v reflect.Value) bool { + return v.Comparable() +} diff --git a/vendor/github.com/google/go-cmp/cmp/cmpopts/sort.go b/vendor/github.com/google/go-cmp/cmp/cmpopts/sort.go index c6d09dae40..720f3cdf57 100644 --- a/vendor/github.com/google/go-cmp/cmp/cmpopts/sort.go +++ b/vendor/github.com/google/go-cmp/cmp/cmpopts/sort.go @@ -14,22 +14,29 @@ import ( ) // SortSlices returns a [cmp.Transformer] option that sorts all []V. -// The less function must be of the form "func(T, T) bool" which is used to -// sort any slice with element type V that is assignable to T. +// The lessOrCompareFunc function must be either +// a less function of the form "func(T, T) bool" or +// a compare function of the format "func(T, T) int" +// which is used to sort any slice with element type V that is assignable to T. // -// The less function must be: +// A less function must be: // - Deterministic: less(x, y) == less(x, y) // - Irreflexive: !less(x, x) // - Transitive: if !less(x, y) and !less(y, z), then !less(x, z) // -// The less function does not have to be "total". That is, if !less(x, y) and -// !less(y, x) for two elements x and y, their relative order is maintained. +// A compare function must be: +// - Deterministic: compare(x, y) == compare(x, y) +// - Irreflexive: compare(x, x) == 0 +// - Transitive: if !less(x, y) and !less(y, z), then !less(x, z) +// +// The function does not have to be "total". That is, if x != y, but +// less or compare report inequality, their relative order is maintained. // // SortSlices can be used in conjunction with [EquateEmpty]. -func SortSlices(lessFunc interface{}) cmp.Option { - vf := reflect.ValueOf(lessFunc) - if !function.IsType(vf.Type(), function.Less) || vf.IsNil() { - panic(fmt.Sprintf("invalid less function: %T", lessFunc)) +func SortSlices(lessOrCompareFunc interface{}) cmp.Option { + vf := reflect.ValueOf(lessOrCompareFunc) + if (!function.IsType(vf.Type(), function.Less) && !function.IsType(vf.Type(), function.Compare)) || vf.IsNil() { + panic(fmt.Sprintf("invalid less or compare function: %T", lessOrCompareFunc)) } ss := sliceSorter{vf.Type().In(0), vf} return cmp.FilterValues(ss.filter, cmp.Transformer("cmpopts.SortSlices", ss.sort)) @@ -79,28 +86,40 @@ func (ss sliceSorter) checkSort(v reflect.Value) { } func (ss sliceSorter) less(v reflect.Value, i, j int) bool { vx, vy := v.Index(i), v.Index(j) - return ss.fnc.Call([]reflect.Value{vx, vy})[0].Bool() + vo := ss.fnc.Call([]reflect.Value{vx, vy})[0] + if vo.Kind() == reflect.Bool { + return vo.Bool() + } else { + return vo.Int() < 0 + } } -// SortMaps returns a [cmp.Transformer] option that flattens map[K]V types to be a -// sorted []struct{K, V}. The less function must be of the form -// "func(T, T) bool" which is used to sort any map with key K that is -// assignable to T. +// SortMaps returns a [cmp.Transformer] option that flattens map[K]V types to be +// a sorted []struct{K, V}. The lessOrCompareFunc function must be either +// a less function of the form "func(T, T) bool" or +// a compare function of the format "func(T, T) int" +// which is used to sort any map with key K that is assignable to T. // // Flattening the map into a slice has the property that [cmp.Equal] is able to // use [cmp.Comparer] options on K or the K.Equal method if it exists. // -// The less function must be: +// A less function must be: // - Deterministic: less(x, y) == less(x, y) // - Irreflexive: !less(x, x) // - Transitive: if !less(x, y) and !less(y, z), then !less(x, z) // - Total: if x != y, then either less(x, y) or less(y, x) // +// A compare function must be: +// - Deterministic: compare(x, y) == compare(x, y) +// - Irreflexive: compare(x, x) == 0 +// - Transitive: if compare(x, y) < 0 and compare(y, z) < 0, then compare(x, z) < 0 +// - Total: if x != y, then compare(x, y) != 0 +// // SortMaps can be used in conjunction with [EquateEmpty]. -func SortMaps(lessFunc interface{}) cmp.Option { - vf := reflect.ValueOf(lessFunc) - if !function.IsType(vf.Type(), function.Less) || vf.IsNil() { - panic(fmt.Sprintf("invalid less function: %T", lessFunc)) +func SortMaps(lessOrCompareFunc interface{}) cmp.Option { + vf := reflect.ValueOf(lessOrCompareFunc) + if (!function.IsType(vf.Type(), function.Less) && !function.IsType(vf.Type(), function.Compare)) || vf.IsNil() { + panic(fmt.Sprintf("invalid less or compare function: %T", lessOrCompareFunc)) } ms := mapSorter{vf.Type().In(0), vf} return cmp.FilterValues(ms.filter, cmp.Transformer("cmpopts.SortMaps", ms.sort)) @@ -143,5 +162,10 @@ func (ms mapSorter) checkSort(v reflect.Value) { } func (ms mapSorter) less(v reflect.Value, i, j int) bool { vx, vy := v.Index(i).Field(0), v.Index(j).Field(0) - return ms.fnc.Call([]reflect.Value{vx, vy})[0].Bool() + vo := ms.fnc.Call([]reflect.Value{vx, vy})[0] + if vo.Kind() == reflect.Bool { + return vo.Bool() + } else { + return vo.Int() < 0 + } } diff --git a/vendor/github.com/google/go-cmp/cmp/internal/function/func.go b/vendor/github.com/google/go-cmp/cmp/internal/function/func.go index d127d43623..def01a6be3 100644 --- a/vendor/github.com/google/go-cmp/cmp/internal/function/func.go +++ b/vendor/github.com/google/go-cmp/cmp/internal/function/func.go @@ -19,6 +19,7 @@ const ( tbFunc // func(T) bool ttbFunc // func(T, T) bool + ttiFunc // func(T, T) int trbFunc // func(T, R) bool tibFunc // func(T, I) bool trFunc // func(T) R @@ -28,11 +29,13 @@ const ( Transformer = trFunc // func(T) R ValueFilter = ttbFunc // func(T, T) bool Less = ttbFunc // func(T, T) bool + Compare = ttiFunc // func(T, T) int ValuePredicate = tbFunc // func(T) bool KeyValuePredicate = trbFunc // func(T, R) bool ) var boolType = reflect.TypeOf(true) +var intType = reflect.TypeOf(0) // IsType reports whether the reflect.Type is of the specified function type. func IsType(t reflect.Type, ft funcType) bool { @@ -49,6 +52,10 @@ func IsType(t reflect.Type, ft funcType) bool { if ni == 2 && no == 1 && t.In(0) == t.In(1) && t.Out(0) == boolType { return true } + case ttiFunc: // func(T, T) int + if ni == 2 && no == 1 && t.In(0) == t.In(1) && t.Out(0) == intType { + return true + } case trbFunc: // func(T, R) bool if ni == 2 && no == 1 && t.Out(0) == boolType { return true diff --git a/vendor/github.com/google/go-cmp/cmp/options.go b/vendor/github.com/google/go-cmp/cmp/options.go index 754496f3b3..ba3fce81ff 100644 --- a/vendor/github.com/google/go-cmp/cmp/options.go +++ b/vendor/github.com/google/go-cmp/cmp/options.go @@ -232,7 +232,15 @@ func (validator) apply(s *state, vx, vy reflect.Value) { if t := s.curPath.Index(-2).Type(); t.Name() != "" { // Named type with unexported fields. name = fmt.Sprintf("%q.%v", t.PkgPath(), t.Name()) // e.g., "path/to/package".MyType - if _, ok := reflect.New(t).Interface().(error); ok { + isProtoMessage := func(t reflect.Type) bool { + m, ok := reflect.PointerTo(t).MethodByName("ProtoReflect") + return ok && m.Type.NumIn() == 1 && m.Type.NumOut() == 1 && + m.Type.Out(0).PkgPath() == "google.golang.org/protobuf/reflect/protoreflect" && + m.Type.Out(0).Name() == "Message" + } + if isProtoMessage(t) { + help = `consider using "google.golang.org/protobuf/testing/protocmp".Transform to compare proto.Message types` + } else if _, ok := reflect.New(t).Interface().(error); ok { help = "consider using cmpopts.EquateErrors to compare error values" } else if t.Comparable() { help = "consider using cmpopts.EquateComparable to compare comparable Go types" diff --git a/vendor/github.com/google/go-github/v68/github/orgs_rules.go b/vendor/github.com/google/go-github/v68/github/orgs_rules.go deleted file mode 100644 index 37c06a7333..0000000000 --- a/vendor/github.com/google/go-github/v68/github/orgs_rules.go +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2023 The go-github 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 github - -import ( - "context" - "fmt" -) - -// GetAllOrganizationRulesets gets all the rulesets for the specified organization. -// -// GitHub API docs: https://docs.github.com/rest/orgs/rules#get-all-organization-repository-rulesets -// -//meta:operation GET /orgs/{org}/rulesets -func (s *OrganizationsService) GetAllOrganizationRulesets(ctx context.Context, org string) ([]*Ruleset, *Response, error) { - u := fmt.Sprintf("orgs/%v/rulesets", org) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var rulesets []*Ruleset - resp, err := s.client.Do(ctx, req, &rulesets) - if err != nil { - return nil, resp, err - } - - return rulesets, resp, nil -} - -// CreateOrganizationRuleset creates a ruleset for the specified organization. -// -// GitHub API docs: https://docs.github.com/rest/orgs/rules#create-an-organization-repository-ruleset -// -//meta:operation POST /orgs/{org}/rulesets -func (s *OrganizationsService) CreateOrganizationRuleset(ctx context.Context, org string, rs *Ruleset) (*Ruleset, *Response, error) { - u := fmt.Sprintf("orgs/%v/rulesets", org) - - req, err := s.client.NewRequest("POST", u, rs) - if err != nil { - return nil, nil, err - } - - var ruleset *Ruleset - resp, err := s.client.Do(ctx, req, &ruleset) - if err != nil { - return nil, resp, err - } - - return ruleset, resp, nil -} - -// GetOrganizationRuleset gets a ruleset from the specified organization. -// -// GitHub API docs: https://docs.github.com/rest/orgs/rules#get-an-organization-repository-ruleset -// -//meta:operation GET /orgs/{org}/rulesets/{ruleset_id} -func (s *OrganizationsService) GetOrganizationRuleset(ctx context.Context, org string, rulesetID int64) (*Ruleset, *Response, error) { - u := fmt.Sprintf("orgs/%v/rulesets/%v", org, rulesetID) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var ruleset *Ruleset - resp, err := s.client.Do(ctx, req, &ruleset) - if err != nil { - return nil, resp, err - } - - return ruleset, resp, nil -} - -// UpdateOrganizationRuleset updates a ruleset from the specified organization. -// -// GitHub API docs: https://docs.github.com/rest/orgs/rules#update-an-organization-repository-ruleset -// -//meta:operation PUT /orgs/{org}/rulesets/{ruleset_id} -func (s *OrganizationsService) UpdateOrganizationRuleset(ctx context.Context, org string, rulesetID int64, rs *Ruleset) (*Ruleset, *Response, error) { - u := fmt.Sprintf("orgs/%v/rulesets/%v", org, rulesetID) - - req, err := s.client.NewRequest("PUT", u, rs) - if err != nil { - return nil, nil, err - } - - var ruleset *Ruleset - resp, err := s.client.Do(ctx, req, &ruleset) - if err != nil { - return nil, resp, err - } - - return ruleset, resp, nil -} - -// DeleteOrganizationRuleset deletes a ruleset from the specified organization. -// -// GitHub API docs: https://docs.github.com/rest/orgs/rules#delete-an-organization-repository-ruleset -// -//meta:operation DELETE /orgs/{org}/rulesets/{ruleset_id} -func (s *OrganizationsService) DeleteOrganizationRuleset(ctx context.Context, org string, rulesetID int64) (*Response, error) { - u := fmt.Sprintf("orgs/%v/rulesets/%v", org, rulesetID) - - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/v68/github/packages.go b/vendor/github.com/google/go-github/v68/github/packages.go deleted file mode 100644 index ef7df07405..0000000000 --- a/vendor/github.com/google/go-github/v68/github/packages.go +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2020 The go-github 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 github - -// Package represents a GitHub package. -type Package struct { - ID *int64 `json:"id,omitempty"` - Name *string `json:"name,omitempty"` - PackageType *string `json:"package_type,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` - Owner *User `json:"owner,omitempty"` - PackageVersion *PackageVersion `json:"package_version,omitempty"` - Registry *PackageRegistry `json:"registry,omitempty"` - URL *string `json:"url,omitempty"` - VersionCount *int64 `json:"version_count,omitempty"` - Visibility *string `json:"visibility,omitempty"` - Repository *Repository `json:"repository,omitempty"` -} - -func (p Package) String() string { - return Stringify(p) -} - -// PackageVersion represents a GitHub package version. -type PackageVersion struct { - ID *int64 `json:"id,omitempty"` - Version *string `json:"version,omitempty"` - Summary *string `json:"summary,omitempty"` - Body *string `json:"body,omitempty"` - BodyHTML *string `json:"body_html,omitempty"` - Release *PackageRelease `json:"release,omitempty"` - Manifest *string `json:"manifest,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - TagName *string `json:"tag_name,omitempty"` - TargetCommitish *string `json:"target_commitish,omitempty"` - TargetOID *string `json:"target_oid,omitempty"` - Draft *bool `json:"draft,omitempty"` - Prerelease *bool `json:"prerelease,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` - PackageFiles []*PackageFile `json:"package_files,omitempty"` - Author *User `json:"author,omitempty"` - InstallationCommand *string `json:"installation_command,omitempty"` - Metadata *PackageMetadata `json:"metadata,omitempty"` - PackageHTMLURL *string `json:"package_html_url,omitempty"` - Name *string `json:"name,omitempty"` - URL *string `json:"url,omitempty"` -} - -func (pv PackageVersion) String() string { - return Stringify(pv) -} - -// PackageRelease represents a GitHub package version release. -type PackageRelease struct { - URL *string `json:"url,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - ID *int64 `json:"id,omitempty"` - TagName *string `json:"tag_name,omitempty"` - TargetCommitish *string `json:"target_commitish,omitempty"` - Name *string `json:"name,omitempty"` - Draft *bool `json:"draft,omitempty"` - Author *User `json:"author,omitempty"` - Prerelease *bool `json:"prerelease,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - PublishedAt *Timestamp `json:"published_at,omitempty"` -} - -func (r PackageRelease) String() string { - return Stringify(r) -} - -// PackageFile represents a GitHub package version release file. -type PackageFile struct { - DownloadURL *string `json:"download_url,omitempty"` - ID *int64 `json:"id,omitempty"` - Name *string `json:"name,omitempty"` - SHA256 *string `json:"sha256,omitempty"` - SHA1 *string `json:"sha1,omitempty"` - MD5 *string `json:"md5,omitempty"` - ContentType *string `json:"content_type,omitempty"` - State *string `json:"state,omitempty"` - Author *User `json:"author,omitempty"` - Size *int64 `json:"size,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` -} - -func (pf PackageFile) String() string { - return Stringify(pf) -} - -// PackageRegistry represents a GitHub package registry. -type PackageRegistry struct { - AboutURL *string `json:"about_url,omitempty"` - Name *string `json:"name,omitempty"` - Type *string `json:"type,omitempty"` - URL *string `json:"url,omitempty"` - Vendor *string `json:"vendor,omitempty"` -} - -func (r PackageRegistry) String() string { - return Stringify(r) -} - -// PackageListOptions represents the optional list options for a package. -type PackageListOptions struct { - // Visibility of packages "public", "internal" or "private". - Visibility *string `url:"visibility,omitempty"` - - // PackageType represents the type of package. - // It can be one of "npm", "maven", "rubygems", "nuget", "docker", or "container". - PackageType *string `url:"package_type,omitempty"` - - // State of package either "active" or "deleted". - State *string `url:"state,omitempty"` - - ListOptions -} - -// PackageMetadata represents metadata from a package. -type PackageMetadata struct { - PackageType *string `json:"package_type,omitempty"` - Container *PackageContainerMetadata `json:"container,omitempty"` -} - -func (r PackageMetadata) String() string { - return Stringify(r) -} - -// PackageContainerMetadata represents container metadata for docker container packages. -type PackageContainerMetadata struct { - Tags []string `json:"tags,omitempty"` -} - -func (r PackageContainerMetadata) String() string { - return Stringify(r) -} diff --git a/vendor/github.com/google/go-github/v68/github/repos_rules.go b/vendor/github.com/google/go-github/v68/github/repos_rules.go deleted file mode 100644 index b113553a24..0000000000 --- a/vendor/github.com/google/go-github/v68/github/repos_rules.go +++ /dev/null @@ -1,995 +0,0 @@ -// Copyright 2023 The go-github 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 github - -import ( - "context" - "encoding/json" - "fmt" -) - -// BypassActor represents the bypass actors from a ruleset. -type BypassActor struct { - ActorID *int64 `json:"actor_id,omitempty"` - // Possible values for ActorType are: RepositoryRole, Team, Integration, OrganizationAdmin - ActorType *string `json:"actor_type,omitempty"` - // Possible values for BypassMode are: always, pull_request - BypassMode *string `json:"bypass_mode,omitempty"` -} - -// RulesetLink represents a single link object from GitHub ruleset request _links. -type RulesetLink struct { - HRef *string `json:"href,omitempty"` -} - -// RulesetLinks represents the "_links" object in a Ruleset. -type RulesetLinks struct { - Self *RulesetLink `json:"self,omitempty"` -} - -// RulesetRefConditionParameters represents the conditions object for ref_names. -type RulesetRefConditionParameters struct { - Include []string `json:"include"` - Exclude []string `json:"exclude"` -} - -// RulesetRepositoryNamesConditionParameters represents the conditions object for repository_names. -type RulesetRepositoryNamesConditionParameters struct { - Include []string `json:"include"` - Exclude []string `json:"exclude"` - Protected *bool `json:"protected,omitempty"` -} - -// RulesetRepositoryIDsConditionParameters represents the conditions object for repository_ids. -type RulesetRepositoryIDsConditionParameters struct { - RepositoryIDs []int64 `json:"repository_ids,omitempty"` -} - -// RulesetRepositoryPropertyTargetParameters represents a repository_property name and values to be used for targeting. -type RulesetRepositoryPropertyTargetParameters struct { - Name string `json:"name"` - Values []string `json:"property_values"` - Source *string `json:"source,omitempty"` -} - -// RulesetRepositoryPropertyConditionParameters represents the conditions object for repository_property. -type RulesetRepositoryPropertyConditionParameters struct { - Include []RulesetRepositoryPropertyTargetParameters `json:"include"` - Exclude []RulesetRepositoryPropertyTargetParameters `json:"exclude"` -} - -// RulesetConditions represents the conditions object in a ruleset. -// Set either RepositoryName or RepositoryID or RepositoryProperty, not more than one. -type RulesetConditions struct { - RefName *RulesetRefConditionParameters `json:"ref_name,omitempty"` - RepositoryName *RulesetRepositoryNamesConditionParameters `json:"repository_name,omitempty"` - RepositoryID *RulesetRepositoryIDsConditionParameters `json:"repository_id,omitempty"` - RepositoryProperty *RulesetRepositoryPropertyConditionParameters `json:"repository_property,omitempty"` -} - -// RulePatternParameters represents the rule pattern parameters. -type RulePatternParameters struct { - Name *string `json:"name,omitempty"` - // If Negate is true, the rule will fail if the pattern matches. - Negate *bool `json:"negate,omitempty"` - // Possible values for Operator are: starts_with, ends_with, contains, regex - Operator string `json:"operator"` - Pattern string `json:"pattern"` -} - -// RuleFileParameters represents a list of file paths. -type RuleFileParameters struct { - RestrictedFilePaths *[]string `json:"restricted_file_paths"` -} - -// RuleMaxFilePathLengthParameters represents the max_file_path_length rule parameters. -type RuleMaxFilePathLengthParameters struct { - MaxFilePathLength int `json:"max_file_path_length"` -} - -// RuleFileExtensionRestrictionParameters represents the file_extension_restriction rule parameters. -type RuleFileExtensionRestrictionParameters struct { - RestrictedFileExtensions []string `json:"restricted_file_extensions"` -} - -// RuleMaxFileSizeParameters represents the max_file_size rule parameters. -type RuleMaxFileSizeParameters struct { - MaxFileSize int64 `json:"max_file_size"` -} - -// UpdateAllowsFetchAndMergeRuleParameters represents the update rule parameters. -type UpdateAllowsFetchAndMergeRuleParameters struct { - UpdateAllowsFetchAndMerge bool `json:"update_allows_fetch_and_merge"` -} - -// RequiredDeploymentEnvironmentsRuleParameters represents the required_deployments rule parameters. -type RequiredDeploymentEnvironmentsRuleParameters struct { - RequiredDeploymentEnvironments []string `json:"required_deployment_environments"` -} - -// PullRequestRuleParameters represents the pull_request rule parameters. -type PullRequestRuleParameters struct { - DismissStaleReviewsOnPush bool `json:"dismiss_stale_reviews_on_push"` - RequireCodeOwnerReview bool `json:"require_code_owner_review"` - RequireLastPushApproval bool `json:"require_last_push_approval"` - RequiredApprovingReviewCount int `json:"required_approving_review_count"` - RequiredReviewThreadResolution bool `json:"required_review_thread_resolution"` -} - -// RuleRequiredStatusChecks represents the RequiredStatusChecks for the RequiredStatusChecksRuleParameters object. -type RuleRequiredStatusChecks struct { - Context string `json:"context"` - IntegrationID *int64 `json:"integration_id,omitempty"` -} - -// MergeQueueRuleParameters represents the merge_queue rule parameters. -type MergeQueueRuleParameters struct { - CheckResponseTimeoutMinutes int `json:"check_response_timeout_minutes"` - // Possible values for GroupingStrategy are: ALLGREEN, HEADGREEN - GroupingStrategy string `json:"grouping_strategy"` - MaxEntriesToBuild int `json:"max_entries_to_build"` - MaxEntriesToMerge int `json:"max_entries_to_merge"` - // Possible values for MergeMethod are: MERGE, SQUASH, REBASE - MergeMethod string `json:"merge_method"` - MinEntriesToMerge int `json:"min_entries_to_merge"` - MinEntriesToMergeWaitMinutes int `json:"min_entries_to_merge_wait_minutes"` -} - -// RequiredStatusChecksRuleParameters represents the required_status_checks rule parameters. -type RequiredStatusChecksRuleParameters struct { - DoNotEnforceOnCreate *bool `json:"do_not_enforce_on_create,omitempty"` - RequiredStatusChecks []RuleRequiredStatusChecks `json:"required_status_checks"` - StrictRequiredStatusChecksPolicy bool `json:"strict_required_status_checks_policy"` -} - -// RuleRequiredWorkflow represents the Workflow for the RequireWorkflowsRuleParameters object. -type RuleRequiredWorkflow struct { - Path string `json:"path"` - Ref *string `json:"ref,omitempty"` - RepositoryID *int64 `json:"repository_id,omitempty"` - Sha *string `json:"sha,omitempty"` -} - -// RequiredWorkflowsRuleParameters represents the workflows rule parameters. -type RequiredWorkflowsRuleParameters struct { - DoNotEnforceOnCreate bool `json:"do_not_enforce_on_create,omitempty"` - RequiredWorkflows []*RuleRequiredWorkflow `json:"workflows"` -} - -// RuleRequiredCodeScanningTool represents a single required code-scanning tool for the RequiredCodeScanningParameters object. -type RuleRequiredCodeScanningTool struct { - AlertsThreshold string `json:"alerts_threshold"` - SecurityAlertsThreshold string `json:"security_alerts_threshold"` - Tool string `json:"tool"` -} - -// RequiredCodeScanningRuleParameters represents the code_scanning rule parameters. -type RequiredCodeScanningRuleParameters struct { - RequiredCodeScanningTools []*RuleRequiredCodeScanningTool `json:"code_scanning_tools"` -} - -// RepositoryRule represents a GitHub Rule. -type RepositoryRule struct { - Type string `json:"type"` - Parameters *json.RawMessage `json:"parameters,omitempty"` - RulesetSourceType string `json:"ruleset_source_type"` - RulesetSource string `json:"ruleset_source"` - RulesetID int64 `json:"ruleset_id"` -} - -// RepositoryRulesetEditedChanges represents the changes made to a repository ruleset. -type RepositoryRulesetEditedChanges struct { - Name *RepositoryRulesetEditedSource `json:"name,omitempty"` - Enforcement *RepositoryRulesetEditedSource `json:"enforcement,omitempty"` - Conditions *RepositoryRulesetEditedConditions `json:"conditions,omitempty"` - Rules *RepositoryRulesetEditedRules `json:"rules,omitempty"` -} - -// RepositoryRulesetEditedSource represents a source change for the ruleset. -type RepositoryRulesetEditedSource struct { - From *string `json:"from,omitempty"` -} - -// RepositoryRulesetEditedSources represents multiple source changes for the ruleset. -type RepositoryRulesetEditedSources struct { - From []string `json:"from,omitempty"` -} - -// RepositoryRulesetEditedConditions holds changes to conditions in a ruleset. -type RepositoryRulesetEditedConditions struct { - Added []*RepositoryRulesetRefCondition `json:"added,omitempty"` - Deleted []*RepositoryRulesetRefCondition `json:"deleted,omitempty"` - Updated []*RepositoryRulesetEditedUpdatedConditions `json:"updated,omitempty"` -} - -// RepositoryRulesetEditedRules holds changes to rules in a ruleset. -type RepositoryRulesetEditedRules struct { - Added []*RepositoryRulesetRule `json:"added,omitempty"` - Deleted []*RepositoryRulesetRule `json:"deleted,omitempty"` - Updated []*RepositoryRulesetUpdatedRules `json:"updated,omitempty"` -} - -// RepositoryRulesetRefCondition represents a reference condition for the ruleset. -type RepositoryRulesetRefCondition struct { - RefName *RulesetRefConditionParameters `json:"ref_name,omitempty"` -} - -// RepositoryRulesetEditedUpdatedConditions holds updates to conditions in a ruleset. -type RepositoryRulesetEditedUpdatedConditions struct { - Condition *RepositoryRulesetRefCondition `json:"condition,omitempty"` - Changes *RepositoryRulesetUpdatedConditionsEdited `json:"changes,omitempty"` -} - -// RepositoryRulesetUpdatedConditionsEdited holds the edited updates to conditions in a ruleset. -type RepositoryRulesetUpdatedConditionsEdited struct { - ConditionType *RepositoryRulesetEditedSource `json:"condition_type,omitempty"` - Target *RepositoryRulesetEditedSource `json:"target,omitempty"` - Include *RepositoryRulesetEditedSources `json:"include,omitempty"` - Exclude *RepositoryRulesetEditedSources `json:"exclude,omitempty"` -} - -// RepositoryRulesetUpdatedRules holds updates to rules in a ruleset. -type RepositoryRulesetUpdatedRules struct { - Rule *RepositoryRulesetRule `json:"rule,omitempty"` - Changes *RepositoryRulesetEditedRuleChanges `json:"changes,omitempty"` -} - -// RepositoryRulesetEditedRuleChanges holds changes made to a rule in a ruleset. -type RepositoryRulesetEditedRuleChanges struct { - Configuration *RepositoryRulesetEditedSources `json:"configuration,omitempty"` - RuleType *RepositoryRulesetEditedSources `json:"rule_type,omitempty"` - Pattern *RepositoryRulesetEditedSources `json:"pattern,omitempty"` -} - -// RepositoryRuleset represents the structure of a ruleset associated with a GitHub repository. -type RepositoryRuleset struct { - ID int64 `json:"id"` - Name string `json:"name"` - // Possible values for target: "branch", "tag", "push" - Target *string `json:"target,omitempty"` - // Possible values for source type: "Repository", "Organization" - SourceType *string `json:"source_type,omitempty"` - Source string `json:"source"` - // Possible values for enforcement: "disabled", "active", "evaluate" - Enforcement string `json:"enforcement"` - BypassActors []*BypassActor `json:"bypass_actors,omitempty"` - // Possible values for current user can bypass: "always", "pull_requests_only", "never" - CurrentUserCanBypass *string `json:"current_user_can_bypass,omitempty"` - NodeID *string `json:"node_id,omitempty"` - Links *RepositoryRulesetLink `json:"_links,omitempty"` - Conditions json.RawMessage `json:"conditions,omitempty"` - Rules []*RepositoryRulesetRule `json:"rules,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` -} - -// RepositoryRulesetRule represents individual rules which are present in a repository's ruleset. -type RepositoryRulesetRule struct { - Creation *RepositoryRulesetRuleType `json:"creation,omitempty"` - Update *RepositoryRulesetUpdateRule `json:"update,omitempty"` - Deletion *RepositoryRulesetRuleType `json:"deletion,omitempty"` - RequiredLinearHistory *RepositoryRulesetRuleType `json:"required_linear_history,omitempty"` - MergeQueue *RepositoryRulesetMergeQueueRule `json:"merge_queue,omitempty"` - RequiredDeployments *RepositoryRulesetRequiredDeploymentsRule `json:"required_deployments,omitempty"` - RequiredSignatures *RepositoryRulesetRuleType `json:"required_signatures,omitempty"` - PullRequest *RepositoryRulesetPullRequestRule `json:"pull_request,omitempty"` - RequiredStatusChecks *RepositoryRulesetRequiredStatusChecksRule `json:"required_status_checks,omitempty"` - NonFastForward *RepositoryRulesetRuleType `json:"non_fast_forward,omitempty"` - CommitMessagePattern *RepositoryRulesetPatternRule `json:"commit_message_pattern,omitempty"` - CommitAuthorEmailPattern *RepositoryRulesetPatternRule `json:"commit_author_email_pattern,omitempty"` - CommitterEmailPattern *RepositoryRulesetPatternRule `json:"committer_email_pattern,omitempty"` - BranchNamePattern *RepositoryRulesetPatternRule `json:"branch_name_pattern,omitempty"` - TagNamePattern *RepositoryRulesetPatternRule `json:"tag_name_pattern,omitempty"` - FilePathRestriction *RepositoryRulesetFilePathRestrictionRule `json:"file_path_restriction,omitempty"` - MaxFilePathLength *RepositoryRulesetMaxFilePathLengthRule `json:"max_file_path_length,omitempty"` - FileExtensionRestriction *RepositoryRulesetFileExtensionRestrictionRule `json:"file_extension_restriction,omitempty"` - MaxFileSize *RepositoryRulesetMaxFileSizeRule `json:"max_file_size,omitempty"` - Workflows *RepositoryRulesetWorkflowsRule `json:"workflows,omitempty"` - CodeScanning *RepositoryRulesetCodeScanningRule `json:"code_scanning,omitempty"` -} - -// RepositoryRulesetLink represents Links associated with a repository's rulesets. These links are used to provide more information about the ruleset. -type RepositoryRulesetLink struct { - Self *RulesetLink `json:"self,omitempty"` - HTML *RulesetLink `json:"html,omitempty"` -} - -// RepositoryRulesetRuleType represents the type of a ruleset rule. -type RepositoryRulesetRuleType struct { - Type string `json:"type"` -} - -// RepositoryRulesetUpdateRule defines an update rule for the repository. -type RepositoryRulesetUpdateRule struct { - // Type can be one of: "update". - Type string `json:"type"` - Parameters *UpdateAllowsFetchAndMergeRuleParameters `json:"parameters,omitempty"` -} - -// RepositoryRulesetMergeQueueRule defines a merge queue rule for the repository. -type RepositoryRulesetMergeQueueRule struct { - // Type can be one of: "merge_queue". - Type string `json:"type"` - Parameters *MergeQueueRuleParameters `json:"parameters,omitempty"` -} - -// RepositoryRulesetRequiredDeploymentsRule defines a rule for required deployments. -type RepositoryRulesetRequiredDeploymentsRule struct { - // Type can be one of: "required_deployments". - Type string `json:"type"` - Parameters *RequiredDeploymentEnvironmentsRuleParameters `json:"parameters,omitempty"` -} - -// RepositoryRulesetPullRequestRule defines a rule for pull requests. -type RepositoryRulesetPullRequestRule struct { - // Type can be one of: "pull_request". - - Type string `json:"type"` - Parameters *PullRequestRuleParameters `json:"parameters,omitempty"` -} - -// RepositoryRulesetRequiredStatusChecksRule defines a rule for required status checks. -type RepositoryRulesetRequiredStatusChecksRule struct { - // Type can be one of: "required_status_checks". - - Type string `json:"type"` - Parameters *RequiredStatusChecksRuleParameters `json:"parameters,omitempty"` -} - -// RepositoryRulesetPatternRule defines a pattern rule for the repository. -type RepositoryRulesetPatternRule struct { - Type string `json:"type"` - Parameters *RulePatternParameters `json:"parameters,omitempty"` -} - -// RepositoryRulesetFilePathRestrictionRule defines a file path restriction rule for the repository. -type RepositoryRulesetFilePathRestrictionRule struct { - // Type can be one of: "file_path_restriction". - Type string `json:"type"` - Parameters *RuleFileParameters `json:"parameters,omitempty"` -} - -// RepositoryRulesetMaxFilePathLengthRule defines a maximum file path length rule for the repository. -type RepositoryRulesetMaxFilePathLengthRule struct { - // Type can be one of: "max_file_path_length". - - Type string `json:"type"` - Parameters *RuleMaxFilePathLengthParameters `json:"parameters,omitempty"` -} - -// RepositoryRulesetFileExtensionRestrictionRule defines a file extension restriction rule for the repository. -type RepositoryRulesetFileExtensionRestrictionRule struct { - // Type can be one of: "file_extension_restriction". - Type string `json:"type"` - Parameters *RuleFileExtensionRestrictionParameters `json:"parameters,omitempty"` -} - -// RepositoryRulesetMaxFileSizeRule defines a maximum file size rule for the repository. -type RepositoryRulesetMaxFileSizeRule struct { - // Type can be one of: "max_file_size". - Type string `json:"type"` - Parameters *RuleMaxFileSizeParameters `json:"parameters,omitempty"` -} - -// RepositoryRulesetWorkflowsRule defines a workflow rule for the repository. -type RepositoryRulesetWorkflowsRule struct { - // Type can be one of: "workflows". - Type string `json:"type"` - Parameters *RequiredWorkflowsRuleParameters `json:"parameters,omitempty"` -} - -// RepositoryRulesetCodeScanningRule defines a code scanning rule for the repository. -type RepositoryRulesetCodeScanningRule struct { - // Type can be one of: "code_scanning". - Type string `json:"type"` - Parameters *RuleCodeScanningParameters `json:"parameters,omitempty"` -} - -// RuleCodeScanningParameters defines parameters for code scanning rules. -type RuleCodeScanningParameters struct { - CodeScanningTools []*CodeScanningTool `json:"code_scanning_tools,omitempty"` -} - -// CodeScanningTool defines a specific tool used for code scanning. -type CodeScanningTool struct { - AlertsThreshold string `json:"alerts_threshold"` - SecurityAlertsThreshold string `json:"security_alerts_threshold"` - Tool string `json:"tool"` -} - -// UnmarshalJSON implements the json.Unmarshaler interface. -// This helps us handle the fact that RepositoryRule parameter field can be of numerous types. -func (r *RepositoryRule) UnmarshalJSON(data []byte) error { - type rule RepositoryRule - var repositoryRule rule - if err := json.Unmarshal(data, &repositoryRule); err != nil { - return err - } - - r.RulesetID = repositoryRule.RulesetID - r.RulesetSourceType = repositoryRule.RulesetSourceType - r.RulesetSource = repositoryRule.RulesetSource - r.Type = repositoryRule.Type - - switch repositoryRule.Type { - case "creation", "deletion", "non_fast_forward", "required_linear_history", "required_signatures": - r.Parameters = nil - case "update": - if repositoryRule.Parameters == nil { - r.Parameters = nil - return nil - } - params := UpdateAllowsFetchAndMergeRuleParameters{} - if err := json.Unmarshal(*repositoryRule.Parameters, ¶ms); err != nil { - return err - } - - bytes, _ := json.Marshal(params) - rawParams := json.RawMessage(bytes) - - r.Parameters = &rawParams - case "merge_queue": - if repositoryRule.Parameters == nil { - r.Parameters = nil - return nil - } - params := MergeQueueRuleParameters{} - if err := json.Unmarshal(*repositoryRule.Parameters, ¶ms); err != nil { - return err - } - - bytes, _ := json.Marshal(params) - rawParams := json.RawMessage(bytes) - - r.Parameters = &rawParams - case "required_deployments": - params := RequiredDeploymentEnvironmentsRuleParameters{} - if err := json.Unmarshal(*repositoryRule.Parameters, ¶ms); err != nil { - return err - } - - bytes, _ := json.Marshal(params) - rawParams := json.RawMessage(bytes) - - r.Parameters = &rawParams - case "commit_message_pattern", "commit_author_email_pattern", "committer_email_pattern", "branch_name_pattern", "tag_name_pattern": - params := RulePatternParameters{} - if err := json.Unmarshal(*repositoryRule.Parameters, ¶ms); err != nil { - return err - } - - bytes, _ := json.Marshal(params) - rawParams := json.RawMessage(bytes) - - r.Parameters = &rawParams - case "pull_request": - params := PullRequestRuleParameters{} - if err := json.Unmarshal(*repositoryRule.Parameters, ¶ms); err != nil { - return err - } - - bytes, _ := json.Marshal(params) - rawParams := json.RawMessage(bytes) - - r.Parameters = &rawParams - case "required_status_checks": - params := RequiredStatusChecksRuleParameters{} - if err := json.Unmarshal(*repositoryRule.Parameters, ¶ms); err != nil { - return err - } - - bytes, _ := json.Marshal(params) - rawParams := json.RawMessage(bytes) - - r.Parameters = &rawParams - case "workflows": - params := RequiredWorkflowsRuleParameters{} - if err := json.Unmarshal(*repositoryRule.Parameters, ¶ms); err != nil { - return err - } - - bytes, _ := json.Marshal(params) - rawParams := json.RawMessage(bytes) - - r.Parameters = &rawParams - case "file_path_restriction": - params := RuleFileParameters{} - if err := json.Unmarshal(*repositoryRule.Parameters, ¶ms); err != nil { - return err - } - bytes, _ := json.Marshal(params) - rawParams := json.RawMessage(bytes) - - r.Parameters = &rawParams - case "code_scanning": - params := RequiredCodeScanningRuleParameters{} - if err := json.Unmarshal(*repositoryRule.Parameters, ¶ms); err != nil { - return err - } - bytes, _ := json.Marshal(params) - rawParams := json.RawMessage(bytes) - - r.Parameters = &rawParams - case "max_file_path_length": - params := RuleMaxFilePathLengthParameters{} - if err := json.Unmarshal(*repositoryRule.Parameters, ¶ms); err != nil { - return err - } - bytes, _ := json.Marshal(params) - rawParams := json.RawMessage(bytes) - - r.Parameters = &rawParams - case "file_extension_restriction": - params := RuleFileExtensionRestrictionParameters{} - if err := json.Unmarshal(*repositoryRule.Parameters, ¶ms); err != nil { - return err - } - bytes, _ := json.Marshal(params) - rawParams := json.RawMessage(bytes) - - r.Parameters = &rawParams - case "max_file_size": - params := RuleMaxFileSizeParameters{} - if err := json.Unmarshal(*repositoryRule.Parameters, ¶ms); err != nil { - return err - } - bytes, _ := json.Marshal(params) - rawParams := json.RawMessage(bytes) - - r.Parameters = &rawParams - default: - r.Type = "" - r.Parameters = nil - return fmt.Errorf("RepositoryRule.Type %q is not yet implemented, unable to unmarshal (%#v)", repositoryRule.Type, repositoryRule) - } - - return nil -} - -// NewMergeQueueRule creates a rule to only allow merges via a merge queue. -func NewMergeQueueRule(params *MergeQueueRuleParameters) (rule *RepositoryRule) { - if params != nil { - bytes, _ := json.Marshal(params) - - rawParams := json.RawMessage(bytes) - - return &RepositoryRule{ - Type: "merge_queue", - Parameters: &rawParams, - } - } - return &RepositoryRule{ - Type: "merge_queue", - } -} - -// NewCreationRule creates a rule to only allow users with bypass permission to create matching refs. -func NewCreationRule() (rule *RepositoryRule) { - return &RepositoryRule{ - Type: "creation", - } -} - -// NewUpdateRule creates a rule to only allow users with bypass permission to update matching refs. -func NewUpdateRule(params *UpdateAllowsFetchAndMergeRuleParameters) (rule *RepositoryRule) { - if params != nil { - bytes, _ := json.Marshal(params) - - rawParams := json.RawMessage(bytes) - - return &RepositoryRule{ - Type: "update", - Parameters: &rawParams, - } - } - return &RepositoryRule{ - Type: "update", - } -} - -// NewDeletionRule creates a rule to only allow users with bypass permissions to delete matching refs. -func NewDeletionRule() (rule *RepositoryRule) { - return &RepositoryRule{ - Type: "deletion", - } -} - -// NewRequiredLinearHistoryRule creates a rule to prevent merge commits from being pushed to matching branches. -func NewRequiredLinearHistoryRule() (rule *RepositoryRule) { - return &RepositoryRule{ - Type: "required_linear_history", - } -} - -// NewRequiredDeploymentsRule creates a rule to require environments to be successfully deployed before they can be merged into the matching branches. -func NewRequiredDeploymentsRule(params *RequiredDeploymentEnvironmentsRuleParameters) (rule *RepositoryRule) { - bytes, _ := json.Marshal(params) - - rawParams := json.RawMessage(bytes) - - return &RepositoryRule{ - Type: "required_deployments", - Parameters: &rawParams, - } -} - -// NewRequiredSignaturesRule creates a rule a to require commits pushed to matching branches to have verified signatures. -func NewRequiredSignaturesRule() (rule *RepositoryRule) { - return &RepositoryRule{ - Type: "required_signatures", - } -} - -// NewPullRequestRule creates a rule to require all commits be made to a non-target branch and submitted via a pull request before they can be merged. -func NewPullRequestRule(params *PullRequestRuleParameters) (rule *RepositoryRule) { - bytes, _ := json.Marshal(params) - - rawParams := json.RawMessage(bytes) - - return &RepositoryRule{ - Type: "pull_request", - Parameters: &rawParams, - } -} - -// NewRequiredStatusChecksRule creates a rule to require which status checks must pass before branches can be merged into a branch rule. -func NewRequiredStatusChecksRule(params *RequiredStatusChecksRuleParameters) (rule *RepositoryRule) { - bytes, _ := json.Marshal(params) - - rawParams := json.RawMessage(bytes) - - return &RepositoryRule{ - Type: "required_status_checks", - Parameters: &rawParams, - } -} - -// NewNonFastForwardRule creates a rule as part to prevent users with push access from force pushing to matching branches. -func NewNonFastForwardRule() (rule *RepositoryRule) { - return &RepositoryRule{ - Type: "non_fast_forward", - } -} - -// NewCommitMessagePatternRule creates a rule to restrict commit message patterns being pushed to matching branches. -func NewCommitMessagePatternRule(params *RulePatternParameters) (rule *RepositoryRule) { - bytes, _ := json.Marshal(params) - - rawParams := json.RawMessage(bytes) - - return &RepositoryRule{ - Type: "commit_message_pattern", - Parameters: &rawParams, - } -} - -// NewCommitAuthorEmailPatternRule creates a rule to restrict commits with author email patterns being merged into matching branches. -func NewCommitAuthorEmailPatternRule(params *RulePatternParameters) (rule *RepositoryRule) { - bytes, _ := json.Marshal(params) - - rawParams := json.RawMessage(bytes) - - return &RepositoryRule{ - Type: "commit_author_email_pattern", - Parameters: &rawParams, - } -} - -// NewCommitterEmailPatternRule creates a rule to restrict commits with committer email patterns being merged into matching branches. -func NewCommitterEmailPatternRule(params *RulePatternParameters) (rule *RepositoryRule) { - bytes, _ := json.Marshal(params) - - rawParams := json.RawMessage(bytes) - - return &RepositoryRule{ - Type: "committer_email_pattern", - Parameters: &rawParams, - } -} - -// NewBranchNamePatternRule creates a rule to restrict branch patterns from being merged into matching branches. -func NewBranchNamePatternRule(params *RulePatternParameters) (rule *RepositoryRule) { - bytes, _ := json.Marshal(params) - - rawParams := json.RawMessage(bytes) - - return &RepositoryRule{ - Type: "branch_name_pattern", - Parameters: &rawParams, - } -} - -// NewTagNamePatternRule creates a rule to restrict tag patterns contained in non-target branches from being merged into matching branches. -func NewTagNamePatternRule(params *RulePatternParameters) (rule *RepositoryRule) { - bytes, _ := json.Marshal(params) - - rawParams := json.RawMessage(bytes) - - return &RepositoryRule{ - Type: "tag_name_pattern", - Parameters: &rawParams, - } -} - -// NewRequiredWorkflowsRule creates a rule to require which status checks must pass before branches can be merged into a branch rule. -func NewRequiredWorkflowsRule(params *RequiredWorkflowsRuleParameters) (rule *RepositoryRule) { - bytes, _ := json.Marshal(params) - - rawParams := json.RawMessage(bytes) - - return &RepositoryRule{ - Type: "workflows", - Parameters: &rawParams, - } -} - -// NewRequiredCodeScanningRule creates a rule to require which tools must provide code scanning results before the reference is updated. -func NewRequiredCodeScanningRule(params *RequiredCodeScanningRuleParameters) (rule *RepositoryRule) { - bytes, _ := json.Marshal(params) - - rawParams := json.RawMessage(bytes) - - return &RepositoryRule{ - Type: "code_scanning", - Parameters: &rawParams, - } -} - -// NewFilePathRestrictionRule creates a rule to restrict file paths from being pushed to. -func NewFilePathRestrictionRule(params *RuleFileParameters) (rule *RepositoryRule) { - bytes, _ := json.Marshal(params) - - rawParams := json.RawMessage(bytes) - - return &RepositoryRule{ - Type: "file_path_restriction", - Parameters: &rawParams, - } -} - -// NewMaxFilePathLengthRule creates a rule to restrict file paths longer than the limit from being pushed. -func NewMaxFilePathLengthRule(params *RuleMaxFilePathLengthParameters) (rule *RepositoryRule) { - bytes, _ := json.Marshal(params) - - rawParams := json.RawMessage(bytes) - - return &RepositoryRule{ - Type: "max_file_path_length", - Parameters: &rawParams, - } -} - -// NewFileExtensionRestrictionRule creates a rule to restrict file extensions from being pushed to a commit. -func NewFileExtensionRestrictionRule(params *RuleFileExtensionRestrictionParameters) (rule *RepositoryRule) { - bytes, _ := json.Marshal(params) - - rawParams := json.RawMessage(bytes) - - return &RepositoryRule{ - Type: "file_extension_restriction", - Parameters: &rawParams, - } -} - -// NewMaxFileSizeRule creates a rule to restrict file sizes from being pushed to a commit. -func NewMaxFileSizeRule(params *RuleMaxFileSizeParameters) (rule *RepositoryRule) { - bytes, _ := json.Marshal(params) - - rawParams := json.RawMessage(bytes) - - return &RepositoryRule{ - Type: "max_file_size", - Parameters: &rawParams, - } -} - -// Ruleset represents a GitHub ruleset object. -type Ruleset struct { - ID *int64 `json:"id,omitempty"` - Name string `json:"name"` - // Possible values for Target are branch, tag, push - Target *string `json:"target,omitempty"` - // Possible values for SourceType are: Repository, Organization - SourceType *string `json:"source_type,omitempty"` - Source string `json:"source"` - // Possible values for Enforcement are: disabled, active, evaluate - Enforcement string `json:"enforcement"` - BypassActors []*BypassActor `json:"bypass_actors,omitempty"` - NodeID *string `json:"node_id,omitempty"` - Links *RulesetLinks `json:"_links,omitempty"` - Conditions *RulesetConditions `json:"conditions,omitempty"` - Rules []*RepositoryRule `json:"rules,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` -} - -// rulesetNoOmitBypassActors represents a GitHub ruleset object. The struct does not omit bypassActors if the field is nil or an empty array is passed. -type rulesetNoOmitBypassActors struct { - ID *int64 `json:"id,omitempty"` - Name string `json:"name"` - // Possible values for Target are branch, tag - Target *string `json:"target,omitempty"` - // Possible values for SourceType are: Repository, Organization - SourceType *string `json:"source_type,omitempty"` - Source string `json:"source"` - // Possible values for Enforcement are: disabled, active, evaluate - Enforcement string `json:"enforcement"` - BypassActors []*BypassActor `json:"bypass_actors"` - NodeID *string `json:"node_id,omitempty"` - Links *RulesetLinks `json:"_links,omitempty"` - Conditions *RulesetConditions `json:"conditions,omitempty"` - Rules []*RepositoryRule `json:"rules,omitempty"` -} - -// GetRulesForBranch gets all the rules that apply to the specified branch. -// -// GitHub API docs: https://docs.github.com/rest/repos/rules#get-rules-for-a-branch -// -//meta:operation GET /repos/{owner}/{repo}/rules/branches/{branch} -func (s *RepositoriesService) GetRulesForBranch(ctx context.Context, owner, repo, branch string) ([]*RepositoryRule, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/rules/branches/%v", owner, repo, branch) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var rules []*RepositoryRule - resp, err := s.client.Do(ctx, req, &rules) - if err != nil { - return nil, resp, err - } - - return rules, resp, nil -} - -// GetAllRulesets gets all the rules that apply to the specified repository. -// If includesParents is true, rulesets configured at the organization level that apply to the repository will be returned. -// -// GitHub API docs: https://docs.github.com/rest/repos/rules#get-all-repository-rulesets -// -//meta:operation GET /repos/{owner}/{repo}/rulesets -func (s *RepositoriesService) GetAllRulesets(ctx context.Context, owner, repo string, includesParents bool) ([]*Ruleset, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/rulesets?includes_parents=%v", owner, repo, includesParents) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var ruleset []*Ruleset - resp, err := s.client.Do(ctx, req, &ruleset) - if err != nil { - return nil, resp, err - } - - return ruleset, resp, nil -} - -// CreateRuleset creates a ruleset for the specified repository. -// -// GitHub API docs: https://docs.github.com/rest/repos/rules#create-a-repository-ruleset -// -//meta:operation POST /repos/{owner}/{repo}/rulesets -func (s *RepositoriesService) CreateRuleset(ctx context.Context, owner, repo string, rs *Ruleset) (*Ruleset, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/rulesets", owner, repo) - - req, err := s.client.NewRequest("POST", u, rs) - if err != nil { - return nil, nil, err - } - - var ruleset *Ruleset - resp, err := s.client.Do(ctx, req, &ruleset) - if err != nil { - return nil, resp, err - } - - return ruleset, resp, nil -} - -// GetRuleset gets a ruleset for the specified repository. -// If includesParents is true, rulesets configured at the organization level that apply to the repository will be returned. -// -// GitHub API docs: https://docs.github.com/rest/repos/rules#get-a-repository-ruleset -// -//meta:operation GET /repos/{owner}/{repo}/rulesets/{ruleset_id} -func (s *RepositoriesService) GetRuleset(ctx context.Context, owner, repo string, rulesetID int64, includesParents bool) (*Ruleset, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/rulesets/%v?includes_parents=%v", owner, repo, rulesetID, includesParents) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var ruleset *Ruleset - resp, err := s.client.Do(ctx, req, &ruleset) - if err != nil { - return nil, resp, err - } - - return ruleset, resp, nil -} - -// UpdateRuleset updates a ruleset for the specified repository. -// -// GitHub API docs: https://docs.github.com/rest/repos/rules#update-a-repository-ruleset -// -//meta:operation PUT /repos/{owner}/{repo}/rulesets/{ruleset_id} -func (s *RepositoriesService) UpdateRuleset(ctx context.Context, owner, repo string, rulesetID int64, rs *Ruleset) (*Ruleset, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/rulesets/%v", owner, repo, rulesetID) - - req, err := s.client.NewRequest("PUT", u, rs) - if err != nil { - return nil, nil, err - } - - var ruleset *Ruleset - resp, err := s.client.Do(ctx, req, &ruleset) - if err != nil { - return nil, resp, err - } - - return ruleset, resp, nil -} - -// UpdateRulesetNoBypassActor updates a ruleset for the specified repository. -// -// This function is necessary as the UpdateRuleset function does not marshal ByPassActor if passed as nil or an empty array. -// -// GitHub API docs: https://docs.github.com/rest/repos/rules#update-a-repository-ruleset -// -//meta:operation PUT /repos/{owner}/{repo}/rulesets/{ruleset_id} -func (s *RepositoriesService) UpdateRulesetNoBypassActor(ctx context.Context, owner, repo string, rulesetID int64, rs *Ruleset) (*Ruleset, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/rulesets/%v", owner, repo, rulesetID) - - rsNoBypassActor := &rulesetNoOmitBypassActors{} - - if rs != nil { - rsNoBypassActor = &rulesetNoOmitBypassActors{ - ID: rs.ID, - Name: rs.Name, - Target: rs.Target, - SourceType: rs.SourceType, - Source: rs.Source, - Enforcement: rs.Enforcement, - BypassActors: rs.BypassActors, - NodeID: rs.NodeID, - Links: rs.Links, - Conditions: rs.Conditions, - Rules: rs.Rules, - } - } - - req, err := s.client.NewRequest("PUT", u, rsNoBypassActor) - if err != nil { - return nil, nil, err - } - - var ruleSet *Ruleset - resp, err := s.client.Do(ctx, req, &ruleSet) - if err != nil { - return nil, resp, err - } - - return ruleSet, resp, nil -} - -// DeleteRuleset deletes a ruleset for the specified repository. -// -// GitHub API docs: https://docs.github.com/rest/repos/rules#delete-a-repository-ruleset -// -//meta:operation DELETE /repos/{owner}/{repo}/rulesets/{ruleset_id} -func (s *RepositoriesService) DeleteRuleset(ctx context.Context, owner, repo string, rulesetID int64) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/rulesets/%v", owner, repo, rulesetID) - - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/v68/AUTHORS b/vendor/github.com/google/go-github/v70/AUTHORS similarity index 100% rename from vendor/github.com/google/go-github/v68/AUTHORS rename to vendor/github.com/google/go-github/v70/AUTHORS diff --git a/vendor/github.com/google/go-github/v68/LICENSE b/vendor/github.com/google/go-github/v70/LICENSE similarity index 100% rename from vendor/github.com/google/go-github/v68/LICENSE rename to vendor/github.com/google/go-github/v70/LICENSE diff --git a/vendor/github.com/google/go-github/v68/github/actions.go b/vendor/github.com/google/go-github/v70/github/actions.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/actions.go rename to vendor/github.com/google/go-github/v70/github/actions.go diff --git a/vendor/github.com/google/go-github/v68/github/actions_artifacts.go b/vendor/github.com/google/go-github/v70/github/actions_artifacts.go similarity index 87% rename from vendor/github.com/google/go-github/v68/github/actions_artifacts.go rename to vendor/github.com/google/go-github/v70/github/actions_artifacts.go index e05a9a8402..2b560fa05d 100644 --- a/vendor/github.com/google/go-github/v68/github/actions_artifacts.go +++ b/vendor/github.com/google/go-github/v70/github/actions_artifacts.go @@ -142,6 +142,14 @@ func (s *ActionsService) GetArtifact(ctx context.Context, owner, repo string, ar func (s *ActionsService) DownloadArtifact(ctx context.Context, owner, repo string, artifactID int64, maxRedirects int) (*url.URL, *Response, error) { u := fmt.Sprintf("repos/%v/%v/actions/artifacts/%v/zip", owner, repo, artifactID) + if s.client.RateLimitRedirectionalEndpoints { + return s.downloadArtifactWithRateLimit(ctx, u, maxRedirects) + } + + return s.downloadArtifactWithoutRateLimit(ctx, u, maxRedirects) +} + +func (s *ActionsService) downloadArtifactWithoutRateLimit(ctx context.Context, u string, maxRedirects int) (*url.URL, *Response, error) { resp, err := s.client.roundTripWithOptionalFollowRedirect(ctx, u, maxRedirects) if err != nil { return nil, nil, err @@ -149,7 +157,7 @@ func (s *ActionsService) DownloadArtifact(ctx context.Context, owner, repo strin defer resp.Body.Close() if resp.StatusCode != http.StatusFound { - return nil, newResponse(resp), fmt.Errorf("unexpected status code: %s", resp.Status) + return nil, newResponse(resp), fmt.Errorf("unexpected status code: %v", resp.Status) } parsedURL, err := url.Parse(resp.Header.Get("Location")) @@ -160,6 +168,26 @@ func (s *ActionsService) DownloadArtifact(ctx context.Context, owner, repo strin return parsedURL, newResponse(resp), nil } +func (s *ActionsService) downloadArtifactWithRateLimit(ctx context.Context, u string, maxRedirects int) (*url.URL, *Response, error) { + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + url, resp, err := s.client.bareDoUntilFound(ctx, req, maxRedirects) + if err != nil { + return nil, resp, err + } + defer resp.Body.Close() + + // If we didn't receive a valid Location in a 302 response + if url == nil { + return nil, resp, fmt.Errorf("unexpected status code: %v", resp.Status) + } + + return url, resp, nil +} + // DeleteArtifact deletes a workflow run artifact. // // GitHub API docs: https://docs.github.com/rest/actions/artifacts#delete-an-artifact diff --git a/vendor/github.com/google/go-github/v68/github/actions_cache.go b/vendor/github.com/google/go-github/v70/github/actions_cache.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/actions_cache.go rename to vendor/github.com/google/go-github/v70/github/actions_cache.go diff --git a/vendor/github.com/google/go-github/v70/github/actions_hosted_runners.go b/vendor/github.com/google/go-github/v70/github/actions_hosted_runners.go new file mode 100644 index 0000000000..dbe1f6b5b1 --- /dev/null +++ b/vendor/github.com/google/go-github/v70/github/actions_hosted_runners.go @@ -0,0 +1,376 @@ +// Copyright 2025 The go-github 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 github + +import ( + "context" + "errors" + "fmt" +) + +// HostedRunnerPublicIP represents the details of a public IP for GitHub-hosted runner. +type HostedRunnerPublicIP struct { + Enabled bool `json:"enabled"` // Whether public IP is enabled. + Prefix string `json:"prefix"` // The prefix for the public IP. Example: 20.80.208.150 + Length int `json:"length"` // The length of the IP prefix. Example: 28 +} + +// HostedRunnerMachineSpec represents the details of a particular machine specification for GitHub-hosted runner. +type HostedRunnerMachineSpec struct { + ID string `json:"id"` // The ID used for the `size` parameter when creating a new runner. Example: 8-core + CPUCores int `json:"cpu_cores"` // The number of cores. Example: 8 + MemoryGB int `json:"memory_gb"` // The available RAM for the machine spec. Example: 32 + StorageGB int `json:"storage_gb"` // The available SSD storage for the machine spec. Example: 300 +} + +// HostedRunner represents a single GitHub-hosted runner with additional details. +type HostedRunner struct { + ID *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + RunnerGroupID *int64 `json:"runner_group_id,omitempty"` + Platform *string `json:"platform,omitempty"` + ImageDetails *HostedRunnerImageDetail `json:"image_details,omitempty"` + MachineSizeDetails *HostedRunnerMachineSpec `json:"machine_size_details,omitempty"` + Status *string `json:"status,omitempty"` + MaximumRunners *int64 `json:"maximum_runners,omitempty"` + PublicIPEnabled *bool `json:"public_ip_enabled,omitempty"` + PublicIPs []*HostedRunnerPublicIP `json:"public_ips,omitempty"` + LastActiveOn *Timestamp `json:"last_active_on,omitempty"` +} + +// HostedRunnerImageDetail represents the image details of a GitHub-hosted runners. +type HostedRunnerImageDetail struct { + ID *string `json:"id"` // The ID of the image. Use this ID for the `image` parameter when creating a new larger runner. Example: ubuntu-20.04 + SizeGB *int64 `json:"size_gb"` // Image size in GB. Example: 86 + DisplayName *string `json:"display_name"` // Display name for this image. Example: 20.04 + Source *string `json:"source"` // The image provider. Example: github, partner, custom + Version *string `json:"version"` // The image version of the hosted runner pool. Example: latest +} + +// HostedRunners represents a collection of GitHub-hosted runners for an organization. +type HostedRunners struct { + TotalCount int `json:"total_count"` + Runners []*HostedRunner `json:"runners"` +} + +// ListHostedRunners lists all the GitHub-hosted runners for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/hosted-runners#list-github-hosted-runners-for-an-organization +// +//meta:operation GET /orgs/{org}/actions/hosted-runners +func (s *ActionsService) ListHostedRunners(ctx context.Context, org string, opts *ListOptions) (*HostedRunners, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/hosted-runners", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + runners := &HostedRunners{} + resp, err := s.client.Do(ctx, req, &runners) + if err != nil { + return nil, resp, err + } + + return runners, resp, nil +} + +// HostedRunnerImage represents the image of GitHub-hosted runners. +// To list all available images, use GET /actions/hosted-runners/images/github-owned or GET /actions/hosted-runners/images/partner. +type HostedRunnerImage struct { + ID string `json:"id"` + Source string `json:"source"` + Version string `json:"version"` +} + +// HostedRunnerRequest specifies body parameters to Hosted Runner configuration. +type HostedRunnerRequest struct { + Name string `json:"name,omitempty"` + Image HostedRunnerImage `json:"image,omitempty"` + RunnerGroupID int64 `json:"runner_group_id,omitempty"` + Size string `json:"size,omitempty"` + MaximumRunners int64 `json:"maximum_runners,omitempty"` + EnableStaticIP bool `json:"enable_static_ip,omitempty"` + ImageVersion string `json:"image_version,omitempty"` +} + +// validateCreateHostedRunnerRequest validates the provided HostedRunnerRequest to ensure +// that all required fields are properly set and that no invalid fields are present for hosted runner create request. +// +// If any of these conditions are violated, an appropriate error message is returned. +// Otherwise, nil is returned, indicating the request is valid. +func validateCreateHostedRunnerRequest(request *HostedRunnerRequest) error { + if request.Size == "" { + return errors.New("size is required for creating a hosted runner") + } + if request.Image == (HostedRunnerImage{}) { + return errors.New("image is required for creating a hosted runner") + } + if request.Name == "" { + return errors.New("name is required for creating a hosted runner") + } + if request.RunnerGroupID == 0 { + return errors.New("runner group ID is required for creating a hosted runner") + } + if request.ImageVersion != "" { + return errors.New("imageVersion should not be set directly; use the Image struct to specify image details") + } + return nil +} + +// CreateHostedRunner creates a GitHub-hosted runner for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/hosted-runners#create-a-github-hosted-runner-for-an-organization +// +//meta:operation POST /orgs/{org}/actions/hosted-runners +func (s *ActionsService) CreateHostedRunner(ctx context.Context, org string, request *HostedRunnerRequest) (*HostedRunner, *Response, error) { + if err := validateCreateHostedRunnerRequest(request); err != nil { + return nil, nil, fmt.Errorf("validation failed: %w", err) + } + + u := fmt.Sprintf("orgs/%v/actions/hosted-runners", org) + req, err := s.client.NewRequest("POST", u, request) + if err != nil { + return nil, nil, err + } + + hostedRunner := new(HostedRunner) + resp, err := s.client.Do(ctx, req, hostedRunner) + if err != nil { + return nil, resp, err + } + + return hostedRunner, resp, nil +} + +// HostedRunnerImageSpecs represents the details of a GitHub-hosted runner image. +type HostedRunnerImageSpecs struct { + ID string `json:"id"` + Platform string `json:"platform"` + SizeGB int `json:"size_gb"` + DisplayName string `json:"display_name"` + Source string `json:"source"` +} + +// HostedRunnerImages represents the response containing the total count and details of runner images. +type HostedRunnerImages struct { + TotalCount int `json:"total_count"` + Images []*HostedRunnerImageSpecs `json:"images"` +} + +// GetHostedRunnerGitHubOwnedImages gets the list of GitHub-owned images available for GitHub-hosted runners for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/hosted-runners#get-github-owned-images-for-github-hosted-runners-in-an-organization +// +//meta:operation GET /orgs/{org}/actions/hosted-runners/images/github-owned +func (s *ActionsService) GetHostedRunnerGitHubOwnedImages(ctx context.Context, org string) (*HostedRunnerImages, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/hosted-runners/images/github-owned", org) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + hostedRunnerImages := new(HostedRunnerImages) + resp, err := s.client.Do(ctx, req, hostedRunnerImages) + if err != nil { + return nil, resp, err + } + + return hostedRunnerImages, resp, nil +} + +// GetHostedRunnerPartnerImages gets the list of partner images available for GitHub-hosted runners for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/hosted-runners#get-partner-images-for-github-hosted-runners-in-an-organization +// +//meta:operation GET /orgs/{org}/actions/hosted-runners/images/partner +func (s *ActionsService) GetHostedRunnerPartnerImages(ctx context.Context, org string) (*HostedRunnerImages, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/hosted-runners/images/partner", org) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + hostedRunnerImages := new(HostedRunnerImages) + resp, err := s.client.Do(ctx, req, hostedRunnerImages) + if err != nil { + return nil, resp, err + } + + return hostedRunnerImages, resp, nil +} + +// HostedRunnerPublicIPLimits represents the static public IP limits for GitHub-hosted runners. +type HostedRunnerPublicIPLimits struct { + PublicIPs *PublicIPUsage `json:"public_ips"` +} + +// PublicIPUsage provides details of static public IP limits for GitHub-hosted runners. +type PublicIPUsage struct { + Maximum int64 `json:"maximum"` // The maximum number of static public IP addresses that can be used for Hosted Runners. Example: 50 + CurrentUsage int64 `json:"current_usage"` // The current number of static public IP addresses in use by Hosted Runners. Example: 17 +} + +// GetHostedRunnerLimits gets the GitHub-hosted runners Static public IP Limits for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/hosted-runners#get-limits-on-github-hosted-runners-for-an-organization +// +//meta:operation GET /orgs/{org}/actions/hosted-runners/limits +func (s *ActionsService) GetHostedRunnerLimits(ctx context.Context, org string) (*HostedRunnerPublicIPLimits, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/hosted-runners/limits", org) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + publicIPLimits := new(HostedRunnerPublicIPLimits) + resp, err := s.client.Do(ctx, req, publicIPLimits) + if err != nil { + return nil, resp, err + } + + return publicIPLimits, resp, nil +} + +// HostedRunnerMachineSpecs represents the response containing the total count and details of machine specs for GitHub-hosted runners. +type HostedRunnerMachineSpecs struct { + TotalCount int `json:"total_count"` + MachineSpecs []*HostedRunnerMachineSpec `json:"machine_specs"` +} + +// GetHostedRunnerMachineSpecs gets the list of machine specs available for GitHub-hosted runners for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/hosted-runners#get-github-hosted-runners-machine-specs-for-an-organization +// +//meta:operation GET /orgs/{org}/actions/hosted-runners/machine-sizes +func (s *ActionsService) GetHostedRunnerMachineSpecs(ctx context.Context, org string) (*HostedRunnerMachineSpecs, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/hosted-runners/machine-sizes", org) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + machineSpecs := new(HostedRunnerMachineSpecs) + resp, err := s.client.Do(ctx, req, machineSpecs) + if err != nil { + return nil, resp, err + } + + return machineSpecs, resp, nil +} + +// HostedRunnerPlatforms represents the response containing the total count and platforms for GitHub-hosted runners. +type HostedRunnerPlatforms struct { + TotalCount int `json:"total_count"` + Platforms []string `json:"platforms"` +} + +// GetHostedRunnerPlatforms gets list of platforms available for GitHub-hosted runners for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/hosted-runners#get-platforms-for-github-hosted-runners-in-an-organization +// +//meta:operation GET /orgs/{org}/actions/hosted-runners/platforms +func (s *ActionsService) GetHostedRunnerPlatforms(ctx context.Context, org string) (*HostedRunnerPlatforms, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/hosted-runners/platforms", org) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + platforms := new(HostedRunnerPlatforms) + resp, err := s.client.Do(ctx, req, platforms) + if err != nil { + return nil, resp, err + } + + return platforms, resp, nil +} + +// GetHostedRunner gets a GitHub-hosted runner in an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/hosted-runners#get-a-github-hosted-runner-for-an-organization +// +//meta:operation GET /orgs/{org}/actions/hosted-runners/{hosted_runner_id} +func (s *ActionsService) GetHostedRunner(ctx context.Context, org string, runnerID int64) (*HostedRunner, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/hosted-runners/%v", org, runnerID) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + hostedRunner := new(HostedRunner) + resp, err := s.client.Do(ctx, req, hostedRunner) + if err != nil { + return nil, resp, err + } + + return hostedRunner, resp, nil +} + +// validateUpdateHostedRunnerRequest validates the provided HostedRunnerRequest to ensure +// that no disallowed updates are made for a hosted runner update request. +// +// If any of these conditions are violated, an appropriate error message is returned. +// Otherwise, nil is returned, indicating the request is valid for an update. +func validateUpdateHostedRunnerRequest(request *HostedRunnerRequest) error { + if request.Size != "" { + return errors.New("size cannot be updated, API does not support updating size") + } + if request.Image != (HostedRunnerImage{}) { + return errors.New("image struct should not be set directly; use the ImageVersion to specify version details") + } + return nil +} + +// UpdateHostedRunner updates a GitHub-hosted runner for an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/hosted-runners#update-a-github-hosted-runner-for-an-organization +// +//meta:operation PATCH /orgs/{org}/actions/hosted-runners/{hosted_runner_id} +func (s *ActionsService) UpdateHostedRunner(ctx context.Context, org string, runnerID int64, updateReq HostedRunnerRequest) (*HostedRunner, *Response, error) { + if err := validateUpdateHostedRunnerRequest(&updateReq); err != nil { + return nil, nil, fmt.Errorf("validation failed: %w", err) + } + + u := fmt.Sprintf("orgs/%v/actions/hosted-runners/%v", org, runnerID) + req, err := s.client.NewRequest("PATCH", u, updateReq) + if err != nil { + return nil, nil, err + } + + hostedRunner := new(HostedRunner) + resp, err := s.client.Do(ctx, req, hostedRunner) + if err != nil { + return nil, resp, err + } + + return hostedRunner, resp, nil +} + +// DeleteHostedRunner deletes GitHub-hosted runner from an organization. +// +// GitHub API docs: https://docs.github.com/rest/actions/hosted-runners#delete-a-github-hosted-runner-for-an-organization +// +//meta:operation DELETE /orgs/{org}/actions/hosted-runners/{hosted_runner_id} +func (s *ActionsService) DeleteHostedRunner(ctx context.Context, org string, runnerID int64) (*HostedRunner, *Response, error) { + u := fmt.Sprintf("orgs/%v/actions/hosted-runners/%v", org, runnerID) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, nil, err + } + + hostedRunner := new(HostedRunner) + resp, err := s.client.Do(ctx, req, hostedRunner) + if err != nil { + return nil, resp, err + } + + return hostedRunner, resp, nil +} diff --git a/vendor/github.com/google/go-github/v68/github/actions_oidc.go b/vendor/github.com/google/go-github/v70/github/actions_oidc.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/actions_oidc.go rename to vendor/github.com/google/go-github/v70/github/actions_oidc.go diff --git a/vendor/github.com/google/go-github/v68/github/actions_permissions_enterprise.go b/vendor/github.com/google/go-github/v70/github/actions_permissions_enterprise.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/actions_permissions_enterprise.go rename to vendor/github.com/google/go-github/v70/github/actions_permissions_enterprise.go diff --git a/vendor/github.com/google/go-github/v68/github/actions_permissions_orgs.go b/vendor/github.com/google/go-github/v70/github/actions_permissions_orgs.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/actions_permissions_orgs.go rename to vendor/github.com/google/go-github/v70/github/actions_permissions_orgs.go diff --git a/vendor/github.com/google/go-github/v68/github/actions_required_workflows.go b/vendor/github.com/google/go-github/v70/github/actions_required_workflows.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/actions_required_workflows.go rename to vendor/github.com/google/go-github/v70/github/actions_required_workflows.go diff --git a/vendor/github.com/google/go-github/v68/github/actions_runner_groups.go b/vendor/github.com/google/go-github/v70/github/actions_runner_groups.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/actions_runner_groups.go rename to vendor/github.com/google/go-github/v70/github/actions_runner_groups.go diff --git a/vendor/github.com/google/go-github/v68/github/actions_runners.go b/vendor/github.com/google/go-github/v70/github/actions_runners.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/actions_runners.go rename to vendor/github.com/google/go-github/v70/github/actions_runners.go diff --git a/vendor/github.com/google/go-github/v68/github/actions_secrets.go b/vendor/github.com/google/go-github/v70/github/actions_secrets.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/actions_secrets.go rename to vendor/github.com/google/go-github/v70/github/actions_secrets.go diff --git a/vendor/github.com/google/go-github/v68/github/actions_variables.go b/vendor/github.com/google/go-github/v70/github/actions_variables.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/actions_variables.go rename to vendor/github.com/google/go-github/v70/github/actions_variables.go diff --git a/vendor/github.com/google/go-github/v68/github/actions_workflow_jobs.go b/vendor/github.com/google/go-github/v70/github/actions_workflow_jobs.go similarity index 86% rename from vendor/github.com/google/go-github/v68/github/actions_workflow_jobs.go rename to vendor/github.com/google/go-github/v70/github/actions_workflow_jobs.go index 84bbe5aa46..10067c8b26 100644 --- a/vendor/github.com/google/go-github/v68/github/actions_workflow_jobs.go +++ b/vendor/github.com/google/go-github/v70/github/actions_workflow_jobs.go @@ -150,6 +150,14 @@ func (s *ActionsService) GetWorkflowJobByID(ctx context.Context, owner, repo str func (s *ActionsService) GetWorkflowJobLogs(ctx context.Context, owner, repo string, jobID int64, maxRedirects int) (*url.URL, *Response, error) { u := fmt.Sprintf("repos/%v/%v/actions/jobs/%v/logs", owner, repo, jobID) + if s.client.RateLimitRedirectionalEndpoints { + return s.getWorkflowJobLogsWithRateLimit(ctx, u, maxRedirects) + } + + return s.getWorkflowJobLogsWithoutRateLimit(ctx, u, maxRedirects) +} + +func (s *ActionsService) getWorkflowJobLogsWithoutRateLimit(ctx context.Context, u string, maxRedirects int) (*url.URL, *Response, error) { resp, err := s.client.roundTripWithOptionalFollowRedirect(ctx, u, maxRedirects) if err != nil { return nil, nil, err @@ -157,9 +165,29 @@ func (s *ActionsService) GetWorkflowJobLogs(ctx context.Context, owner, repo str defer resp.Body.Close() if resp.StatusCode != http.StatusFound { - return nil, newResponse(resp), fmt.Errorf("unexpected status code: %s", resp.Status) + return nil, newResponse(resp), fmt.Errorf("unexpected status code: %v", resp.Status) } parsedURL, err := url.Parse(resp.Header.Get("Location")) return parsedURL, newResponse(resp), err } + +func (s *ActionsService) getWorkflowJobLogsWithRateLimit(ctx context.Context, u string, maxRedirects int) (*url.URL, *Response, error) { + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + url, resp, err := s.client.bareDoUntilFound(ctx, req, maxRedirects) + if err != nil { + return nil, resp, err + } + defer resp.Body.Close() + + // If we didn't receive a valid Location in a 302 response + if url == nil { + return nil, resp, fmt.Errorf("unexpected status code: %v", resp.Status) + } + + return url, resp, nil +} diff --git a/vendor/github.com/google/go-github/v68/github/actions_workflow_runs.go b/vendor/github.com/google/go-github/v70/github/actions_workflow_runs.go similarity index 82% rename from vendor/github.com/google/go-github/v68/github/actions_workflow_runs.go rename to vendor/github.com/google/go-github/v70/github/actions_workflow_runs.go index 122ea1d0e2..20b9cfcd50 100644 --- a/vendor/github.com/google/go-github/v68/github/actions_workflow_runs.go +++ b/vendor/github.com/google/go-github/v70/github/actions_workflow_runs.go @@ -204,6 +204,7 @@ func (s *ActionsService) ListRepositoryWorkflowRuns(ctx context.Context, owner, } // GetWorkflowRunByID gets a specific workflow run by ID. +// You can use the helper function *DeploymentProtectionRuleEvent.GetRunID() to easily retrieve the workflow run ID from a DeploymentProtectionRuleEvent. // // GitHub API docs: https://docs.github.com/rest/actions/workflow-runs#get-a-workflow-run // @@ -226,6 +227,7 @@ func (s *ActionsService) GetWorkflowRunByID(ctx context.Context, owner, repo str } // GetWorkflowRunAttempt gets a specific workflow run attempt. +// You can use the helper function *DeploymentProtectionRuleEvent.GetRunID() to easily retrieve the workflow run ID from a DeploymentProtectionRuleEvent. // // GitHub API docs: https://docs.github.com/rest/actions/workflow-runs#get-a-workflow-run-attempt // @@ -252,6 +254,7 @@ func (s *ActionsService) GetWorkflowRunAttempt(ctx context.Context, owner, repo } // GetWorkflowRunAttemptLogs gets a redirect URL to download a plain text file of logs for a workflow run for attempt number. +// You can use the helper function *DeploymentProtectionRuleEvent.GetRunID() to easily retrieve a workflow run ID from the DeploymentProtectionRuleEvent. // // GitHub API docs: https://docs.github.com/rest/actions/workflow-runs#download-workflow-run-attempt-logs // @@ -259,6 +262,14 @@ func (s *ActionsService) GetWorkflowRunAttempt(ctx context.Context, owner, repo func (s *ActionsService) GetWorkflowRunAttemptLogs(ctx context.Context, owner, repo string, runID int64, attemptNumber int, maxRedirects int) (*url.URL, *Response, error) { u := fmt.Sprintf("repos/%v/%v/actions/runs/%v/attempts/%v/logs", owner, repo, runID, attemptNumber) + if s.client.RateLimitRedirectionalEndpoints { + return s.getWorkflowRunAttemptLogsWithRateLimit(ctx, u, maxRedirects) + } + + return s.getWorkflowRunAttemptLogsWithoutRateLimit(ctx, u, maxRedirects) +} + +func (s *ActionsService) getWorkflowRunAttemptLogsWithoutRateLimit(ctx context.Context, u string, maxRedirects int) (*url.URL, *Response, error) { resp, err := s.client.roundTripWithOptionalFollowRedirect(ctx, u, maxRedirects) if err != nil { return nil, nil, err @@ -266,14 +277,35 @@ func (s *ActionsService) GetWorkflowRunAttemptLogs(ctx context.Context, owner, r defer resp.Body.Close() if resp.StatusCode != http.StatusFound { - return nil, newResponse(resp), fmt.Errorf("unexpected status code: %s", resp.Status) + return nil, newResponse(resp), fmt.Errorf("unexpected status code: %v", resp.Status) } parsedURL, err := url.Parse(resp.Header.Get("Location")) return parsedURL, newResponse(resp), err } +func (s *ActionsService) getWorkflowRunAttemptLogsWithRateLimit(ctx context.Context, u string, maxRedirects int) (*url.URL, *Response, error) { + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + url, resp, err := s.client.bareDoUntilFound(ctx, req, maxRedirects) + if err != nil { + return nil, resp, err + } + defer resp.Body.Close() + + // If we didn't receive a valid Location in a 302 response + if url == nil { + return nil, resp, fmt.Errorf("unexpected status code: %v", resp.Status) + } + + return url, resp, nil +} + // RerunWorkflowByID re-runs a workflow by ID. +// You can use the helper function *DeploymentProtectionRuleEvent.GetRunID() to easily retrieve the workflow run ID a the DeploymentProtectionRuleEvent. // // GitHub API docs: https://docs.github.com/rest/actions/workflow-runs#re-run-a-workflow // @@ -290,6 +322,7 @@ func (s *ActionsService) RerunWorkflowByID(ctx context.Context, owner, repo stri } // RerunFailedJobsByID re-runs all of the failed jobs and their dependent jobs in a workflow run by ID. +// You can use the helper function *DeploymentProtectionRuleEvent.GetRunID() to easily retrieve the workflow run ID from a DeploymentProtectionRuleEvent. // // GitHub API docs: https://docs.github.com/rest/actions/workflow-runs#re-run-failed-jobs-from-a-workflow-run // @@ -307,6 +340,8 @@ func (s *ActionsService) RerunFailedJobsByID(ctx context.Context, owner, repo st // RerunJobByID re-runs a job and its dependent jobs in a workflow run by ID. // +// You can use the helper function *DeploymentProtectionRuleEvent.GetRunID() to easily retrieve the workflow run ID from a DeploymentProtectionRuleEvent. +// // GitHub API docs: https://docs.github.com/rest/actions/workflow-runs#re-run-a-job-from-a-workflow-run // //meta:operation POST /repos/{owner}/{repo}/actions/jobs/{job_id}/rerun @@ -322,6 +357,7 @@ func (s *ActionsService) RerunJobByID(ctx context.Context, owner, repo string, j } // CancelWorkflowRunByID cancels a workflow run by ID. +// You can use the helper function *DeploymentProtectionRuleEvent.GetRunID() to easily retrieve the workflow run ID from a DeploymentProtectionRuleEvent. // // GitHub API docs: https://docs.github.com/rest/actions/workflow-runs#cancel-a-workflow-run // @@ -338,6 +374,7 @@ func (s *ActionsService) CancelWorkflowRunByID(ctx context.Context, owner, repo } // GetWorkflowRunLogs gets a redirect URL to download a plain text file of logs for a workflow run. +// You can use the helper function *DeploymentProtectionRuleEvent.GetRunID() to easily retrieve the workflow run ID from a DeploymentProtectionRuleEvent. // // GitHub API docs: https://docs.github.com/rest/actions/workflow-runs#download-workflow-run-logs // @@ -345,6 +382,14 @@ func (s *ActionsService) CancelWorkflowRunByID(ctx context.Context, owner, repo func (s *ActionsService) GetWorkflowRunLogs(ctx context.Context, owner, repo string, runID int64, maxRedirects int) (*url.URL, *Response, error) { u := fmt.Sprintf("repos/%v/%v/actions/runs/%v/logs", owner, repo, runID) + if s.client.RateLimitRedirectionalEndpoints { + return s.getWorkflowRunLogsWithRateLimit(ctx, u, maxRedirects) + } + + return s.getWorkflowRunLogsWithoutRateLimit(ctx, u, maxRedirects) +} + +func (s *ActionsService) getWorkflowRunLogsWithoutRateLimit(ctx context.Context, u string, maxRedirects int) (*url.URL, *Response, error) { resp, err := s.client.roundTripWithOptionalFollowRedirect(ctx, u, maxRedirects) if err != nil { return nil, nil, err @@ -359,7 +404,28 @@ func (s *ActionsService) GetWorkflowRunLogs(ctx context.Context, owner, repo str return parsedURL, newResponse(resp), err } +func (s *ActionsService) getWorkflowRunLogsWithRateLimit(ctx context.Context, u string, maxRedirects int) (*url.URL, *Response, error) { + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + url, resp, err := s.client.bareDoUntilFound(ctx, req, maxRedirects) + if err != nil { + return nil, resp, err + } + defer resp.Body.Close() + + // If we didn't receive a valid Location in a 302 response + if url == nil { + return nil, resp, fmt.Errorf("unexpected status code: %v", resp.Status) + } + + return url, resp, nil +} + // DeleteWorkflowRun deletes a workflow run by ID. +// You can use the helper function *DeploymentProtectionRuleEvent.GetRunID() to easily retrieve the workflow run ID from a DeploymentProtectionRuleEvent. // // GitHub API docs: https://docs.github.com/rest/actions/workflow-runs#delete-a-workflow-run // @@ -376,6 +442,7 @@ func (s *ActionsService) DeleteWorkflowRun(ctx context.Context, owner, repo stri } // DeleteWorkflowRunLogs deletes all logs for a workflow run. +// You can use the helper function *DeploymentProtectionRuleEvent.GetRunID() to easily retrieve the workflow run ID from a DeploymentProtectionRuleEvent. // // GitHub API docs: https://docs.github.com/rest/actions/workflow-runs#delete-workflow-run-logs // @@ -392,6 +459,7 @@ func (s *ActionsService) DeleteWorkflowRunLogs(ctx context.Context, owner, repo } // GetWorkflowRunUsageByID gets a specific workflow usage run by run ID in the unit of billable milliseconds. +// You can use the helper function *DeploymentProtectionRuleEvent.GetRunID() to easily retrieve the workflow run ID from a DeploymentProtectionRuleEvent. // // GitHub API docs: https://docs.github.com/rest/actions/workflow-runs#get-workflow-run-usage // @@ -414,6 +482,7 @@ func (s *ActionsService) GetWorkflowRunUsageByID(ctx context.Context, owner, rep } // GetPendingDeployments get all deployment environments for a workflow run that are waiting for protection rules to pass. +// You can use the helper function *DeploymentProtectionRuleEvent.GetRunID() to easily retrieve the workflow run ID from a DeploymentProtectionRuleEvent. // // GitHub API docs: https://docs.github.com/rest/actions/workflow-runs#get-pending-deployments-for-a-workflow-run // @@ -436,6 +505,7 @@ func (s *ActionsService) GetPendingDeployments(ctx context.Context, owner, repo } // PendingDeployments approve or reject pending deployments that are waiting on approval by a required reviewer. +// You can use the helper function *DeploymentProtectionRuleEvent.GetRunID() to easily retrieve the workflow run ID from a DeploymentProtectionRuleEvent. // // GitHub API docs: https://docs.github.com/rest/actions/workflow-runs#review-pending-deployments-for-a-workflow-run // @@ -458,6 +528,7 @@ func (s *ActionsService) PendingDeployments(ctx context.Context, owner, repo str } // ReviewCustomDeploymentProtectionRule approves or rejects custom deployment protection rules provided by a GitHub App for a workflow run. +// You can use the helper function *DeploymentProtectionRuleEvent.GetRunID() to easily retrieve the workflow run ID from a DeploymentProtectionRuleEvent. // // GitHub API docs: https://docs.github.com/rest/actions/workflow-runs#review-custom-deployment-protection-rules-for-a-workflow-run // diff --git a/vendor/github.com/google/go-github/v68/github/actions_workflows.go b/vendor/github.com/google/go-github/v70/github/actions_workflows.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/actions_workflows.go rename to vendor/github.com/google/go-github/v70/github/actions_workflows.go diff --git a/vendor/github.com/google/go-github/v68/github/activity.go b/vendor/github.com/google/go-github/v70/github/activity.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/activity.go rename to vendor/github.com/google/go-github/v70/github/activity.go diff --git a/vendor/github.com/google/go-github/v68/github/activity_events.go b/vendor/github.com/google/go-github/v70/github/activity_events.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/activity_events.go rename to vendor/github.com/google/go-github/v70/github/activity_events.go diff --git a/vendor/github.com/google/go-github/v68/github/activity_notifications.go b/vendor/github.com/google/go-github/v70/github/activity_notifications.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/activity_notifications.go rename to vendor/github.com/google/go-github/v70/github/activity_notifications.go diff --git a/vendor/github.com/google/go-github/v68/github/activity_star.go b/vendor/github.com/google/go-github/v70/github/activity_star.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/activity_star.go rename to vendor/github.com/google/go-github/v70/github/activity_star.go diff --git a/vendor/github.com/google/go-github/v68/github/activity_watching.go b/vendor/github.com/google/go-github/v70/github/activity_watching.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/activity_watching.go rename to vendor/github.com/google/go-github/v70/github/activity_watching.go diff --git a/vendor/github.com/google/go-github/v68/github/admin.go b/vendor/github.com/google/go-github/v70/github/admin.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/admin.go rename to vendor/github.com/google/go-github/v70/github/admin.go diff --git a/vendor/github.com/google/go-github/v68/github/admin_orgs.go b/vendor/github.com/google/go-github/v70/github/admin_orgs.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/admin_orgs.go rename to vendor/github.com/google/go-github/v70/github/admin_orgs.go diff --git a/vendor/github.com/google/go-github/v68/github/admin_stats.go b/vendor/github.com/google/go-github/v70/github/admin_stats.go similarity index 94% rename from vendor/github.com/google/go-github/v68/github/admin_stats.go rename to vendor/github.com/google/go-github/v70/github/admin_stats.go index f012d7984c..a6e406beca 100644 --- a/vendor/github.com/google/go-github/v68/github/admin_stats.go +++ b/vendor/github.com/google/go-github/v70/github/admin_stats.go @@ -118,13 +118,13 @@ func (s GistStats) String() string { return Stringify(s) } -// PullStats represents the number of total, merged, mergable and unmergeable +// PullStats represents the number of total, merged, mergeable and unmergeable // pull-requests. type PullStats struct { - TotalPulls *int `json:"total_pulls,omitempty"` - MergedPulls *int `json:"merged_pulls,omitempty"` - MergablePulls *int `json:"mergeable_pulls,omitempty"` - UnmergablePulls *int `json:"unmergeable_pulls,omitempty"` + TotalPulls *int `json:"total_pulls,omitempty"` + MergedPulls *int `json:"merged_pulls,omitempty"` + MergeablePulls *int `json:"mergeable_pulls,omitempty"` + UnmergeablePulls *int `json:"unmergeable_pulls,omitempty"` } func (s PullStats) String() string { diff --git a/vendor/github.com/google/go-github/v68/github/admin_users.go b/vendor/github.com/google/go-github/v70/github/admin_users.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/admin_users.go rename to vendor/github.com/google/go-github/v70/github/admin_users.go diff --git a/vendor/github.com/google/go-github/v68/github/apps.go b/vendor/github.com/google/go-github/v70/github/apps.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/apps.go rename to vendor/github.com/google/go-github/v70/github/apps.go diff --git a/vendor/github.com/google/go-github/v68/github/apps_hooks.go b/vendor/github.com/google/go-github/v70/github/apps_hooks.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/apps_hooks.go rename to vendor/github.com/google/go-github/v70/github/apps_hooks.go diff --git a/vendor/github.com/google/go-github/v68/github/apps_hooks_deliveries.go b/vendor/github.com/google/go-github/v70/github/apps_hooks_deliveries.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/apps_hooks_deliveries.go rename to vendor/github.com/google/go-github/v70/github/apps_hooks_deliveries.go diff --git a/vendor/github.com/google/go-github/v68/github/apps_installation.go b/vendor/github.com/google/go-github/v70/github/apps_installation.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/apps_installation.go rename to vendor/github.com/google/go-github/v70/github/apps_installation.go diff --git a/vendor/github.com/google/go-github/v68/github/apps_manifest.go b/vendor/github.com/google/go-github/v70/github/apps_manifest.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/apps_manifest.go rename to vendor/github.com/google/go-github/v70/github/apps_manifest.go diff --git a/vendor/github.com/google/go-github/v68/github/apps_marketplace.go b/vendor/github.com/google/go-github/v70/github/apps_marketplace.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/apps_marketplace.go rename to vendor/github.com/google/go-github/v70/github/apps_marketplace.go diff --git a/vendor/github.com/google/go-github/v68/github/attestations.go b/vendor/github.com/google/go-github/v70/github/attestations.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/attestations.go rename to vendor/github.com/google/go-github/v70/github/attestations.go diff --git a/vendor/github.com/google/go-github/v68/github/authorizations.go b/vendor/github.com/google/go-github/v70/github/authorizations.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/authorizations.go rename to vendor/github.com/google/go-github/v70/github/authorizations.go diff --git a/vendor/github.com/google/go-github/v68/github/billing.go b/vendor/github.com/google/go-github/v70/github/billing.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/billing.go rename to vendor/github.com/google/go-github/v70/github/billing.go diff --git a/vendor/github.com/google/go-github/v68/github/checks.go b/vendor/github.com/google/go-github/v70/github/checks.go similarity index 99% rename from vendor/github.com/google/go-github/v68/github/checks.go rename to vendor/github.com/google/go-github/v70/github/checks.go index 711be207c2..2f0f65bf92 100644 --- a/vendor/github.com/google/go-github/v68/github/checks.go +++ b/vendor/github.com/google/go-github/v70/github/checks.go @@ -87,8 +87,8 @@ type CheckSuite struct { // The following fields are only populated by Webhook events. HeadCommit *Commit `json:"head_commit,omitempty"` LatestCheckRunsCount *int64 `json:"latest_check_runs_count,omitempty"` - Rerequstable *bool `json:"rerequestable,omitempty"` - RunsRerequstable *bool `json:"runs_rerequestable,omitempty"` + Rerequestable *bool `json:"rerequestable,omitempty"` + RunsRerequestable *bool `json:"runs_rerequestable,omitempty"` } func (c CheckRun) String() string { diff --git a/vendor/github.com/google/go-github/v68/github/code_scanning.go b/vendor/github.com/google/go-github/v70/github/code_scanning.go similarity index 98% rename from vendor/github.com/google/go-github/v68/github/code_scanning.go rename to vendor/github.com/google/go-github/v70/github/code_scanning.go index a8fca98a92..19a88241d3 100644 --- a/vendor/github.com/google/go-github/v68/github/code_scanning.go +++ b/vendor/github.com/google/go-github/v70/github/code_scanning.go @@ -141,6 +141,15 @@ type AlertListOptions struct { // The name of a code scanning tool. Only results by this tool will be listed. ToolName string `url:"tool_name,omitempty"` + // The GUID of a code scanning tool. Only results by this tool will be listed. + ToolGUID string `url:"tool_guid,omitempty"` + + // The direction to sort the results by. Possible values are: asc, desc. Default: desc. + Direction string `url:"direction,omitempty"` + + // The property by which to sort the results. Possible values are: created, updated. Default: created. + Sort string `url:"sort,omitempty"` + ListCursorOptions // Add ListOptions so offset pagination with integer type "page" query parameter is accepted @@ -391,7 +400,7 @@ func (s *CodeScanningService) UploadSarif(ctx context.Context, owner, repo strin return nil, nil, err } - // This will always return an error without unmarshalling the data + // This will always return an error without unmarshaling the data resp, err := s.client.Do(ctx, req, nil) // Even though there was an error, we still return the response // in case the caller wants to inspect it further. diff --git a/vendor/github.com/google/go-github/v68/github/codesofconduct.go b/vendor/github.com/google/go-github/v70/github/codesofconduct.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/codesofconduct.go rename to vendor/github.com/google/go-github/v70/github/codesofconduct.go diff --git a/vendor/github.com/google/go-github/v68/github/codespaces.go b/vendor/github.com/google/go-github/v70/github/codespaces.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/codespaces.go rename to vendor/github.com/google/go-github/v70/github/codespaces.go diff --git a/vendor/github.com/google/go-github/v68/github/codespaces_secrets.go b/vendor/github.com/google/go-github/v70/github/codespaces_secrets.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/codespaces_secrets.go rename to vendor/github.com/google/go-github/v70/github/codespaces_secrets.go diff --git a/vendor/github.com/google/go-github/v68/github/copilot.go b/vendor/github.com/google/go-github/v70/github/copilot.go similarity index 97% rename from vendor/github.com/google/go-github/v68/github/copilot.go rename to vendor/github.com/google/go-github/v70/github/copilot.go index a2b2aa0995..b9adfcb4ee 100644 --- a/vendor/github.com/google/go-github/v68/github/copilot.go +++ b/vendor/github.com/google/go-github/v70/github/copilot.go @@ -307,7 +307,7 @@ func (s *CopilotService) ListCopilotSeats(ctx context.Context, org string, opts // // To paginate through all seats, populate 'Page' with the number of the last page. // -// GitHub API docs: https://docs.github.com/rest/copilot/copilot-user-management#list-all-copilot-seat-assignments-for-an-enterprise +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/copilot/copilot-user-management#list-all-copilot-seat-assignments-for-an-enterprise // //meta:operation GET /enterprises/{enterprise}/copilot/billing/seats func (s *CopilotService) ListCopilotEnterpriseSeats(ctx context.Context, enterprise string, opts *ListOptions) (*ListCopilotSeatsResponse, *Response, error) { @@ -467,7 +467,7 @@ func (s *CopilotService) GetSeatDetails(ctx context.Context, org, user string) ( // GetEnterpriseMetrics gets Copilot usage metrics for an enterprise. // -// GitHub API docs: https://docs.github.com/rest/copilot/copilot-metrics#get-copilot-metrics-for-an-enterprise +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/copilot/copilot-metrics#get-copilot-metrics-for-an-enterprise // //meta:operation GET /enterprises/{enterprise}/copilot/metrics func (s *CopilotService) GetEnterpriseMetrics(ctx context.Context, enterprise string, opts *CopilotMetricsListOptions) ([]*CopilotMetrics, *Response, error) { @@ -493,7 +493,7 @@ func (s *CopilotService) GetEnterpriseMetrics(ctx context.Context, enterprise st // GetEnterpriseTeamMetrics gets Copilot usage metrics for an enterprise team. // -// GitHub API docs: https://docs.github.com/rest/copilot/copilot-metrics#get-copilot-metrics-for-an-enterprise-team +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/copilot/copilot-metrics#get-copilot-metrics-for-an-enterprise-team // //meta:operation GET /enterprises/{enterprise}/team/{team_slug}/copilot/metrics func (s *CopilotService) GetEnterpriseTeamMetrics(ctx context.Context, enterprise, team string, opts *CopilotMetricsListOptions) ([]*CopilotMetrics, *Response, error) { diff --git a/vendor/github.com/google/go-github/v68/github/dependabot.go b/vendor/github.com/google/go-github/v70/github/dependabot.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/dependabot.go rename to vendor/github.com/google/go-github/v70/github/dependabot.go diff --git a/vendor/github.com/google/go-github/v68/github/dependabot_alerts.go b/vendor/github.com/google/go-github/v70/github/dependabot_alerts.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/dependabot_alerts.go rename to vendor/github.com/google/go-github/v70/github/dependabot_alerts.go diff --git a/vendor/github.com/google/go-github/v68/github/dependabot_secrets.go b/vendor/github.com/google/go-github/v70/github/dependabot_secrets.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/dependabot_secrets.go rename to vendor/github.com/google/go-github/v70/github/dependabot_secrets.go diff --git a/vendor/github.com/google/go-github/v68/github/dependency_graph.go b/vendor/github.com/google/go-github/v70/github/dependency_graph.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/dependency_graph.go rename to vendor/github.com/google/go-github/v70/github/dependency_graph.go diff --git a/vendor/github.com/google/go-github/v68/github/dependency_graph_snapshots.go b/vendor/github.com/google/go-github/v70/github/dependency_graph_snapshots.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/dependency_graph_snapshots.go rename to vendor/github.com/google/go-github/v70/github/dependency_graph_snapshots.go diff --git a/vendor/github.com/google/go-github/v68/github/doc.go b/vendor/github.com/google/go-github/v70/github/doc.go similarity index 89% rename from vendor/github.com/google/go-github/v68/github/doc.go rename to vendor/github.com/google/go-github/v70/github/doc.go index 4a0f163a0b..befbd8b8db 100644 --- a/vendor/github.com/google/go-github/v68/github/doc.go +++ b/vendor/github.com/google/go-github/v70/github/doc.go @@ -8,7 +8,7 @@ Package github provides a client for using the GitHub API. Usage: - import "github.com/google/go-github/v68/github" // with go modules enabled (GO111MODULE=on or outside GOPATH) + import "github.com/google/go-github/v70/github" // with go modules enabled (GO111MODULE=on or outside GOPATH) import "github.com/google/go-github/github" // with go modules disabled Construct a new GitHub client, then use the various services on the client to @@ -138,11 +138,17 @@ To detect this condition of error, you can check if its type is # Conditional Requests -The GitHub API has good support for conditional requests which will help -prevent you from burning through your rate limit, as well as help speed up your -application. go-github does not handle conditional requests directly, but is -instead designed to work with a caching http.Transport. We recommend using -https://github.com/gregjones/httpcache for that. +The GitHub REST API has good support for conditional HTTP requests +via the ETag header which will help prevent you from burning through your +rate limit, as well as help speed up your application. go-github does not +handle conditional requests directly, but is instead designed to work with a +caching http.Transport. + +Typically, an RFC 7234 compliant HTTP cache such as https://github.com/gregjones/httpcache +is recommended. Alternatively, the https://github.com/bored-engineer/github-conditional-http-transport +package relies on (undocumented) GitHub specific cache logic and is +recommended when making requests using short-lived credentials such as a +GitHub App installation token. Learn more about GitHub conditional requests at https://docs.github.com/rest/overview/resources-in-the-rest-api#conditional-requests. diff --git a/vendor/github.com/google/go-github/v68/github/emojis.go b/vendor/github.com/google/go-github/v70/github/emojis.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/emojis.go rename to vendor/github.com/google/go-github/v70/github/emojis.go diff --git a/vendor/github.com/google/go-github/v68/github/enterprise.go b/vendor/github.com/google/go-github/v70/github/enterprise.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/enterprise.go rename to vendor/github.com/google/go-github/v70/github/enterprise.go diff --git a/vendor/github.com/google/go-github/v70/github/enterprise_actions_hosted_runners.go b/vendor/github.com/google/go-github/v70/github/enterprise_actions_hosted_runners.go new file mode 100644 index 0000000000..e82ba9b806 --- /dev/null +++ b/vendor/github.com/google/go-github/v70/github/enterprise_actions_hosted_runners.go @@ -0,0 +1,234 @@ +// Copyright 2025 The go-github 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 github + +import ( + "context" + "fmt" +) + +// ListHostedRunners lists all the GitHub-hosted runners for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners#list-github-hosted-runners-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/hosted-runners +func (s *EnterpriseService) ListHostedRunners(ctx context.Context, enterprise string, opts *ListOptions) (*HostedRunners, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/hosted-runners", enterprise) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + runners := &HostedRunners{} + resp, err := s.client.Do(ctx, req, &runners) + if err != nil { + return nil, resp, err + } + + return runners, resp, nil +} + +// CreateHostedRunner creates a GitHub-hosted runner for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners#create-a-github-hosted-runner-for-an-enterprise +// +//meta:operation POST /enterprises/{enterprise}/actions/hosted-runners +func (s *EnterpriseService) CreateHostedRunner(ctx context.Context, enterprise string, request *HostedRunnerRequest) (*HostedRunner, *Response, error) { + if err := validateCreateHostedRunnerRequest(request); err != nil { + return nil, nil, fmt.Errorf("validation failed: %w", err) + } + + u := fmt.Sprintf("enterprises/%v/actions/hosted-runners", enterprise) + req, err := s.client.NewRequest("POST", u, request) + if err != nil { + return nil, nil, err + } + + hostedRunner := new(HostedRunner) + resp, err := s.client.Do(ctx, req, hostedRunner) + if err != nil { + return nil, resp, err + } + + return hostedRunner, resp, nil +} + +// GetHostedRunnerGitHubOwnedImages gets the list of GitHub-owned images available for GitHub-hosted runners for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners#get-github-owned-images-for-github-hosted-runners-in-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/hosted-runners/images/github-owned +func (s *EnterpriseService) GetHostedRunnerGitHubOwnedImages(ctx context.Context, enterprise string) (*HostedRunnerImages, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/hosted-runners/images/github-owned", enterprise) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + hostedRunnerImages := new(HostedRunnerImages) + resp, err := s.client.Do(ctx, req, hostedRunnerImages) + if err != nil { + return nil, resp, err + } + + return hostedRunnerImages, resp, nil +} + +// GetHostedRunnerPartnerImages gets the list of partner images available for GitHub-hosted runners for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners#get-partner-images-for-github-hosted-runners-in-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/hosted-runners/images/partner +func (s *EnterpriseService) GetHostedRunnerPartnerImages(ctx context.Context, enterprise string) (*HostedRunnerImages, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/hosted-runners/images/partner", enterprise) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + hostedRunnerImages := new(HostedRunnerImages) + resp, err := s.client.Do(ctx, req, hostedRunnerImages) + if err != nil { + return nil, resp, err + } + + return hostedRunnerImages, resp, nil +} + +// GetHostedRunnerLimits gets the GitHub-hosted runners Static public IP Limits for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners#get-limits-on-github-hosted-runners-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/hosted-runners/limits +func (s *EnterpriseService) GetHostedRunnerLimits(ctx context.Context, enterprise string) (*HostedRunnerPublicIPLimits, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/hosted-runners/limits", enterprise) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + publicIPLimits := new(HostedRunnerPublicIPLimits) + resp, err := s.client.Do(ctx, req, publicIPLimits) + if err != nil { + return nil, resp, err + } + + return publicIPLimits, resp, nil +} + +// GetHostedRunnerMachineSpecs gets the list of machine specs available for GitHub-hosted runners for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners#get-github-hosted-runners-machine-specs-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/hosted-runners/machine-sizes +func (s *EnterpriseService) GetHostedRunnerMachineSpecs(ctx context.Context, enterprise string) (*HostedRunnerMachineSpecs, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/hosted-runners/machine-sizes", enterprise) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + machineSpecs := new(HostedRunnerMachineSpecs) + resp, err := s.client.Do(ctx, req, machineSpecs) + if err != nil { + return nil, resp, err + } + + return machineSpecs, resp, nil +} + +// GetHostedRunnerPlatforms gets list of platforms available for GitHub-hosted runners for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners#get-platforms-for-github-hosted-runners-in-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/hosted-runners/platforms +func (s *EnterpriseService) GetHostedRunnerPlatforms(ctx context.Context, enterprise string) (*HostedRunnerPlatforms, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/hosted-runners/platforms", enterprise) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + platforms := new(HostedRunnerPlatforms) + resp, err := s.client.Do(ctx, req, platforms) + if err != nil { + return nil, resp, err + } + + return platforms, resp, nil +} + +// GetHostedRunner gets a GitHub-hosted runner in an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners#get-a-github-hosted-runner-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/actions/hosted-runners/{hosted_runner_id} +func (s *EnterpriseService) GetHostedRunner(ctx context.Context, enterprise string, runnerID int64) (*HostedRunner, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/hosted-runners/%v", enterprise, runnerID) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + hostedRunner := new(HostedRunner) + resp, err := s.client.Do(ctx, req, hostedRunner) + if err != nil { + return nil, resp, err + } + + return hostedRunner, resp, nil +} + +// UpdateHostedRunner updates a GitHub-hosted runner for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners#update-a-github-hosted-runner-for-an-enterprise +// +//meta:operation PATCH /enterprises/{enterprise}/actions/hosted-runners/{hosted_runner_id} +func (s *EnterpriseService) UpdateHostedRunner(ctx context.Context, enterprise string, runnerID int64, updateReq HostedRunnerRequest) (*HostedRunner, *Response, error) { + if err := validateUpdateHostedRunnerRequest(&updateReq); err != nil { + return nil, nil, fmt.Errorf("validation failed: %w", err) + } + + u := fmt.Sprintf("enterprises/%v/actions/hosted-runners/%v", enterprise, runnerID) + req, err := s.client.NewRequest("PATCH", u, updateReq) + if err != nil { + return nil, nil, err + } + + hostedRunner := new(HostedRunner) + resp, err := s.client.Do(ctx, req, hostedRunner) + if err != nil { + return nil, resp, err + } + + return hostedRunner, resp, nil +} + +// DeleteHostedRunner deletes GitHub-hosted runner from an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/actions/hosted-runners#delete-a-github-hosted-runner-for-an-enterprise +// +//meta:operation DELETE /enterprises/{enterprise}/actions/hosted-runners/{hosted_runner_id} +func (s *EnterpriseService) DeleteHostedRunner(ctx context.Context, enterprise string, runnerID int64) (*HostedRunner, *Response, error) { + u := fmt.Sprintf("enterprises/%v/actions/hosted-runners/%v", enterprise, runnerID) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, nil, err + } + + hostedRunner := new(HostedRunner) + resp, err := s.client.Do(ctx, req, hostedRunner) + if err != nil { + return nil, resp, err + } + + return hostedRunner, resp, nil +} diff --git a/vendor/github.com/google/go-github/v68/github/enterprise_actions_runner_groups.go b/vendor/github.com/google/go-github/v70/github/enterprise_actions_runner_groups.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/enterprise_actions_runner_groups.go rename to vendor/github.com/google/go-github/v70/github/enterprise_actions_runner_groups.go diff --git a/vendor/github.com/google/go-github/v68/github/enterprise_actions_runners.go b/vendor/github.com/google/go-github/v70/github/enterprise_actions_runners.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/enterprise_actions_runners.go rename to vendor/github.com/google/go-github/v70/github/enterprise_actions_runners.go diff --git a/vendor/github.com/google/go-github/v68/github/enterprise_audit_log.go b/vendor/github.com/google/go-github/v70/github/enterprise_audit_log.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/enterprise_audit_log.go rename to vendor/github.com/google/go-github/v70/github/enterprise_audit_log.go diff --git a/vendor/github.com/google/go-github/v68/github/enterprise_code_security_and_analysis.go b/vendor/github.com/google/go-github/v70/github/enterprise_code_security_and_analysis.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/enterprise_code_security_and_analysis.go rename to vendor/github.com/google/go-github/v70/github/enterprise_code_security_and_analysis.go diff --git a/vendor/github.com/google/go-github/v70/github/enterprise_manage_ghes.go b/vendor/github.com/google/go-github/v70/github/enterprise_manage_ghes.go new file mode 100644 index 0000000000..e14836eb02 --- /dev/null +++ b/vendor/github.com/google/go-github/v70/github/enterprise_manage_ghes.go @@ -0,0 +1,163 @@ +// Copyright 2025 The go-github 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 github + +import ( + "context" +) + +// NodeQueryOptions specifies the optional parameters to the EnterpriseService +// Node management APIs. +type NodeQueryOptions struct { + // UUID filters issues based on the node UUID. + UUID *string `url:"uuid,omitempty"` + + // ClusterRoles filters the cluster roles from the cluster configuration file. + ClusterRoles *string `url:"cluster_roles,omitempty"` +} + +// ClusterStatus represents a response from the ClusterStatus and ReplicationStatus methods. +type ClusterStatus struct { + Status *string `json:"status,omitempty"` + Nodes []*ClusterStatusNode `json:"nodes"` +} + +// ClusterStatusNode represents the status of a cluster node. +type ClusterStatusNode struct { + Hostname *string `json:"hostname,omitempty"` + Status *string `json:"status,omitempty"` + Services []*ClusterStatusNodeServiceItem `json:"services"` +} + +// ClusterStatusNodeServiceItem represents the status of a service running on a cluster node. +type ClusterStatusNodeServiceItem struct { + Status *string `json:"status,omitempty"` + Name *string `json:"name,omitempty"` + Details *string `json:"details,omitempty"` +} + +// SystemRequirements represents a response from the CheckSystemRequirements method. +type SystemRequirements struct { + Status *string `json:"status,omitempty"` + Nodes []*SystemRequirementsNode `json:"nodes"` +} + +// SystemRequirementsNode represents the status of a system node. +type SystemRequirementsNode struct { + Hostname *string `json:"hostname,omitempty"` + Status *string `json:"status,omitempty"` + RolesStatus []*SystemRequirementsNodeRoleStatus `json:"roles_status"` +} + +// SystemRequirementsNodeRoleStatus represents the status of a role on a system node. +type SystemRequirementsNodeRoleStatus struct { + Status *string `json:"status,omitempty"` + Role *string `json:"role,omitempty"` +} + +// NodeReleaseVersion represents a response from the GetNodeReleaseVersions method. +type NodeReleaseVersion struct { + Hostname *string `json:"hostname,omitempty"` + Version *ReleaseVersion `json:"version"` +} + +// ReleaseVersion holds the release version information of the node. +type ReleaseVersion struct { + Version *string `json:"version,omitempty"` + Platform *string `json:"platform,omitempty"` + BuildID *string `json:"build_id,omitempty"` + BuildDate *string `json:"build_date,omitempty"` +} + +// CheckSystemRequirements checks if GHES system nodes meet the system requirements. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.15/rest/enterprise-admin/manage-ghes#get-the-system-requirement-check-results-for-configured-cluster-nodes +// +//meta:operation GET /manage/v1/checks/system-requirements +func (s *EnterpriseService) CheckSystemRequirements(ctx context.Context) (*SystemRequirements, *Response, error) { + u := "manage/v1/checks/system-requirements" + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + systemRequirements := new(SystemRequirements) + resp, err := s.client.Do(ctx, req, systemRequirements) + if err != nil { + return nil, resp, err + } + + return systemRequirements, resp, nil +} + +// ClusterStatus gets the status of all services running on each cluster node. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.15/rest/enterprise-admin/manage-ghes#get-the-status-of-services-running-on-all-cluster-nodes +// +//meta:operation GET /manage/v1/cluster/status +func (s *EnterpriseService) ClusterStatus(ctx context.Context) (*ClusterStatus, *Response, error) { + u := "manage/v1/cluster/status" + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + clusterStatus := new(ClusterStatus) + resp, err := s.client.Do(ctx, req, clusterStatus) + if err != nil { + return nil, resp, err + } + + return clusterStatus, resp, nil +} + +// ReplicationStatus gets the status of all services running on each replica node. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.15/rest/enterprise-admin/manage-ghes#get-the-status-of-services-running-on-all-replica-nodes +// +//meta:operation GET /manage/v1/replication/status +func (s *EnterpriseService) ReplicationStatus(ctx context.Context, opts *NodeQueryOptions) (*ClusterStatus, *Response, error) { + u, err := addOptions("manage/v1/replication/status", opts) + if err != nil { + return nil, nil, err + } + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + status := new(ClusterStatus) + resp, err := s.client.Do(ctx, req, status) + if err != nil { + return nil, resp, err + } + + return status, resp, nil +} + +// GetNodeReleaseVersions gets the version information deployed to each node. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.15/rest/enterprise-admin/manage-ghes#get-all-ghes-release-versions-for-all-nodes +// +//meta:operation GET /manage/v1/version +func (s *EnterpriseService) GetNodeReleaseVersions(ctx context.Context, opts *NodeQueryOptions) ([]*NodeReleaseVersion, *Response, error) { + u, err := addOptions("manage/v1/version", opts) + if err != nil { + return nil, nil, err + } + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var releaseVersions []*NodeReleaseVersion + resp, err := s.client.Do(ctx, req, &releaseVersions) + if err != nil { + return nil, resp, err + } + + return releaseVersions, resp, nil +} diff --git a/vendor/github.com/google/go-github/v70/github/enterprise_manage_ghes_config.go b/vendor/github.com/google/go-github/v70/github/enterprise_manage_ghes_config.go new file mode 100644 index 0000000000..10fb8590e4 --- /dev/null +++ b/vendor/github.com/google/go-github/v70/github/enterprise_manage_ghes_config.go @@ -0,0 +1,516 @@ +// Copyright 2025 The go-github 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 github + +import ( + "context" + "errors" +) + +// ConfigApplyOptions is a struct to hold the options for the ConfigApply API and the response. +type ConfigApplyOptions struct { + // RunID is the ID of the run to get the status of. If empty a random one will be generated. + RunID *string `json:"run_id,omitempty"` +} + +// ConfigApplyStatus is a struct to hold the response from the ConfigApply API. +type ConfigApplyStatus struct { + Running *bool `json:"running,omitempty"` + Successful *bool `json:"successful,omitempty"` + Nodes []*ConfigApplyStatusNode `json:"nodes"` +} + +// ConfigApplyStatusNode is a struct to hold the response from the ConfigApply API. +type ConfigApplyStatusNode struct { + Hostname *string `json:"hostname,omitempty"` + Running *bool `json:"running,omitempty"` + Successful *bool `json:"successful,omitempty"` + RunID *string `json:"run_id,omitempty"` +} + +// ConfigApplyEventsOptions is used to enable pagination. +type ConfigApplyEventsOptions struct { + LastRequestID *string `url:"last_request_id,omitempty"` +} + +// ConfigApplyEvents is a struct to hold the response from the ConfigApplyEvents API. +type ConfigApplyEvents struct { + Nodes []*ConfigApplyEventsNode `json:"nodes"` +} + +// ConfigApplyEventsNode is a struct to hold the response from the ConfigApplyEvents API. +type ConfigApplyEventsNode struct { + Node *string `json:"node,omitempty"` + LastRequestID *string `json:"last_request_id,omitempty"` + Events []*ConfigApplyEventsNodeEvent `json:"events"` +} + +// ConfigApplyEventsNodeEvent is a struct to hold the response from the ConfigApplyEvents API. +type ConfigApplyEventsNodeEvent struct { + Timestamp *Timestamp `json:"timestamp,omitempty"` + SeverityText *string `json:"severity_text,omitempty"` + Body *string `json:"body,omitempty"` + EventName *string `json:"event_name,omitempty"` + Topology *string `json:"topology,omitempty"` + Hostname *string `json:"hostname,omitempty"` + ConfigRunID *string `json:"config_run_id,omitempty"` + TraceID *string `json:"trace_id,omitempty"` + SpanID *string `json:"span_id,omitempty"` + SpanParentID *int64 `json:"span_parent_id,omitempty"` + SpanDepth *int `json:"span_depth,omitempty"` +} + +// InitialConfigOptions is a struct to hold the options for the InitialConfig API. +type InitialConfigOptions struct { + License string `url:"license"` + Password string `url:"password"` +} + +// LicenseStatus is a struct to hold the response from the License API. +type LicenseStatus struct { + AdvancedSecurityEnabled *bool `json:"advancedSecurityEnabled,omitempty"` + AdvancedSecuritySeats *int `json:"advancedSecuritySeats,omitempty"` + ClusterSupport *bool `json:"clusterSupport,omitempty"` + Company *string `json:"company,omitempty"` + CroquetSupport *bool `json:"croquetSupport,omitempty"` + CustomTerms *bool `json:"customTerms,omitempty"` + Evaluation *bool `json:"evaluation,omitempty"` + ExpireAt *Timestamp `json:"expireAt,omitempty"` + InsightsEnabled *bool `json:"insightsEnabled,omitempty"` + InsightsExpireAt *Timestamp `json:"insightsExpireAt,omitempty"` + LearningLabEvaluationExpires *Timestamp `json:"learningLabEvaluationExpires,omitempty"` + LearningLabSeats *int `json:"learningLabSeats,omitempty"` + Perpetual *bool `json:"perpetual,omitempty"` + ReferenceNumber *string `json:"referenceNumber,omitempty"` + Seats *int `json:"seats,omitempty"` + SSHAllowed *bool `json:"sshAllowed,omitempty"` + SupportKey *string `json:"supportKey,omitempty"` + UnlimitedSeating *bool `json:"unlimitedSeating,omitempty"` +} + +// UploadLicenseOptions is a struct to hold the options for the UploadLicense API. +type UploadLicenseOptions struct { + License string `url:"license"` +} + +// LicenseCheck is a struct to hold the response from the LicenseStatus API. +type LicenseCheck struct { + Status *string `json:"status,omitempty"` +} + +// ConfigSettings is a struct to hold the response from the Settings API. +// There are many fields that link to other structs. +type ConfigSettings struct { + PrivateMode *bool `json:"private_mode,omitempty"` + PublicPages *bool `json:"public_pages,omitempty"` + SubdomainIsolation *bool `json:"subdomain_isolation,omitempty"` + SignupEnabled *bool `json:"signup_enabled,omitempty"` + GithubHostname *string `json:"github_hostname,omitempty"` + IdenticonsHost *string `json:"identicons_host,omitempty"` + HTTPProxy *string `json:"http_proxy,omitempty"` + AuthMode *string `json:"auth_mode,omitempty"` + ExpireSessions *bool `json:"expire_sessions,omitempty"` + AdminPassword *string `json:"admin_password,omitempty"` + ConfigurationID *int64 `json:"configuration_id,omitempty"` + ConfigurationRunCount *int `json:"configuration_run_count,omitempty"` + Avatar *ConfigSettingsAvatar `json:"avatar,omitempty"` + Customer *ConfigSettingsCustomer `json:"customer,omitempty"` + License *ConfigSettingsLicenseSettings `json:"license,omitempty"` + GithubSSL *ConfigSettingsGithubSSL `json:"github_ssl,omitempty"` + LDAP *ConfigSettingsLDAP `json:"ldap,omitempty"` + CAS *ConfigSettingsCAS `json:"cas,omitempty"` + SAML *ConfigSettingsSAML `json:"saml,omitempty"` + GithubOAuth *ConfigSettingsGithubOAuth `json:"github_oauth,omitempty"` + SMTP *ConfigSettingsSMTP `json:"smtp,omitempty"` + NTP *ConfigSettingsNTP `json:"ntp,omitempty"` + Timezone *string `json:"timezone,omitempty"` + SNMP *ConfigSettingsSNMP `json:"snmp,omitempty"` + Syslog *ConfigSettingsSyslog `json:"syslog,omitempty"` + Assets *string `json:"assets,omitempty"` + Pages *ConfigSettingsPagesSettings `json:"pages,omitempty"` + Collectd *ConfigSettingsCollectd `json:"collectd,omitempty"` + Mapping *ConfigSettingsMapping `json:"mapping,omitempty"` + LoadBalancer *string `json:"load_balancer,omitempty"` +} + +// ConfigSettingsAvatar is a struct to hold the response from the Settings API. +type ConfigSettingsAvatar struct { + Enabled *bool `json:"enabled,omitempty"` + URI *string `json:"uri,omitempty"` +} + +// ConfigSettingsCustomer is a struct to hold the response from the Settings API. +type ConfigSettingsCustomer struct { + Name *string `json:"name,omitempty"` + Email *string `json:"email,omitempty"` + UUID *string `json:"uuid,omitempty"` + Secret *string `json:"secret,omitempty"` + PublicKeyData *string `json:"public_key_data,omitempty"` +} + +// ConfigSettingsLicenseSettings is a struct to hold the response from the Settings API. +type ConfigSettingsLicenseSettings struct { + Seats *int `json:"seats,omitempty"` + Evaluation *bool `json:"evaluation,omitempty"` + Perpetual *bool `json:"perpetual,omitempty"` + UnlimitedSeating *bool `json:"unlimited_seating,omitempty"` + SupportKey *string `json:"support_key,omitempty"` + SSHAllowed *bool `json:"ssh_allowed,omitempty"` + ClusterSupport *bool `json:"cluster_support,omitempty"` + ExpireAt *Timestamp `json:"expire_at,omitempty"` +} + +// ConfigSettingsGithubSSL is a struct to hold the response from the Settings API. +type ConfigSettingsGithubSSL struct { + Enabled *bool `json:"enabled,omitempty"` + Cert *string `json:"cert,omitempty"` + Key *string `json:"key,omitempty"` +} + +// ConfigSettingsLDAP is a struct to hold the response from the Settings API. +type ConfigSettingsLDAP struct { + Host *string `json:"host,omitempty"` + Port *int `json:"port,omitempty"` + Base []string `json:"base,omitempty"` + UID *string `json:"uid,omitempty"` + BindDN *string `json:"bind_dn,omitempty"` + Password *string `json:"password,omitempty"` + Method *string `json:"method,omitempty"` + SearchStrategy *string `json:"search_strategy,omitempty"` + UserGroups []string `json:"user_groups,omitempty"` + AdminGroup *string `json:"admin_group,omitempty"` + VirtualAttributeEnabled *bool `json:"virtual_attribute_enabled,omitempty"` + RecursiveGroupSearch *bool `json:"recursive_group_search,omitempty"` + PosixSupport *bool `json:"posix_support,omitempty"` + UserSyncEmails *bool `json:"user_sync_emails,omitempty"` + UserSyncKeys *bool `json:"user_sync_keys,omitempty"` + UserSyncInterval *int `json:"user_sync_interval,omitempty"` + TeamSyncInterval *int `json:"team_sync_interval,omitempty"` + SyncEnabled *bool `json:"sync_enabled,omitempty"` + Reconciliation *ConfigSettingsLDAPReconciliation `json:"reconciliation,omitempty"` + Profile *ConfigSettingsLDAPProfile `json:"profile,omitempty"` +} + +// ConfigSettingsLDAPReconciliation is part of the ConfigSettingsLDAP struct. +type ConfigSettingsLDAPReconciliation struct { + User *string `json:"user,omitempty"` + Org *string `json:"org,omitempty"` +} + +// ConfigSettingsLDAPProfile is part of the ConfigSettingsLDAP struct. +type ConfigSettingsLDAPProfile struct { + UID *string `json:"uid,omitempty"` + Name *string `json:"name,omitempty"` + Mail *string `json:"mail,omitempty"` + Key *string `json:"key,omitempty"` +} + +// ConfigSettingsCAS is a struct to hold the response from the Settings API. +type ConfigSettingsCAS struct { + URL *string `json:"url,omitempty"` +} + +// ConfigSettingsSAML is a struct to hold the response from the Settings API. +type ConfigSettingsSAML struct { + SSOURL *string `json:"sso_url,omitempty"` + Certificate *string `json:"certificate,omitempty"` + CertificatePath *string `json:"certificate_path,omitempty"` + Issuer *string `json:"issuer,omitempty"` + IDPInitiatedSSO *bool `json:"idp_initiated_sso,omitempty"` + DisableAdminDemote *bool `json:"disable_admin_demote,omitempty"` +} + +// ConfigSettingsGithubOAuth is a struct to hold the response from the Settings API. +type ConfigSettingsGithubOAuth struct { + ClientID *string `json:"client_id,omitempty"` + ClientSecret *string `json:"client_secret,omitempty"` + OrganizationName *string `json:"organization_name,omitempty"` + OrganizationTeam *string `json:"organization_team,omitempty"` +} + +// ConfigSettingsSMTP is a struct to hold the response from the Settings API. +type ConfigSettingsSMTP struct { + Enabled *bool `json:"enabled,omitempty"` + Address *string `json:"address,omitempty"` + Authentication *string `json:"authentication,omitempty"` + Port *string `json:"port,omitempty"` + Domain *string `json:"domain,omitempty"` + Username *string `json:"username,omitempty"` + UserName *string `json:"user_name,omitempty"` + EnableStarttlsAuto *bool `json:"enable_starttls_auto,omitempty"` + Password *string `json:"password,omitempty"` + DiscardToNoreplyAddress *bool `json:"discard-to-noreply-address,omitempty"` + SupportAddress *string `json:"support_address,omitempty"` + SupportAddressType *string `json:"support_address_type,omitempty"` + NoreplyAddress *string `json:"noreply_address,omitempty"` +} + +// ConfigSettingsNTP is a struct to hold the response from the Settings API. +type ConfigSettingsNTP struct { + PrimaryServer *string `json:"primary_server,omitempty"` + SecondaryServer *string `json:"secondary_server,omitempty"` +} + +// ConfigSettingsSNMP is a struct to hold the response from the Settings API. +type ConfigSettingsSNMP struct { + Enabled *bool `json:"enabled,omitempty"` + Community *string `json:"community,omitempty"` +} + +// ConfigSettingsSyslog is a struct to hold the response from the Settings API. +type ConfigSettingsSyslog struct { + Enabled *bool `json:"enabled,omitempty"` + Server *string `json:"server,omitempty"` + ProtocolName *string `json:"protocol_name,omitempty"` +} + +// ConfigSettingsPagesSettings is a struct to hold the response from the Settings API. +type ConfigSettingsPagesSettings struct { + Enabled *bool `json:"enabled,omitempty"` +} + +// ConfigSettingsCollectd is a struct to hold the response from the Settings API. +type ConfigSettingsCollectd struct { + Enabled *bool `json:"enabled,omitempty"` + Server *string `json:"server,omitempty"` + Port *int `json:"port,omitempty"` + Encryption *string `json:"encryption,omitempty"` + Username *string `json:"username,omitempty"` + Password *string `json:"password,omitempty"` +} + +// ConfigSettingsMapping is a struct to hold the response from the Settings API. +type ConfigSettingsMapping struct { + Enabled *bool `json:"enabled,omitempty"` + Tileserver *string `json:"tileserver,omitempty"` + Basemap *string `json:"basemap,omitempty"` + Token *string `json:"token,omitempty"` +} + +// NodeMetadataStatus is a struct to hold the response from the NodeMetadata API. +type NodeMetadataStatus struct { + Topology *string `json:"topology,omitempty"` + Nodes []*NodeDetails `json:"nodes"` +} + +// NodeDetails is a struct to hold the response from the NodeMetadata API. +type NodeDetails struct { + Hostname *string `json:"hostname,omitempty"` + UUID *string `json:"uuid,omitempty"` + ClusterRoles []string `json:"cluster_roles,omitempty"` +} + +// ConfigApplyEvents gets events from the command ghe-config-apply. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.15/rest/enterprise-admin/manage-ghes#list-events-from-ghe-config-apply +// +//meta:operation GET /manage/v1/config/apply/events +func (s *EnterpriseService) ConfigApplyEvents(ctx context.Context, opts *ConfigApplyEventsOptions) (*ConfigApplyEvents, *Response, error) { + u, err := addOptions("manage/v1/config/apply/events", opts) + if err != nil { + return nil, nil, err + } + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + configApplyEvents := new(ConfigApplyEvents) + resp, err := s.client.Do(ctx, req, configApplyEvents) + if err != nil { + return nil, resp, err + } + + return configApplyEvents, resp, nil +} + +// InitialConfig initializes the GitHub Enterprise instance with a license and password. +// After initializing the instance, you need to run an apply to apply the configuration. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.15/rest/enterprise-admin/manage-ghes#initialize-instance-configuration-with-license-and-password +// +//meta:operation POST /manage/v1/config/init +func (s *EnterpriseService) InitialConfig(ctx context.Context, license, password string) (*Response, error) { + u := "manage/v1/config/init" + + opts := &InitialConfigOptions{ + License: license, + Password: password, + } + + req, err := s.client.NewRequest("POST", u, opts) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// License gets the current license information for the GitHub Enterprise instance. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.15/rest/enterprise-admin/manage-ghes#get-the-enterprise-license-information +// +//meta:operation GET /manage/v1/config/license +func (s *EnterpriseService) License(ctx context.Context) ([]*LicenseStatus, *Response, error) { + u := "manage/v1/config/license" + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var licenseStatus []*LicenseStatus + resp, err := s.client.Do(ctx, req, &licenseStatus) + if err != nil { + return nil, resp, err + } + + return licenseStatus, resp, nil +} + +// UploadLicense uploads a new license to the GitHub Enterprise instance. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.15/rest/enterprise-admin/manage-ghes#upload-an-enterprise-license +// +//meta:operation PUT /manage/v1/config/license +func (s *EnterpriseService) UploadLicense(ctx context.Context, license string) (*Response, error) { + u := "manage/v1/config/license" + opts := &UploadLicenseOptions{ + License: license, + } + req, err := s.client.NewRequest("PUT", u, opts) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// LicenseStatus gets the current license status for the GitHub Enterprise instance. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.15/rest/enterprise-admin/manage-ghes#check-a-license +// +//meta:operation GET /manage/v1/config/license/check +func (s *EnterpriseService) LicenseStatus(ctx context.Context) ([]*LicenseCheck, *Response, error) { + u := "manage/v1/config/license/check" + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var checks []*LicenseCheck + resp, err := s.client.Do(ctx, req, &checks) + if err != nil { + return nil, resp, err + } + + return checks, resp, nil +} + +// NodeMetadata gets the metadata for all nodes in the GitHub Enterprise instance. +// This is required for clustered setups. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.15/rest/enterprise-admin/manage-ghes#get-ghes-node-metadata-for-all-nodes +// +//meta:operation GET /manage/v1/config/nodes +func (s *EnterpriseService) NodeMetadata(ctx context.Context, opts *NodeQueryOptions) (*NodeMetadataStatus, *Response, error) { + u, err := addOptions("manage/v1/config/nodes", opts) + if err != nil { + return nil, nil, err + } + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + status := new(NodeMetadataStatus) + resp, err := s.client.Do(ctx, req, status) + if err != nil { + return nil, resp, err + } + + return status, resp, nil +} + +// Settings gets the current configuration settings for the GitHub Enterprise instance. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.15/rest/enterprise-admin/manage-ghes#get-the-ghes-settings +// +//meta:operation GET /manage/v1/config/settings +func (s *EnterpriseService) Settings(ctx context.Context) (*ConfigSettings, *Response, error) { + u := "manage/v1/config/settings" + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + configSettings := new(ConfigSettings) + resp, err := s.client.Do(ctx, req, configSettings) + if err != nil { + return nil, resp, err + } + + return configSettings, resp, nil +} + +// UpdateSettings updates the configuration settings for the GitHub Enterprise instance. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.15/rest/enterprise-admin/manage-ghes#set-settings +// +//meta:operation PUT /manage/v1/config/settings +func (s *EnterpriseService) UpdateSettings(ctx context.Context, opts *ConfigSettings) (*Response, error) { + u := "manage/v1/config/settings" + + if opts == nil { + return nil, errors.New("opts should not be nil") + } + req, err := s.client.NewRequest("PUT", u, opts) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} + +// ConfigApply triggers a configuration apply run on the GitHub Enterprise instance. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.15/rest/enterprise-admin/manage-ghes#trigger-a-ghe-config-apply-run +// +//meta:operation POST /manage/v1/config/apply +func (s *EnterpriseService) ConfigApply(ctx context.Context, opts *ConfigApplyOptions) (*ConfigApplyOptions, *Response, error) { + u := "manage/v1/config/apply" + req, err := s.client.NewRequest("POST", u, opts) + if err != nil { + return nil, nil, err + } + + configApplyOptions := new(ConfigApplyOptions) + resp, err := s.client.Do(ctx, req, configApplyOptions) + if err != nil { + return nil, resp, err + } + return configApplyOptions, resp, nil +} + +// ConfigApplyStatus gets the status of a ghe-config-apply run on the GitHub Enterprise instance. +// You can request lat one or specific id one. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.15/rest/enterprise-admin/manage-ghes#get-the-status-of-a-ghe-config-apply-run +// +//meta:operation GET /manage/v1/config/apply +func (s *EnterpriseService) ConfigApplyStatus(ctx context.Context, opts *ConfigApplyOptions) (*ConfigApplyStatus, *Response, error) { + u := "manage/v1/config/apply" + req, err := s.client.NewRequest("GET", u, opts) + if err != nil { + return nil, nil, err + } + + status := new(ConfigApplyStatus) + resp, err := s.client.Do(ctx, req, status) + if err != nil { + return nil, resp, err + } + return status, resp, nil +} diff --git a/vendor/github.com/google/go-github/v70/github/enterprise_manage_ghes_maintenance.go b/vendor/github.com/google/go-github/v70/github/enterprise_manage_ghes_maintenance.go new file mode 100644 index 0000000000..3b1de92df1 --- /dev/null +++ b/vendor/github.com/google/go-github/v70/github/enterprise_manage_ghes_maintenance.go @@ -0,0 +1,94 @@ +// Copyright 2025 The go-github 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 github + +import ( + "context" +) + +// MaintenanceOperationStatus represents the message to be displayed when the instance gets a maintenance operation request. +type MaintenanceOperationStatus struct { + Hostname *string `json:"hostname,omitempty"` + UUID *string `json:"uuid,omitempty"` + Message *string `json:"message,omitempty"` +} + +// MaintenanceStatus represents the status of maintenance mode for all nodes. +type MaintenanceStatus struct { + Hostname *string `json:"hostname,omitempty"` + UUID *string `json:"uuid,omitempty"` + Status *string `json:"status,omitempty"` + ScheduledTime *Timestamp `json:"scheduled_time,omitempty"` + ConnectionServices []*ConnectionServiceItem `json:"connection_services,omitempty"` + CanUnsetMaintenance *bool `json:"can_unset_maintenance,omitempty"` + IPExceptionList []string `json:"ip_exception_list,omitempty"` + MaintenanceModeMessage *string `json:"maintenance_mode_message,omitempty"` +} + +// ConnectionServiceItem represents the connection services for the maintenance status. +type ConnectionServiceItem struct { + Name *string `json:"name,omitempty"` + Number *int `json:"number,omitempty"` +} + +// MaintenanceOptions represents the options for setting the maintenance mode for the instance. +// When can be a string, so we can't use a Timestamp type. +type MaintenanceOptions struct { + Enabled bool `json:"enabled"` + UUID *string `json:"uuid,omitempty"` + When *string `json:"when,omitempty"` + IPExceptionList []string `json:"ip_exception_list,omitempty"` + MaintenanceModeMessage *string `json:"maintenance_mode_message,omitempty"` +} + +// GetMaintenanceStatus gets the status of maintenance mode for all nodes. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.15/rest/enterprise-admin/manage-ghes#get-the-status-of-maintenance-mode +// +//meta:operation GET /manage/v1/maintenance +func (s *EnterpriseService) GetMaintenanceStatus(ctx context.Context, opts *NodeQueryOptions) ([]*MaintenanceStatus, *Response, error) { + u, err := addOptions("manage/v1/maintenance", opts) + if err != nil { + return nil, nil, err + } + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var status []*MaintenanceStatus + resp, err := s.client.Do(ctx, req, &status) + if err != nil { + return nil, resp, err + } + + return status, resp, nil +} + +// CreateMaintenance sets the maintenance mode for the instance. +// With the enable parameter we can control to put instance into maintenance mode or not. With false we can disable the maintenance mode. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.15/rest/enterprise-admin/manage-ghes#set-the-status-of-maintenance-mode +// +//meta:operation POST /manage/v1/maintenance +func (s *EnterpriseService) CreateMaintenance(ctx context.Context, enable bool, opts *MaintenanceOptions) ([]*MaintenanceOperationStatus, *Response, error) { + u := "manage/v1/maintenance" + + opts.Enabled = enable + + req, err := s.client.NewRequest("POST", u, opts) + if err != nil { + return nil, nil, err + } + + var i []*MaintenanceOperationStatus + resp, err := s.client.Do(ctx, req, &i) + if err != nil { + return nil, resp, err + } + + return i, resp, nil +} diff --git a/vendor/github.com/google/go-github/v70/github/enterprise_manage_ghes_ssh.go b/vendor/github.com/google/go-github/v70/github/enterprise_manage_ghes_ssh.go new file mode 100644 index 0000000000..77d2521659 --- /dev/null +++ b/vendor/github.com/google/go-github/v70/github/enterprise_manage_ghes_ssh.go @@ -0,0 +1,99 @@ +// Copyright 2025 The go-github 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 github + +import ( + "context" +) + +// SSHKeyStatus represents the status of a SSH key operation. +type SSHKeyStatus struct { + Hostname *string `json:"hostname,omitempty"` + UUID *string `json:"uuid,omitempty"` + Message *string `json:"message,omitempty"` + Modified *bool `json:"modified,omitempty"` +} + +// SSHKeyOptions specifies the parameters to the SSH create and delete functions. +type SSHKeyOptions struct { + // Key is the SSH key to add to the instance. + Key string `json:"key"` +} + +// ClusterSSHKey represents the SSH keys configured for the instance. +type ClusterSSHKey struct { + Key *string `json:"key,omitempty"` + Fingerprint *string `json:"fingerprint,omitempty"` +} + +// DeleteSSHKey deletes the SSH key from the instance. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.15/rest/enterprise-admin/manage-ghes#delete-a-ssh-key +// +//meta:operation DELETE /manage/v1/access/ssh +func (s *EnterpriseService) DeleteSSHKey(ctx context.Context, key string) ([]*SSHKeyStatus, *Response, error) { + u := "manage/v1/access/ssh" + opts := &SSHKeyOptions{ + Key: key, + } + req, err := s.client.NewRequest("DELETE", u, opts) + if err != nil { + return nil, nil, err + } + + var sshStatus []*SSHKeyStatus + resp, err := s.client.Do(ctx, req, &sshStatus) + if err != nil { + return nil, resp, err + } + + return sshStatus, resp, nil +} + +// GetSSHKey gets the SSH keys configured for the instance. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.15/rest/enterprise-admin/manage-ghes#get-the-configured-ssh-keys +// +//meta:operation GET /manage/v1/access/ssh +func (s *EnterpriseService) GetSSHKey(ctx context.Context) ([]*ClusterSSHKey, *Response, error) { + u := "manage/v1/access/ssh" + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var sshKeys []*ClusterSSHKey + resp, err := s.client.Do(ctx, req, &sshKeys) + if err != nil { + return nil, resp, err + } + + return sshKeys, resp, nil +} + +// CreateSSHKey adds a new SSH key to the instance. +// +// GitHub API docs: https://docs.github.com/enterprise-server@3.15/rest/enterprise-admin/manage-ghes#set-a-new-ssh-key +// +//meta:operation POST /manage/v1/access/ssh +func (s *EnterpriseService) CreateSSHKey(ctx context.Context, key string) ([]*SSHKeyStatus, *Response, error) { + u := "manage/v1/access/ssh" + opts := &SSHKeyOptions{ + Key: key, + } + req, err := s.client.NewRequest("POST", u, opts) + if err != nil { + return nil, nil, err + } + + var sshKeyResponse []*SSHKeyStatus + resp, err := s.client.Do(ctx, req, &sshKeyResponse) + if err != nil { + return nil, resp, err + } + + return sshKeyResponse, resp, nil +} diff --git a/vendor/github.com/google/go-github/v70/github/enterprise_network_configurations.go b/vendor/github.com/google/go-github/v70/github/enterprise_network_configurations.go new file mode 100644 index 0000000000..a6a690d786 --- /dev/null +++ b/vendor/github.com/google/go-github/v70/github/enterprise_network_configurations.go @@ -0,0 +1,139 @@ +// Copyright 2025 The go-github 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 github + +import ( + "context" + "fmt" +) + +// ListEnterpriseNetworkConfigurations lists all hosted compute network configurations configured in an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/network-configurations#list-hosted-compute-network-configurations-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/network-configurations +func (s *EnterpriseService) ListEnterpriseNetworkConfigurations(ctx context.Context, enterprise string, opts *ListOptions) (*NetworkConfigurations, *Response, error) { + u := fmt.Sprintf("enterprises/%v/network-configurations", enterprise) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + networks := &NetworkConfigurations{} + resp, err := s.client.Do(ctx, req, networks) + if err != nil { + return nil, resp, err + } + return networks, resp, nil +} + +// CreateEnterpriseNetworkConfiguration creates a hosted compute network configuration for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/network-configurations#create-a-hosted-compute-network-configuration-for-an-enterprise +// +//meta:operation POST /enterprises/{enterprise}/network-configurations +func (s *EnterpriseService) CreateEnterpriseNetworkConfiguration(ctx context.Context, enterprise string, createReq NetworkConfigurationRequest) (*NetworkConfiguration, *Response, error) { + if err := validateNetworkConfigurationRequest(createReq); err != nil { + return nil, nil, fmt.Errorf("validation failed: %w", err) + } + + u := fmt.Sprintf("enterprises/%v/network-configurations", enterprise) + req, err := s.client.NewRequest("POST", u, createReq) + if err != nil { + return nil, nil, err + } + + network := &NetworkConfiguration{} + resp, err := s.client.Do(ctx, req, network) + if err != nil { + return nil, resp, err + } + + return network, resp, nil +} + +// GetEnterpriseNetworkConfiguration gets a hosted compute network configuration configured in an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/network-configurations#get-a-hosted-compute-network-configuration-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/network-configurations/{network_configuration_id} +func (s *EnterpriseService) GetEnterpriseNetworkConfiguration(ctx context.Context, enterprise, networkID string) (*NetworkConfiguration, *Response, error) { + u := fmt.Sprintf("enterprises/%v/network-configurations/%v", enterprise, networkID) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + network := &NetworkConfiguration{} + resp, err := s.client.Do(ctx, req, network) + if err != nil { + return nil, resp, err + } + return network, resp, nil +} + +// UpdateEnterpriseNetworkConfiguration updates a hosted compute network configuration for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/network-configurations#update-a-hosted-compute-network-configuration-for-an-enterprise +// +//meta:operation PATCH /enterprises/{enterprise}/network-configurations/{network_configuration_id} +func (s *EnterpriseService) UpdateEnterpriseNetworkConfiguration(ctx context.Context, enterprise, networkID string, updateReq NetworkConfigurationRequest) (*NetworkConfiguration, *Response, error) { + if err := validateNetworkConfigurationRequest(updateReq); err != nil { + return nil, nil, fmt.Errorf("validation failed: %w", err) + } + + u := fmt.Sprintf("enterprises/%v/network-configurations/%v", enterprise, networkID) + req, err := s.client.NewRequest("PATCH", u, updateReq) + if err != nil { + return nil, nil, err + } + + network := &NetworkConfiguration{} + resp, err := s.client.Do(ctx, req, network) + if err != nil { + return nil, resp, err + } + return network, resp, nil +} + +// DeleteEnterpriseNetworkConfiguration deletes a hosted compute network configuration from an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/network-configurations#delete-a-hosted-compute-network-configuration-from-an-enterprise +// +//meta:operation DELETE /enterprises/{enterprise}/network-configurations/{network_configuration_id} +func (s *EnterpriseService) DeleteEnterpriseNetworkConfiguration(ctx context.Context, enterprise, networkID string) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/network-configurations/%v", enterprise, networkID) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + return s.client.Do(ctx, req, nil) +} + +// GetEnterpriseNetworkSettingsResource gets a hosted compute network settings resource configured for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/network-configurations#get-a-hosted-compute-network-settings-resource-for-an-enterprise +// +//meta:operation GET /enterprises/{enterprise}/network-settings/{network_settings_id} +func (s *EnterpriseService) GetEnterpriseNetworkSettingsResource(ctx context.Context, enterprise, networkID string) (*NetworkSettingsResource, *Response, error) { + u := fmt.Sprintf("enterprises/%v/network-settings/%v", enterprise, networkID) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + resource := &NetworkSettingsResource{} + resp, err := s.client.Do(ctx, req, resource) + if err != nil { + return nil, resp, err + } + return resource, resp, err +} diff --git a/vendor/github.com/google/go-github/v68/github/enterprise_properties.go b/vendor/github.com/google/go-github/v70/github/enterprise_properties.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/enterprise_properties.go rename to vendor/github.com/google/go-github/v70/github/enterprise_properties.go diff --git a/vendor/github.com/google/go-github/v70/github/enterprise_rules.go b/vendor/github.com/google/go-github/v70/github/enterprise_rules.go new file mode 100644 index 0000000000..f438223370 --- /dev/null +++ b/vendor/github.com/google/go-github/v70/github/enterprise_rules.go @@ -0,0 +1,118 @@ +// Copyright 2025 The go-github 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 github + +import ( + "context" + "fmt" +) + +// CreateRepositoryRuleset creates a repository ruleset for the specified enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/rules#create-an-enterprise-repository-ruleset +// +//meta:operation POST /enterprises/{enterprise}/rulesets +func (s *EnterpriseService) CreateRepositoryRuleset(ctx context.Context, enterprise string, ruleset RepositoryRuleset) (*RepositoryRuleset, *Response, error) { + u := fmt.Sprintf("enterprises/%v/rulesets", enterprise) + + req, err := s.client.NewRequest("POST", u, ruleset) + if err != nil { + return nil, nil, err + } + + var rs *RepositoryRuleset + resp, err := s.client.Do(ctx, req, &rs) + if err != nil { + return nil, resp, err + } + + return rs, resp, nil +} + +// GetRepositoryRuleset gets a repository ruleset for the specified enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/rules#get-an-enterprise-repository-ruleset +// +//meta:operation GET /enterprises/{enterprise}/rulesets/{ruleset_id} +func (s *EnterpriseService) GetRepositoryRuleset(ctx context.Context, enterprise string, rulesetID int64) (*RepositoryRuleset, *Response, error) { + u := fmt.Sprintf("enterprises/%v/rulesets/%v", enterprise, rulesetID) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var ruleset *RepositoryRuleset + resp, err := s.client.Do(ctx, req, &ruleset) + if err != nil { + return nil, resp, err + } + + return ruleset, resp, nil +} + +// UpdateRepositoryRuleset updates a repository ruleset for the specified enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/rules#update-an-enterprise-repository-ruleset +// +//meta:operation PUT /enterprises/{enterprise}/rulesets/{ruleset_id} +func (s *EnterpriseService) UpdateRepositoryRuleset(ctx context.Context, enterprise string, rulesetID int64, ruleset RepositoryRuleset) (*RepositoryRuleset, *Response, error) { + u := fmt.Sprintf("enterprises/%v/rulesets/%v", enterprise, rulesetID) + + req, err := s.client.NewRequest("PUT", u, ruleset) + if err != nil { + return nil, nil, err + } + + var rs *RepositoryRuleset + resp, err := s.client.Do(ctx, req, &rs) + if err != nil { + return nil, resp, err + } + + return rs, resp, nil +} + +// UpdateRepositoryRulesetClearBypassActor clears the bypass actors for a repository ruleset for the specified enterprise. +// +// This function is necessary as the UpdateRepositoryRuleset function does not marshal ByPassActor if passed as an empty array. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/rules#update-an-enterprise-repository-ruleset +// +//meta:operation PUT /enterprises/{enterprise}/rulesets/{ruleset_id} +func (s *EnterpriseService) UpdateRepositoryRulesetClearBypassActor(ctx context.Context, enterprise string, rulesetID int64) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/rulesets/%v", enterprise, rulesetID) + + rsClearBypassActor := rulesetClearBypassActors{} + + req, err := s.client.NewRequest("PUT", u, rsClearBypassActor) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(ctx, req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// DeleteRepositoryRuleset deletes a repository ruleset from the specified enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/rules#delete-an-enterprise-repository-ruleset +// +//meta:operation DELETE /enterprises/{enterprise}/rulesets/{ruleset_id} +func (s *EnterpriseService) DeleteRepositoryRuleset(ctx context.Context, enterprise string, rulesetID int64) (*Response, error) { + u := fmt.Sprintf("enterprises/%v/rulesets/%v", enterprise, rulesetID) + + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/v68/github/event.go b/vendor/github.com/google/go-github/v70/github/event.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/event.go rename to vendor/github.com/google/go-github/v70/github/event.go diff --git a/vendor/github.com/google/go-github/v68/github/event_types.go b/vendor/github.com/google/go-github/v70/github/event_types.go similarity index 96% rename from vendor/github.com/google/go-github/v68/github/event_types.go rename to vendor/github.com/google/go-github/v70/github/event_types.go index 37e62c2fab..6a6caf19dd 100644 --- a/vendor/github.com/google/go-github/v68/github/event_types.go +++ b/vendor/github.com/google/go-github/v70/github/event_types.go @@ -858,8 +858,10 @@ type MergeGroup struct { // // GitHub API docs: https://docs.github.com/developers/webhooks-and-events/webhook-events-and-payloads#merge_group type MergeGroupEvent struct { - // The action that was performed. Currently, can only be checks_requested. + // The action that was performed. Possible values are: "checks_requested", "destroyed". Action *string `json:"action,omitempty"` + // Reason is populated when the action is "destroyed". Possible values: "merged", "invalidated", "dequeued". + Reason *string `json:"reason,omitempty"` // The merge group. MergeGroup *MergeGroup `json:"merge_group,omitempty"` @@ -1521,14 +1523,73 @@ type RepositoryImportEvent struct { // // GitHub API docs: https://docs.github.com/en/webhooks/webhook-events-and-payloads#repository_ruleset type RepositoryRulesetEvent struct { - Action *string `json:"action,omitempty"` - Enterprise *Enterprise `json:"enterprise,omitempty"` - Installation *Installation `json:"installation,omitempty"` - Organization *Organization `json:"organization,omitempty"` - Repository *Repository `json:"repository,omitempty"` - RepositoryRuleset *RepositoryRuleset `json:"repository_ruleset"` - Changes *RepositoryRulesetEditedChanges `json:"changes,omitempty"` - Sender *User `json:"sender"` + Action *string `json:"action,omitempty"` + Enterprise *Enterprise `json:"enterprise,omitempty"` + Installation *Installation `json:"installation,omitempty"` + Organization *Organization `json:"organization,omitempty"` + Repository *Repository `json:"repository,omitempty"` + RepositoryRuleset *RepositoryRuleset `json:"repository_ruleset"` + Changes *RepositoryRulesetChanges `json:"changes,omitempty"` + Sender *User `json:"sender"` +} + +// RepositoryRulesetChanges represents the changes made to a repository ruleset. +type RepositoryRulesetChanges struct { + Name *RepositoryRulesetChangeSource `json:"name,omitempty"` + Enforcement *RepositoryRulesetChangeSource `json:"enforcement,omitempty"` + Conditions *RepositoryRulesetChangedConditions `json:"conditions,omitempty"` + Rules *RepositoryRulesetChangedRules `json:"rules,omitempty"` +} + +// RepositoryRulesetChangeSource represents a source change for the ruleset. +type RepositoryRulesetChangeSource struct { + From *string `json:"from,omitempty"` +} + +// RepositoryRulesetChangeSources represents multiple source changes for the ruleset. +type RepositoryRulesetChangeSources struct { + From []string `json:"from,omitempty"` +} + +// RepositoryRulesetChangedConditions holds changes to conditions in a ruleset. +type RepositoryRulesetChangedConditions struct { + Added []*RepositoryRulesetConditions `json:"added,omitempty"` + Deleted []*RepositoryRulesetConditions `json:"deleted,omitempty"` + Updated []*RepositoryRulesetUpdatedConditions `json:"updated,omitempty"` +} + +// RepositoryRulesetUpdatedConditions represents the edited updates to conditions in a ruleset. +type RepositoryRulesetUpdatedConditions struct { + Condition *RepositoryRulesetConditions `json:"condition,omitempty"` + Changes *RepositoryRulesetUpdatedCondition `json:"changes,omitempty"` +} + +// RepositoryRulesetUpdatedCondition represents the changes to a condition in a ruleset. +type RepositoryRulesetUpdatedCondition struct { + ConditionType *RepositoryRulesetChangeSource `json:"condition_type,omitempty"` + Target *RepositoryRulesetChangeSource `json:"target,omitempty"` + Include *RepositoryRulesetChangeSources `json:"include,omitempty"` + Exclude *RepositoryRulesetChangeSources `json:"exclude,omitempty"` +} + +// RepositoryRulesetChangedRules holds changes to rules in a ruleset. +type RepositoryRulesetChangedRules struct { + Added []*RepositoryRule `json:"added,omitempty"` + Deleted []*RepositoryRule `json:"deleted,omitempty"` + Updated []*RepositoryRulesetUpdatedRules `json:"updated,omitempty"` +} + +// RepositoryRulesetUpdatedRules holds updates to rules in a ruleset. +type RepositoryRulesetUpdatedRules struct { + Rule *RepositoryRule `json:"rule,omitempty"` + Changes *RepositoryRulesetChangedRule `json:"changes,omitempty"` +} + +// RepositoryRulesetChangedRule holds changes made to a rule in a ruleset. +type RepositoryRulesetChangedRule struct { + Configuration *RepositoryRulesetChangeSource `json:"configuration,omitempty"` + RuleType *RepositoryRulesetChangeSource `json:"rule_type,omitempty"` + Pattern *RepositoryRulesetChangeSource `json:"pattern,omitempty"` } // RepositoryVulnerabilityAlertEvent is triggered when a security alert is created, dismissed, or resolved. diff --git a/vendor/github.com/google/go-github/v68/github/gists.go b/vendor/github.com/google/go-github/v70/github/gists.go similarity index 99% rename from vendor/github.com/google/go-github/v68/github/gists.go rename to vendor/github.com/google/go-github/v70/github/gists.go index 08180c6d30..ee4314b986 100644 --- a/vendor/github.com/google/go-github/v68/github/gists.go +++ b/vendor/github.com/google/go-github/v70/github/gists.go @@ -1,6 +1,6 @@ // Copyright 2013 The go-github AUTHORS. All rights reserved. // -// Use of this source code is governed by BSD-style +// Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package github diff --git a/vendor/github.com/google/go-github/v68/github/gists_comments.go b/vendor/github.com/google/go-github/v70/github/gists_comments.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/gists_comments.go rename to vendor/github.com/google/go-github/v70/github/gists_comments.go diff --git a/vendor/github.com/google/go-github/v68/github/git.go b/vendor/github.com/google/go-github/v70/github/git.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/git.go rename to vendor/github.com/google/go-github/v70/github/git.go diff --git a/vendor/github.com/google/go-github/v68/github/git_blobs.go b/vendor/github.com/google/go-github/v70/github/git_blobs.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/git_blobs.go rename to vendor/github.com/google/go-github/v70/github/git_blobs.go diff --git a/vendor/github.com/google/go-github/v68/github/git_commits.go b/vendor/github.com/google/go-github/v70/github/git_commits.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/git_commits.go rename to vendor/github.com/google/go-github/v70/github/git_commits.go diff --git a/vendor/github.com/google/go-github/v68/github/git_refs.go b/vendor/github.com/google/go-github/v70/github/git_refs.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/git_refs.go rename to vendor/github.com/google/go-github/v70/github/git_refs.go diff --git a/vendor/github.com/google/go-github/v68/github/git_tags.go b/vendor/github.com/google/go-github/v70/github/git_tags.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/git_tags.go rename to vendor/github.com/google/go-github/v70/github/git_tags.go diff --git a/vendor/github.com/google/go-github/v68/github/git_trees.go b/vendor/github.com/google/go-github/v70/github/git_trees.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/git_trees.go rename to vendor/github.com/google/go-github/v70/github/git_trees.go diff --git a/vendor/github.com/google/go-github/v68/github/github-accessors.go b/vendor/github.com/google/go-github/v70/github/github-accessors.go similarity index 90% rename from vendor/github.com/google/go-github/v68/github/github-accessors.go rename to vendor/github.com/google/go-github/v70/github/github-accessors.go index 0800c02c6c..ec029df241 100644 --- a/vendor/github.com/google/go-github/v68/github/github-accessors.go +++ b/vendor/github.com/google/go-github/v70/github/github-accessors.go @@ -1934,20 +1934,20 @@ func (b *BypassActor) GetActorID() int64 { return *b.ActorID } -// GetActorType returns the ActorType field if it's non-nil, zero value otherwise. -func (b *BypassActor) GetActorType() string { - if b == nil || b.ActorType == nil { - return "" +// GetActorType returns the ActorType field. +func (b *BypassActor) GetActorType() *BypassActorType { + if b == nil { + return nil } - return *b.ActorType + return b.ActorType } -// GetBypassMode returns the BypassMode field if it's non-nil, zero value otherwise. -func (b *BypassActor) GetBypassMode() string { - if b == nil || b.BypassMode == nil { - return "" +// GetBypassMode returns the BypassMode field. +func (b *BypassActor) GetBypassMode() *BypassMode { + if b == nil { + return nil } - return *b.BypassMode + return b.BypassMode } // GetApp returns the App field. @@ -2358,20 +2358,20 @@ func (c *CheckSuite) GetRepository() *Repository { return c.Repository } -// GetRerequstable returns the Rerequstable field if it's non-nil, zero value otherwise. -func (c *CheckSuite) GetRerequstable() bool { - if c == nil || c.Rerequstable == nil { +// GetRerequestable returns the Rerequestable field if it's non-nil, zero value otherwise. +func (c *CheckSuite) GetRerequestable() bool { + if c == nil || c.Rerequestable == nil { return false } - return *c.Rerequstable + return *c.Rerequestable } -// GetRunsRerequstable returns the RunsRerequstable field if it's non-nil, zero value otherwise. -func (c *CheckSuite) GetRunsRerequstable() bool { - if c == nil || c.RunsRerequstable == nil { +// GetRunsRerequestable returns the RunsRerequestable field if it's non-nil, zero value otherwise. +func (c *CheckSuite) GetRunsRerequestable() bool { + if c == nil || c.RunsRerequestable == nil { return false } - return *c.RunsRerequstable + return *c.RunsRerequestable } // GetStatus returns the Status field if it's non-nil, zero value otherwise. @@ -2462,6 +2462,70 @@ func (c *CheckSuitePreferenceResults) GetRepository() *Repository { return c.Repository } +// GetFingerprint returns the Fingerprint field if it's non-nil, zero value otherwise. +func (c *ClusterSSHKey) GetFingerprint() string { + if c == nil || c.Fingerprint == nil { + return "" + } + return *c.Fingerprint +} + +// GetKey returns the Key field if it's non-nil, zero value otherwise. +func (c *ClusterSSHKey) GetKey() string { + if c == nil || c.Key == nil { + return "" + } + return *c.Key +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (c *ClusterStatus) GetStatus() string { + if c == nil || c.Status == nil { + return "" + } + return *c.Status +} + +// GetHostname returns the Hostname field if it's non-nil, zero value otherwise. +func (c *ClusterStatusNode) GetHostname() string { + if c == nil || c.Hostname == nil { + return "" + } + return *c.Hostname +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (c *ClusterStatusNode) GetStatus() string { + if c == nil || c.Status == nil { + return "" + } + return *c.Status +} + +// GetDetails returns the Details field if it's non-nil, zero value otherwise. +func (c *ClusterStatusNodeServiceItem) GetDetails() string { + if c == nil || c.Details == nil { + return "" + } + return *c.Details +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *ClusterStatusNodeServiceItem) GetName() string { + if c == nil || c.Name == nil { + return "" + } + return *c.Name +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (c *ClusterStatusNodeServiceItem) GetStatus() string { + if c == nil || c.Status == nil { + return "" + } + return *c.Status +} + // GetBody returns the Body field if it's non-nil, zero value otherwise. func (c *CodeOfConduct) GetBody() string { if c == nil || c.Body == nil { @@ -4038,304 +4102,1400 @@ func (c *CommunityHealthMetrics) GetUpdatedAt() Timestamp { return *c.UpdatedAt } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (c *ContentReference) GetID() int64 { - if c == nil || c.ID == nil { - return 0 +// GetLastRequestID returns the LastRequestID field if it's non-nil, zero value otherwise. +func (c *ConfigApplyEventsNode) GetLastRequestID() string { + if c == nil || c.LastRequestID == nil { + return "" } - return *c.ID + return *c.LastRequestID } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (c *ContentReference) GetNodeID() string { - if c == nil || c.NodeID == nil { +// GetNode returns the Node field if it's non-nil, zero value otherwise. +func (c *ConfigApplyEventsNode) GetNode() string { + if c == nil || c.Node == nil { return "" } - return *c.NodeID + return *c.Node } -// GetReference returns the Reference field if it's non-nil, zero value otherwise. -func (c *ContentReference) GetReference() string { - if c == nil || c.Reference == nil { +// GetBody returns the Body field if it's non-nil, zero value otherwise. +func (c *ConfigApplyEventsNodeEvent) GetBody() string { + if c == nil || c.Body == nil { return "" } - return *c.Reference + return *c.Body } -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (c *ContentReferenceEvent) GetAction() string { - if c == nil || c.Action == nil { +// GetConfigRunID returns the ConfigRunID field if it's non-nil, zero value otherwise. +func (c *ConfigApplyEventsNodeEvent) GetConfigRunID() string { + if c == nil || c.ConfigRunID == nil { return "" } - return *c.Action + return *c.ConfigRunID } -// GetContentReference returns the ContentReference field. -func (c *ContentReferenceEvent) GetContentReference() *ContentReference { - if c == nil { - return nil +// GetEventName returns the EventName field if it's non-nil, zero value otherwise. +func (c *ConfigApplyEventsNodeEvent) GetEventName() string { + if c == nil || c.EventName == nil { + return "" } - return c.ContentReference + return *c.EventName } -// GetInstallation returns the Installation field. -func (c *ContentReferenceEvent) GetInstallation() *Installation { - if c == nil { - return nil +// GetHostname returns the Hostname field if it's non-nil, zero value otherwise. +func (c *ConfigApplyEventsNodeEvent) GetHostname() string { + if c == nil || c.Hostname == nil { + return "" } - return c.Installation + return *c.Hostname } -// GetRepo returns the Repo field. -func (c *ContentReferenceEvent) GetRepo() *Repository { - if c == nil { - return nil +// GetSeverityText returns the SeverityText field if it's non-nil, zero value otherwise. +func (c *ConfigApplyEventsNodeEvent) GetSeverityText() string { + if c == nil || c.SeverityText == nil { + return "" } - return c.Repo + return *c.SeverityText } -// GetSender returns the Sender field. -func (c *ContentReferenceEvent) GetSender() *User { - if c == nil { - return nil +// GetSpanDepth returns the SpanDepth field if it's non-nil, zero value otherwise. +func (c *ConfigApplyEventsNodeEvent) GetSpanDepth() int { + if c == nil || c.SpanDepth == nil { + return 0 } - return c.Sender + return *c.SpanDepth } -// GetAvatarURL returns the AvatarURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetAvatarURL() string { - if c == nil || c.AvatarURL == nil { +// GetSpanID returns the SpanID field if it's non-nil, zero value otherwise. +func (c *ConfigApplyEventsNodeEvent) GetSpanID() string { + if c == nil || c.SpanID == nil { return "" } - return *c.AvatarURL + return *c.SpanID } -// GetContributions returns the Contributions field if it's non-nil, zero value otherwise. -func (c *Contributor) GetContributions() int { - if c == nil || c.Contributions == nil { +// GetSpanParentID returns the SpanParentID field if it's non-nil, zero value otherwise. +func (c *ConfigApplyEventsNodeEvent) GetSpanParentID() int64 { + if c == nil || c.SpanParentID == nil { return 0 } - return *c.Contributions + return *c.SpanParentID } -// GetEmail returns the Email field if it's non-nil, zero value otherwise. -func (c *Contributor) GetEmail() string { - if c == nil || c.Email == nil { - return "" +// GetTimestamp returns the Timestamp field if it's non-nil, zero value otherwise. +func (c *ConfigApplyEventsNodeEvent) GetTimestamp() Timestamp { + if c == nil || c.Timestamp == nil { + return Timestamp{} } - return *c.Email + return *c.Timestamp } -// GetEventsURL returns the EventsURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetEventsURL() string { - if c == nil || c.EventsURL == nil { +// GetTopology returns the Topology field if it's non-nil, zero value otherwise. +func (c *ConfigApplyEventsNodeEvent) GetTopology() string { + if c == nil || c.Topology == nil { return "" } - return *c.EventsURL + return *c.Topology } -// GetFollowersURL returns the FollowersURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetFollowersURL() string { - if c == nil || c.FollowersURL == nil { +// GetTraceID returns the TraceID field if it's non-nil, zero value otherwise. +func (c *ConfigApplyEventsNodeEvent) GetTraceID() string { + if c == nil || c.TraceID == nil { return "" } - return *c.FollowersURL + return *c.TraceID } -// GetFollowingURL returns the FollowingURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetFollowingURL() string { - if c == nil || c.FollowingURL == nil { +// GetLastRequestID returns the LastRequestID field if it's non-nil, zero value otherwise. +func (c *ConfigApplyEventsOptions) GetLastRequestID() string { + if c == nil || c.LastRequestID == nil { return "" } - return *c.FollowingURL + return *c.LastRequestID } -// GetGistsURL returns the GistsURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetGistsURL() string { - if c == nil || c.GistsURL == nil { +// GetRunID returns the RunID field if it's non-nil, zero value otherwise. +func (c *ConfigApplyOptions) GetRunID() string { + if c == nil || c.RunID == nil { return "" } - return *c.GistsURL + return *c.RunID } -// GetGravatarID returns the GravatarID field if it's non-nil, zero value otherwise. -func (c *Contributor) GetGravatarID() string { - if c == nil || c.GravatarID == nil { - return "" +// GetRunning returns the Running field if it's non-nil, zero value otherwise. +func (c *ConfigApplyStatus) GetRunning() bool { + if c == nil || c.Running == nil { + return false } - return *c.GravatarID + return *c.Running } -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetHTMLURL() string { - if c == nil || c.HTMLURL == nil { - return "" +// GetSuccessful returns the Successful field if it's non-nil, zero value otherwise. +func (c *ConfigApplyStatus) GetSuccessful() bool { + if c == nil || c.Successful == nil { + return false } - return *c.HTMLURL + return *c.Successful } -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (c *Contributor) GetID() int64 { - if c == nil || c.ID == nil { - return 0 +// GetHostname returns the Hostname field if it's non-nil, zero value otherwise. +func (c *ConfigApplyStatusNode) GetHostname() string { + if c == nil || c.Hostname == nil { + return "" } - return *c.ID + return *c.Hostname } -// GetLogin returns the Login field if it's non-nil, zero value otherwise. -func (c *Contributor) GetLogin() string { - if c == nil || c.Login == nil { +// GetRunID returns the RunID field if it's non-nil, zero value otherwise. +func (c *ConfigApplyStatusNode) GetRunID() string { + if c == nil || c.RunID == nil { return "" } - return *c.Login + return *c.RunID } -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (c *Contributor) GetName() string { - if c == nil || c.Name == nil { - return "" +// GetRunning returns the Running field if it's non-nil, zero value otherwise. +func (c *ConfigApplyStatusNode) GetRunning() bool { + if c == nil || c.Running == nil { + return false } - return *c.Name + return *c.Running } -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (c *Contributor) GetNodeID() string { - if c == nil || c.NodeID == nil { - return "" +// GetSuccessful returns the Successful field if it's non-nil, zero value otherwise. +func (c *ConfigApplyStatusNode) GetSuccessful() bool { + if c == nil || c.Successful == nil { + return false } - return *c.NodeID + return *c.Successful } -// GetOrganizationsURL returns the OrganizationsURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetOrganizationsURL() string { - if c == nil || c.OrganizationsURL == nil { +// GetAdminPassword returns the AdminPassword field if it's non-nil, zero value otherwise. +func (c *ConfigSettings) GetAdminPassword() string { + if c == nil || c.AdminPassword == nil { return "" } - return *c.OrganizationsURL + return *c.AdminPassword } -// GetReceivedEventsURL returns the ReceivedEventsURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetReceivedEventsURL() string { - if c == nil || c.ReceivedEventsURL == nil { +// GetAssets returns the Assets field if it's non-nil, zero value otherwise. +func (c *ConfigSettings) GetAssets() string { + if c == nil || c.Assets == nil { return "" } - return *c.ReceivedEventsURL + return *c.Assets } -// GetReposURL returns the ReposURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetReposURL() string { - if c == nil || c.ReposURL == nil { +// GetAuthMode returns the AuthMode field if it's non-nil, zero value otherwise. +func (c *ConfigSettings) GetAuthMode() string { + if c == nil || c.AuthMode == nil { return "" } - return *c.ReposURL + return *c.AuthMode } -// GetSiteAdmin returns the SiteAdmin field if it's non-nil, zero value otherwise. -func (c *Contributor) GetSiteAdmin() bool { - if c == nil || c.SiteAdmin == nil { - return false +// GetAvatar returns the Avatar field. +func (c *ConfigSettings) GetAvatar() *ConfigSettingsAvatar { + if c == nil { + return nil } - return *c.SiteAdmin + return c.Avatar } -// GetStarredURL returns the StarredURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetStarredURL() string { - if c == nil || c.StarredURL == nil { - return "" +// GetCAS returns the CAS field. +func (c *ConfigSettings) GetCAS() *ConfigSettingsCAS { + if c == nil { + return nil } - return *c.StarredURL + return c.CAS } -// GetSubscriptionsURL returns the SubscriptionsURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetSubscriptionsURL() string { - if c == nil || c.SubscriptionsURL == nil { - return "" +// GetCollectd returns the Collectd field. +func (c *ConfigSettings) GetCollectd() *ConfigSettingsCollectd { + if c == nil { + return nil } - return *c.SubscriptionsURL + return c.Collectd } -// GetType returns the Type field if it's non-nil, zero value otherwise. -func (c *Contributor) GetType() string { - if c == nil || c.Type == nil { - return "" +// GetConfigurationID returns the ConfigurationID field if it's non-nil, zero value otherwise. +func (c *ConfigSettings) GetConfigurationID() int64 { + if c == nil || c.ConfigurationID == nil { + return 0 } - return *c.Type + return *c.ConfigurationID } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetURL() string { - if c == nil || c.URL == nil { - return "" +// GetConfigurationRunCount returns the ConfigurationRunCount field if it's non-nil, zero value otherwise. +func (c *ConfigSettings) GetConfigurationRunCount() int { + if c == nil || c.ConfigurationRunCount == nil { + return 0 } - return *c.URL + return *c.ConfigurationRunCount } -// GetAuthor returns the Author field. -func (c *ContributorStats) GetAuthor() *Contributor { +// GetCustomer returns the Customer field. +func (c *ConfigSettings) GetCustomer() *ConfigSettingsCustomer { if c == nil { return nil } - return c.Author + return c.Customer } -// GetTotal returns the Total field if it's non-nil, zero value otherwise. -func (c *ContributorStats) GetTotal() int { - if c == nil || c.Total == nil { - return 0 +// GetExpireSessions returns the ExpireSessions field if it's non-nil, zero value otherwise. +func (c *ConfigSettings) GetExpireSessions() bool { + if c == nil || c.ExpireSessions == nil { + return false } - return *c.Total + return *c.ExpireSessions } -// GetCustomModelTrainingDate returns the CustomModelTrainingDate field if it's non-nil, zero value otherwise. -func (c *CopilotDotcomChatModel) GetCustomModelTrainingDate() string { - if c == nil || c.CustomModelTrainingDate == nil { +// GetGithubHostname returns the GithubHostname field if it's non-nil, zero value otherwise. +func (c *ConfigSettings) GetGithubHostname() string { + if c == nil || c.GithubHostname == nil { return "" } - return *c.CustomModelTrainingDate + return *c.GithubHostname } -// GetCustomModelTrainingDate returns the CustomModelTrainingDate field if it's non-nil, zero value otherwise. -func (c *CopilotDotcomPullRequestsModel) GetCustomModelTrainingDate() string { - if c == nil || c.CustomModelTrainingDate == nil { - return "" +// GetGithubOAuth returns the GithubOAuth field. +func (c *ConfigSettings) GetGithubOAuth() *ConfigSettingsGithubOAuth { + if c == nil { + return nil } - return *c.CustomModelTrainingDate + return c.GithubOAuth } -// GetCustomModelTrainingDate returns the CustomModelTrainingDate field if it's non-nil, zero value otherwise. -func (c *CopilotIDEChatModel) GetCustomModelTrainingDate() string { - if c == nil || c.CustomModelTrainingDate == nil { - return "" +// GetGithubSSL returns the GithubSSL field. +func (c *ConfigSettings) GetGithubSSL() *ConfigSettingsGithubSSL { + if c == nil { + return nil } - return *c.CustomModelTrainingDate + return c.GithubSSL } -// GetCustomModelTrainingDate returns the CustomModelTrainingDate field if it's non-nil, zero value otherwise. -func (c *CopilotIDECodeCompletionsModel) GetCustomModelTrainingDate() string { - if c == nil || c.CustomModelTrainingDate == nil { +// GetHTTPProxy returns the HTTPProxy field if it's non-nil, zero value otherwise. +func (c *ConfigSettings) GetHTTPProxy() string { + if c == nil || c.HTTPProxy == nil { return "" } - return *c.CustomModelTrainingDate + return *c.HTTPProxy } -// GetCopilotDotcomChat returns the CopilotDotcomChat field. -func (c *CopilotMetrics) GetCopilotDotcomChat() *CopilotDotcomChat { - if c == nil { - return nil +// GetIdenticonsHost returns the IdenticonsHost field if it's non-nil, zero value otherwise. +func (c *ConfigSettings) GetIdenticonsHost() string { + if c == nil || c.IdenticonsHost == nil { + return "" } - return c.CopilotDotcomChat + return *c.IdenticonsHost } -// GetCopilotDotcomPullRequests returns the CopilotDotcomPullRequests field. -func (c *CopilotMetrics) GetCopilotDotcomPullRequests() *CopilotDotcomPullRequests { +// GetLDAP returns the LDAP field. +func (c *ConfigSettings) GetLDAP() *ConfigSettingsLDAP { if c == nil { return nil } - return c.CopilotDotcomPullRequests + return c.LDAP } -// GetCopilotIDEChat returns the CopilotIDEChat field. -func (c *CopilotMetrics) GetCopilotIDEChat() *CopilotIDEChat { +// GetLicense returns the License field. +func (c *ConfigSettings) GetLicense() *ConfigSettingsLicenseSettings { + if c == nil { + return nil + } + return c.License +} + +// GetLoadBalancer returns the LoadBalancer field if it's non-nil, zero value otherwise. +func (c *ConfigSettings) GetLoadBalancer() string { + if c == nil || c.LoadBalancer == nil { + return "" + } + return *c.LoadBalancer +} + +// GetMapping returns the Mapping field. +func (c *ConfigSettings) GetMapping() *ConfigSettingsMapping { + if c == nil { + return nil + } + return c.Mapping +} + +// GetNTP returns the NTP field. +func (c *ConfigSettings) GetNTP() *ConfigSettingsNTP { + if c == nil { + return nil + } + return c.NTP +} + +// GetPages returns the Pages field. +func (c *ConfigSettings) GetPages() *ConfigSettingsPagesSettings { + if c == nil { + return nil + } + return c.Pages +} + +// GetPrivateMode returns the PrivateMode field if it's non-nil, zero value otherwise. +func (c *ConfigSettings) GetPrivateMode() bool { + if c == nil || c.PrivateMode == nil { + return false + } + return *c.PrivateMode +} + +// GetPublicPages returns the PublicPages field if it's non-nil, zero value otherwise. +func (c *ConfigSettings) GetPublicPages() bool { + if c == nil || c.PublicPages == nil { + return false + } + return *c.PublicPages +} + +// GetSAML returns the SAML field. +func (c *ConfigSettings) GetSAML() *ConfigSettingsSAML { + if c == nil { + return nil + } + return c.SAML +} + +// GetSignupEnabled returns the SignupEnabled field if it's non-nil, zero value otherwise. +func (c *ConfigSettings) GetSignupEnabled() bool { + if c == nil || c.SignupEnabled == nil { + return false + } + return *c.SignupEnabled +} + +// GetSMTP returns the SMTP field. +func (c *ConfigSettings) GetSMTP() *ConfigSettingsSMTP { + if c == nil { + return nil + } + return c.SMTP +} + +// GetSNMP returns the SNMP field. +func (c *ConfigSettings) GetSNMP() *ConfigSettingsSNMP { + if c == nil { + return nil + } + return c.SNMP +} + +// GetSubdomainIsolation returns the SubdomainIsolation field if it's non-nil, zero value otherwise. +func (c *ConfigSettings) GetSubdomainIsolation() bool { + if c == nil || c.SubdomainIsolation == nil { + return false + } + return *c.SubdomainIsolation +} + +// GetSyslog returns the Syslog field. +func (c *ConfigSettings) GetSyslog() *ConfigSettingsSyslog { + if c == nil { + return nil + } + return c.Syslog +} + +// GetTimezone returns the Timezone field if it's non-nil, zero value otherwise. +func (c *ConfigSettings) GetTimezone() string { + if c == nil || c.Timezone == nil { + return "" + } + return *c.Timezone +} + +// GetEnabled returns the Enabled field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsAvatar) GetEnabled() bool { + if c == nil || c.Enabled == nil { + return false + } + return *c.Enabled +} + +// GetURI returns the URI field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsAvatar) GetURI() string { + if c == nil || c.URI == nil { + return "" + } + return *c.URI +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsCAS) GetURL() string { + if c == nil || c.URL == nil { + return "" + } + return *c.URL +} + +// GetEnabled returns the Enabled field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsCollectd) GetEnabled() bool { + if c == nil || c.Enabled == nil { + return false + } + return *c.Enabled +} + +// GetEncryption returns the Encryption field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsCollectd) GetEncryption() string { + if c == nil || c.Encryption == nil { + return "" + } + return *c.Encryption +} + +// GetPassword returns the Password field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsCollectd) GetPassword() string { + if c == nil || c.Password == nil { + return "" + } + return *c.Password +} + +// GetPort returns the Port field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsCollectd) GetPort() int { + if c == nil || c.Port == nil { + return 0 + } + return *c.Port +} + +// GetServer returns the Server field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsCollectd) GetServer() string { + if c == nil || c.Server == nil { + return "" + } + return *c.Server +} + +// GetUsername returns the Username field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsCollectd) GetUsername() string { + if c == nil || c.Username == nil { + return "" + } + return *c.Username +} + +// GetEmail returns the Email field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsCustomer) GetEmail() string { + if c == nil || c.Email == nil { + return "" + } + return *c.Email +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsCustomer) GetName() string { + if c == nil || c.Name == nil { + return "" + } + return *c.Name +} + +// GetPublicKeyData returns the PublicKeyData field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsCustomer) GetPublicKeyData() string { + if c == nil || c.PublicKeyData == nil { + return "" + } + return *c.PublicKeyData +} + +// GetSecret returns the Secret field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsCustomer) GetSecret() string { + if c == nil || c.Secret == nil { + return "" + } + return *c.Secret +} + +// GetUUID returns the UUID field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsCustomer) GetUUID() string { + if c == nil || c.UUID == nil { + return "" + } + return *c.UUID +} + +// GetClientID returns the ClientID field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsGithubOAuth) GetClientID() string { + if c == nil || c.ClientID == nil { + return "" + } + return *c.ClientID +} + +// GetClientSecret returns the ClientSecret field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsGithubOAuth) GetClientSecret() string { + if c == nil || c.ClientSecret == nil { + return "" + } + return *c.ClientSecret +} + +// GetOrganizationName returns the OrganizationName field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsGithubOAuth) GetOrganizationName() string { + if c == nil || c.OrganizationName == nil { + return "" + } + return *c.OrganizationName +} + +// GetOrganizationTeam returns the OrganizationTeam field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsGithubOAuth) GetOrganizationTeam() string { + if c == nil || c.OrganizationTeam == nil { + return "" + } + return *c.OrganizationTeam +} + +// GetCert returns the Cert field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsGithubSSL) GetCert() string { + if c == nil || c.Cert == nil { + return "" + } + return *c.Cert +} + +// GetEnabled returns the Enabled field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsGithubSSL) GetEnabled() bool { + if c == nil || c.Enabled == nil { + return false + } + return *c.Enabled +} + +// GetKey returns the Key field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsGithubSSL) GetKey() string { + if c == nil || c.Key == nil { + return "" + } + return *c.Key +} + +// GetAdminGroup returns the AdminGroup field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetAdminGroup() string { + if c == nil || c.AdminGroup == nil { + return "" + } + return *c.AdminGroup +} + +// GetBindDN returns the BindDN field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetBindDN() string { + if c == nil || c.BindDN == nil { + return "" + } + return *c.BindDN +} + +// GetHost returns the Host field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetHost() string { + if c == nil || c.Host == nil { + return "" + } + return *c.Host +} + +// GetMethod returns the Method field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetMethod() string { + if c == nil || c.Method == nil { + return "" + } + return *c.Method +} + +// GetPassword returns the Password field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetPassword() string { + if c == nil || c.Password == nil { + return "" + } + return *c.Password +} + +// GetPort returns the Port field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetPort() int { + if c == nil || c.Port == nil { + return 0 + } + return *c.Port +} + +// GetPosixSupport returns the PosixSupport field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetPosixSupport() bool { + if c == nil || c.PosixSupport == nil { + return false + } + return *c.PosixSupport +} + +// GetProfile returns the Profile field. +func (c *ConfigSettingsLDAP) GetProfile() *ConfigSettingsLDAPProfile { + if c == nil { + return nil + } + return c.Profile +} + +// GetReconciliation returns the Reconciliation field. +func (c *ConfigSettingsLDAP) GetReconciliation() *ConfigSettingsLDAPReconciliation { + if c == nil { + return nil + } + return c.Reconciliation +} + +// GetRecursiveGroupSearch returns the RecursiveGroupSearch field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetRecursiveGroupSearch() bool { + if c == nil || c.RecursiveGroupSearch == nil { + return false + } + return *c.RecursiveGroupSearch +} + +// GetSearchStrategy returns the SearchStrategy field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetSearchStrategy() string { + if c == nil || c.SearchStrategy == nil { + return "" + } + return *c.SearchStrategy +} + +// GetSyncEnabled returns the SyncEnabled field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetSyncEnabled() bool { + if c == nil || c.SyncEnabled == nil { + return false + } + return *c.SyncEnabled +} + +// GetTeamSyncInterval returns the TeamSyncInterval field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetTeamSyncInterval() int { + if c == nil || c.TeamSyncInterval == nil { + return 0 + } + return *c.TeamSyncInterval +} + +// GetUID returns the UID field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetUID() string { + if c == nil || c.UID == nil { + return "" + } + return *c.UID +} + +// GetUserSyncEmails returns the UserSyncEmails field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetUserSyncEmails() bool { + if c == nil || c.UserSyncEmails == nil { + return false + } + return *c.UserSyncEmails +} + +// GetUserSyncInterval returns the UserSyncInterval field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetUserSyncInterval() int { + if c == nil || c.UserSyncInterval == nil { + return 0 + } + return *c.UserSyncInterval +} + +// GetUserSyncKeys returns the UserSyncKeys field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetUserSyncKeys() bool { + if c == nil || c.UserSyncKeys == nil { + return false + } + return *c.UserSyncKeys +} + +// GetVirtualAttributeEnabled returns the VirtualAttributeEnabled field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAP) GetVirtualAttributeEnabled() bool { + if c == nil || c.VirtualAttributeEnabled == nil { + return false + } + return *c.VirtualAttributeEnabled +} + +// GetKey returns the Key field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAPProfile) GetKey() string { + if c == nil || c.Key == nil { + return "" + } + return *c.Key +} + +// GetMail returns the Mail field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAPProfile) GetMail() string { + if c == nil || c.Mail == nil { + return "" + } + return *c.Mail +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAPProfile) GetName() string { + if c == nil || c.Name == nil { + return "" + } + return *c.Name +} + +// GetUID returns the UID field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAPProfile) GetUID() string { + if c == nil || c.UID == nil { + return "" + } + return *c.UID +} + +// GetOrg returns the Org field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAPReconciliation) GetOrg() string { + if c == nil || c.Org == nil { + return "" + } + return *c.Org +} + +// GetUser returns the User field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLDAPReconciliation) GetUser() string { + if c == nil || c.User == nil { + return "" + } + return *c.User +} + +// GetClusterSupport returns the ClusterSupport field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLicenseSettings) GetClusterSupport() bool { + if c == nil || c.ClusterSupport == nil { + return false + } + return *c.ClusterSupport +} + +// GetEvaluation returns the Evaluation field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLicenseSettings) GetEvaluation() bool { + if c == nil || c.Evaluation == nil { + return false + } + return *c.Evaluation +} + +// GetExpireAt returns the ExpireAt field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLicenseSettings) GetExpireAt() Timestamp { + if c == nil || c.ExpireAt == nil { + return Timestamp{} + } + return *c.ExpireAt +} + +// GetPerpetual returns the Perpetual field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLicenseSettings) GetPerpetual() bool { + if c == nil || c.Perpetual == nil { + return false + } + return *c.Perpetual +} + +// GetSeats returns the Seats field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLicenseSettings) GetSeats() int { + if c == nil || c.Seats == nil { + return 0 + } + return *c.Seats +} + +// GetSSHAllowed returns the SSHAllowed field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLicenseSettings) GetSSHAllowed() bool { + if c == nil || c.SSHAllowed == nil { + return false + } + return *c.SSHAllowed +} + +// GetSupportKey returns the SupportKey field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLicenseSettings) GetSupportKey() string { + if c == nil || c.SupportKey == nil { + return "" + } + return *c.SupportKey +} + +// GetUnlimitedSeating returns the UnlimitedSeating field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsLicenseSettings) GetUnlimitedSeating() bool { + if c == nil || c.UnlimitedSeating == nil { + return false + } + return *c.UnlimitedSeating +} + +// GetBasemap returns the Basemap field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsMapping) GetBasemap() string { + if c == nil || c.Basemap == nil { + return "" + } + return *c.Basemap +} + +// GetEnabled returns the Enabled field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsMapping) GetEnabled() bool { + if c == nil || c.Enabled == nil { + return false + } + return *c.Enabled +} + +// GetTileserver returns the Tileserver field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsMapping) GetTileserver() string { + if c == nil || c.Tileserver == nil { + return "" + } + return *c.Tileserver +} + +// GetToken returns the Token field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsMapping) GetToken() string { + if c == nil || c.Token == nil { + return "" + } + return *c.Token +} + +// GetPrimaryServer returns the PrimaryServer field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsNTP) GetPrimaryServer() string { + if c == nil || c.PrimaryServer == nil { + return "" + } + return *c.PrimaryServer +} + +// GetSecondaryServer returns the SecondaryServer field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsNTP) GetSecondaryServer() string { + if c == nil || c.SecondaryServer == nil { + return "" + } + return *c.SecondaryServer +} + +// GetEnabled returns the Enabled field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsPagesSettings) GetEnabled() bool { + if c == nil || c.Enabled == nil { + return false + } + return *c.Enabled +} + +// GetCertificate returns the Certificate field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSAML) GetCertificate() string { + if c == nil || c.Certificate == nil { + return "" + } + return *c.Certificate +} + +// GetCertificatePath returns the CertificatePath field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSAML) GetCertificatePath() string { + if c == nil || c.CertificatePath == nil { + return "" + } + return *c.CertificatePath +} + +// GetDisableAdminDemote returns the DisableAdminDemote field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSAML) GetDisableAdminDemote() bool { + if c == nil || c.DisableAdminDemote == nil { + return false + } + return *c.DisableAdminDemote +} + +// GetIDPInitiatedSSO returns the IDPInitiatedSSO field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSAML) GetIDPInitiatedSSO() bool { + if c == nil || c.IDPInitiatedSSO == nil { + return false + } + return *c.IDPInitiatedSSO +} + +// GetIssuer returns the Issuer field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSAML) GetIssuer() string { + if c == nil || c.Issuer == nil { + return "" + } + return *c.Issuer +} + +// GetSSOURL returns the SSOURL field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSAML) GetSSOURL() string { + if c == nil || c.SSOURL == nil { + return "" + } + return *c.SSOURL +} + +// GetAddress returns the Address field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSMTP) GetAddress() string { + if c == nil || c.Address == nil { + return "" + } + return *c.Address +} + +// GetAuthentication returns the Authentication field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSMTP) GetAuthentication() string { + if c == nil || c.Authentication == nil { + return "" + } + return *c.Authentication +} + +// GetDiscardToNoreplyAddress returns the DiscardToNoreplyAddress field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSMTP) GetDiscardToNoreplyAddress() bool { + if c == nil || c.DiscardToNoreplyAddress == nil { + return false + } + return *c.DiscardToNoreplyAddress +} + +// GetDomain returns the Domain field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSMTP) GetDomain() string { + if c == nil || c.Domain == nil { + return "" + } + return *c.Domain +} + +// GetEnabled returns the Enabled field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSMTP) GetEnabled() bool { + if c == nil || c.Enabled == nil { + return false + } + return *c.Enabled +} + +// GetEnableStarttlsAuto returns the EnableStarttlsAuto field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSMTP) GetEnableStarttlsAuto() bool { + if c == nil || c.EnableStarttlsAuto == nil { + return false + } + return *c.EnableStarttlsAuto +} + +// GetNoreplyAddress returns the NoreplyAddress field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSMTP) GetNoreplyAddress() string { + if c == nil || c.NoreplyAddress == nil { + return "" + } + return *c.NoreplyAddress +} + +// GetPassword returns the Password field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSMTP) GetPassword() string { + if c == nil || c.Password == nil { + return "" + } + return *c.Password +} + +// GetPort returns the Port field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSMTP) GetPort() string { + if c == nil || c.Port == nil { + return "" + } + return *c.Port +} + +// GetSupportAddress returns the SupportAddress field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSMTP) GetSupportAddress() string { + if c == nil || c.SupportAddress == nil { + return "" + } + return *c.SupportAddress +} + +// GetSupportAddressType returns the SupportAddressType field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSMTP) GetSupportAddressType() string { + if c == nil || c.SupportAddressType == nil { + return "" + } + return *c.SupportAddressType +} + +// GetUsername returns the Username field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSMTP) GetUsername() string { + if c == nil || c.Username == nil { + return "" + } + return *c.Username +} + +// GetUserName returns the UserName field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSMTP) GetUserName() string { + if c == nil || c.UserName == nil { + return "" + } + return *c.UserName +} + +// GetCommunity returns the Community field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSNMP) GetCommunity() string { + if c == nil || c.Community == nil { + return "" + } + return *c.Community +} + +// GetEnabled returns the Enabled field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSNMP) GetEnabled() bool { + if c == nil || c.Enabled == nil { + return false + } + return *c.Enabled +} + +// GetEnabled returns the Enabled field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSyslog) GetEnabled() bool { + if c == nil || c.Enabled == nil { + return false + } + return *c.Enabled +} + +// GetProtocolName returns the ProtocolName field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSyslog) GetProtocolName() string { + if c == nil || c.ProtocolName == nil { + return "" + } + return *c.ProtocolName +} + +// GetServer returns the Server field if it's non-nil, zero value otherwise. +func (c *ConfigSettingsSyslog) GetServer() string { + if c == nil || c.Server == nil { + return "" + } + return *c.Server +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *ConnectionServiceItem) GetName() string { + if c == nil || c.Name == nil { + return "" + } + return *c.Name +} + +// GetNumber returns the Number field if it's non-nil, zero value otherwise. +func (c *ConnectionServiceItem) GetNumber() int { + if c == nil || c.Number == nil { + return 0 + } + return *c.Number +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (c *ContentReference) GetID() int64 { + if c == nil || c.ID == nil { + return 0 + } + return *c.ID +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (c *ContentReference) GetNodeID() string { + if c == nil || c.NodeID == nil { + return "" + } + return *c.NodeID +} + +// GetReference returns the Reference field if it's non-nil, zero value otherwise. +func (c *ContentReference) GetReference() string { + if c == nil || c.Reference == nil { + return "" + } + return *c.Reference +} + +// GetAction returns the Action field if it's non-nil, zero value otherwise. +func (c *ContentReferenceEvent) GetAction() string { + if c == nil || c.Action == nil { + return "" + } + return *c.Action +} + +// GetContentReference returns the ContentReference field. +func (c *ContentReferenceEvent) GetContentReference() *ContentReference { + if c == nil { + return nil + } + return c.ContentReference +} + +// GetInstallation returns the Installation field. +func (c *ContentReferenceEvent) GetInstallation() *Installation { + if c == nil { + return nil + } + return c.Installation +} + +// GetRepo returns the Repo field. +func (c *ContentReferenceEvent) GetRepo() *Repository { + if c == nil { + return nil + } + return c.Repo +} + +// GetSender returns the Sender field. +func (c *ContentReferenceEvent) GetSender() *User { + if c == nil { + return nil + } + return c.Sender +} + +// GetAvatarURL returns the AvatarURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetAvatarURL() string { + if c == nil || c.AvatarURL == nil { + return "" + } + return *c.AvatarURL +} + +// GetContributions returns the Contributions field if it's non-nil, zero value otherwise. +func (c *Contributor) GetContributions() int { + if c == nil || c.Contributions == nil { + return 0 + } + return *c.Contributions +} + +// GetEmail returns the Email field if it's non-nil, zero value otherwise. +func (c *Contributor) GetEmail() string { + if c == nil || c.Email == nil { + return "" + } + return *c.Email +} + +// GetEventsURL returns the EventsURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetEventsURL() string { + if c == nil || c.EventsURL == nil { + return "" + } + return *c.EventsURL +} + +// GetFollowersURL returns the FollowersURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetFollowersURL() string { + if c == nil || c.FollowersURL == nil { + return "" + } + return *c.FollowersURL +} + +// GetFollowingURL returns the FollowingURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetFollowingURL() string { + if c == nil || c.FollowingURL == nil { + return "" + } + return *c.FollowingURL +} + +// GetGistsURL returns the GistsURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetGistsURL() string { + if c == nil || c.GistsURL == nil { + return "" + } + return *c.GistsURL +} + +// GetGravatarID returns the GravatarID field if it's non-nil, zero value otherwise. +func (c *Contributor) GetGravatarID() string { + if c == nil || c.GravatarID == nil { + return "" + } + return *c.GravatarID +} + +// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetHTMLURL() string { + if c == nil || c.HTMLURL == nil { + return "" + } + return *c.HTMLURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (c *Contributor) GetID() int64 { + if c == nil || c.ID == nil { + return 0 + } + return *c.ID +} + +// GetLogin returns the Login field if it's non-nil, zero value otherwise. +func (c *Contributor) GetLogin() string { + if c == nil || c.Login == nil { + return "" + } + return *c.Login +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (c *Contributor) GetName() string { + if c == nil || c.Name == nil { + return "" + } + return *c.Name +} + +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (c *Contributor) GetNodeID() string { + if c == nil || c.NodeID == nil { + return "" + } + return *c.NodeID +} + +// GetOrganizationsURL returns the OrganizationsURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetOrganizationsURL() string { + if c == nil || c.OrganizationsURL == nil { + return "" + } + return *c.OrganizationsURL +} + +// GetReceivedEventsURL returns the ReceivedEventsURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetReceivedEventsURL() string { + if c == nil || c.ReceivedEventsURL == nil { + return "" + } + return *c.ReceivedEventsURL +} + +// GetReposURL returns the ReposURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetReposURL() string { + if c == nil || c.ReposURL == nil { + return "" + } + return *c.ReposURL +} + +// GetSiteAdmin returns the SiteAdmin field if it's non-nil, zero value otherwise. +func (c *Contributor) GetSiteAdmin() bool { + if c == nil || c.SiteAdmin == nil { + return false + } + return *c.SiteAdmin +} + +// GetStarredURL returns the StarredURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetStarredURL() string { + if c == nil || c.StarredURL == nil { + return "" + } + return *c.StarredURL +} + +// GetSubscriptionsURL returns the SubscriptionsURL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetSubscriptionsURL() string { + if c == nil || c.SubscriptionsURL == nil { + return "" + } + return *c.SubscriptionsURL +} + +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (c *Contributor) GetType() string { + if c == nil || c.Type == nil { + return "" + } + return *c.Type +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (c *Contributor) GetURL() string { + if c == nil || c.URL == nil { + return "" + } + return *c.URL +} + +// GetAuthor returns the Author field. +func (c *ContributorStats) GetAuthor() *Contributor { + if c == nil { + return nil + } + return c.Author +} + +// GetTotal returns the Total field if it's non-nil, zero value otherwise. +func (c *ContributorStats) GetTotal() int { + if c == nil || c.Total == nil { + return 0 + } + return *c.Total +} + +// GetCustomModelTrainingDate returns the CustomModelTrainingDate field if it's non-nil, zero value otherwise. +func (c *CopilotDotcomChatModel) GetCustomModelTrainingDate() string { + if c == nil || c.CustomModelTrainingDate == nil { + return "" + } + return *c.CustomModelTrainingDate +} + +// GetCustomModelTrainingDate returns the CustomModelTrainingDate field if it's non-nil, zero value otherwise. +func (c *CopilotDotcomPullRequestsModel) GetCustomModelTrainingDate() string { + if c == nil || c.CustomModelTrainingDate == nil { + return "" + } + return *c.CustomModelTrainingDate +} + +// GetCustomModelTrainingDate returns the CustomModelTrainingDate field if it's non-nil, zero value otherwise. +func (c *CopilotIDEChatModel) GetCustomModelTrainingDate() string { + if c == nil || c.CustomModelTrainingDate == nil { + return "" + } + return *c.CustomModelTrainingDate +} + +// GetCustomModelTrainingDate returns the CustomModelTrainingDate field if it's non-nil, zero value otherwise. +func (c *CopilotIDECodeCompletionsModel) GetCustomModelTrainingDate() string { + if c == nil || c.CustomModelTrainingDate == nil { + return "" + } + return *c.CustomModelTrainingDate +} + +// GetCopilotDotcomChat returns the CopilotDotcomChat field. +func (c *CopilotMetrics) GetCopilotDotcomChat() *CopilotDotcomChat { + if c == nil { + return nil + } + return c.CopilotDotcomChat +} + +// GetCopilotDotcomPullRequests returns the CopilotDotcomPullRequests field. +func (c *CopilotMetrics) GetCopilotDotcomPullRequests() *CopilotDotcomPullRequests { + if c == nil { + return nil + } + return c.CopilotDotcomPullRequests +} + +// GetCopilotIDEChat returns the CopilotIDEChat field. +func (c *CopilotMetrics) GetCopilotIDEChat() *CopilotIDEChat { if c == nil { return nil } @@ -9022,6 +10182,134 @@ func (h *HookStats) GetTotalHooks() int { return *h.TotalHooks } +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (h *HostedRunner) GetID() int64 { + if h == nil || h.ID == nil { + return 0 + } + return *h.ID +} + +// GetImageDetails returns the ImageDetails field. +func (h *HostedRunner) GetImageDetails() *HostedRunnerImageDetail { + if h == nil { + return nil + } + return h.ImageDetails +} + +// GetLastActiveOn returns the LastActiveOn field if it's non-nil, zero value otherwise. +func (h *HostedRunner) GetLastActiveOn() Timestamp { + if h == nil || h.LastActiveOn == nil { + return Timestamp{} + } + return *h.LastActiveOn +} + +// GetMachineSizeDetails returns the MachineSizeDetails field. +func (h *HostedRunner) GetMachineSizeDetails() *HostedRunnerMachineSpec { + if h == nil { + return nil + } + return h.MachineSizeDetails +} + +// GetMaximumRunners returns the MaximumRunners field if it's non-nil, zero value otherwise. +func (h *HostedRunner) GetMaximumRunners() int64 { + if h == nil || h.MaximumRunners == nil { + return 0 + } + return *h.MaximumRunners +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (h *HostedRunner) GetName() string { + if h == nil || h.Name == nil { + return "" + } + return *h.Name +} + +// GetPlatform returns the Platform field if it's non-nil, zero value otherwise. +func (h *HostedRunner) GetPlatform() string { + if h == nil || h.Platform == nil { + return "" + } + return *h.Platform +} + +// GetPublicIPEnabled returns the PublicIPEnabled field if it's non-nil, zero value otherwise. +func (h *HostedRunner) GetPublicIPEnabled() bool { + if h == nil || h.PublicIPEnabled == nil { + return false + } + return *h.PublicIPEnabled +} + +// GetRunnerGroupID returns the RunnerGroupID field if it's non-nil, zero value otherwise. +func (h *HostedRunner) GetRunnerGroupID() int64 { + if h == nil || h.RunnerGroupID == nil { + return 0 + } + return *h.RunnerGroupID +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (h *HostedRunner) GetStatus() string { + if h == nil || h.Status == nil { + return "" + } + return *h.Status +} + +// GetDisplayName returns the DisplayName field if it's non-nil, zero value otherwise. +func (h *HostedRunnerImageDetail) GetDisplayName() string { + if h == nil || h.DisplayName == nil { + return "" + } + return *h.DisplayName +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (h *HostedRunnerImageDetail) GetID() string { + if h == nil || h.ID == nil { + return "" + } + return *h.ID +} + +// GetSizeGB returns the SizeGB field if it's non-nil, zero value otherwise. +func (h *HostedRunnerImageDetail) GetSizeGB() int64 { + if h == nil || h.SizeGB == nil { + return 0 + } + return *h.SizeGB +} + +// GetSource returns the Source field if it's non-nil, zero value otherwise. +func (h *HostedRunnerImageDetail) GetSource() string { + if h == nil || h.Source == nil { + return "" + } + return *h.Source +} + +// GetVersion returns the Version field if it's non-nil, zero value otherwise. +func (h *HostedRunnerImageDetail) GetVersion() string { + if h == nil || h.Version == nil { + return "" + } + return *h.Version +} + +// GetPublicIPs returns the PublicIPs field. +func (h *HostedRunnerPublicIPLimits) GetPublicIPs() *PublicIPUsage { + if h == nil { + return nil + } + return h.PublicIPs +} + // GetGroupDescription returns the GroupDescription field if it's non-nil, zero value otherwise. func (i *IDPGroup) GetGroupDescription() string { if i == nil || i.GroupDescription == nil { @@ -10534,6 +11822,14 @@ func (i *Issue) GetTitle() string { return *i.Title } +// GetType returns the Type field. +func (i *Issue) GetType() *IssueType { + if i == nil { + return nil + } + return i.Type +} + // GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. func (i *Issue) GetUpdatedAt() Timestamp { if i == nil || i.UpdatedAt == nil { @@ -11203,31 +12499,87 @@ func (i *IssuesSearchResult) GetTotal() int { if i == nil || i.Total == nil { return 0 } - return *i.Total + return *i.Total +} + +// GetClosedIssues returns the ClosedIssues field if it's non-nil, zero value otherwise. +func (i *IssueStats) GetClosedIssues() int { + if i == nil || i.ClosedIssues == nil { + return 0 + } + return *i.ClosedIssues +} + +// GetOpenIssues returns the OpenIssues field if it's non-nil, zero value otherwise. +func (i *IssueStats) GetOpenIssues() int { + if i == nil || i.OpenIssues == nil { + return 0 + } + return *i.OpenIssues +} + +// GetTotalIssues returns the TotalIssues field if it's non-nil, zero value otherwise. +func (i *IssueStats) GetTotalIssues() int { + if i == nil || i.TotalIssues == nil { + return 0 + } + return *i.TotalIssues +} + +// GetColor returns the Color field if it's non-nil, zero value otherwise. +func (i *IssueType) GetColor() string { + if i == nil || i.Color == nil { + return "" + } + return *i.Color +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (i *IssueType) GetCreatedAt() Timestamp { + if i == nil || i.CreatedAt == nil { + return Timestamp{} + } + return *i.CreatedAt +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (i *IssueType) GetDescription() string { + if i == nil || i.Description == nil { + return "" + } + return *i.Description +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (i *IssueType) GetID() int64 { + if i == nil || i.ID == nil { + return 0 + } + return *i.ID } -// GetClosedIssues returns the ClosedIssues field if it's non-nil, zero value otherwise. -func (i *IssueStats) GetClosedIssues() int { - if i == nil || i.ClosedIssues == nil { - return 0 +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (i *IssueType) GetName() string { + if i == nil || i.Name == nil { + return "" } - return *i.ClosedIssues + return *i.Name } -// GetOpenIssues returns the OpenIssues field if it's non-nil, zero value otherwise. -func (i *IssueStats) GetOpenIssues() int { - if i == nil || i.OpenIssues == nil { - return 0 +// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. +func (i *IssueType) GetNodeID() string { + if i == nil || i.NodeID == nil { + return "" } - return *i.OpenIssues + return *i.NodeID } -// GetTotalIssues returns the TotalIssues field if it's non-nil, zero value otherwise. -func (i *IssueStats) GetTotalIssues() int { - if i == nil || i.TotalIssues == nil { - return 0 +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (i *IssueType) GetUpdatedAt() Timestamp { + if i == nil || i.UpdatedAt == nil { + return Timestamp{} } - return *i.TotalIssues + return *i.UpdatedAt } // GetEncodedJITConfig returns the EncodedJITConfig field if it's non-nil, zero value otherwise. @@ -11638,6 +12990,158 @@ func (l *License) GetURL() string { return *l.URL } +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (l *LicenseCheck) GetStatus() string { + if l == nil || l.Status == nil { + return "" + } + return *l.Status +} + +// GetAdvancedSecurityEnabled returns the AdvancedSecurityEnabled field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetAdvancedSecurityEnabled() bool { + if l == nil || l.AdvancedSecurityEnabled == nil { + return false + } + return *l.AdvancedSecurityEnabled +} + +// GetAdvancedSecuritySeats returns the AdvancedSecuritySeats field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetAdvancedSecuritySeats() int { + if l == nil || l.AdvancedSecuritySeats == nil { + return 0 + } + return *l.AdvancedSecuritySeats +} + +// GetClusterSupport returns the ClusterSupport field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetClusterSupport() bool { + if l == nil || l.ClusterSupport == nil { + return false + } + return *l.ClusterSupport +} + +// GetCompany returns the Company field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetCompany() string { + if l == nil || l.Company == nil { + return "" + } + return *l.Company +} + +// GetCroquetSupport returns the CroquetSupport field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetCroquetSupport() bool { + if l == nil || l.CroquetSupport == nil { + return false + } + return *l.CroquetSupport +} + +// GetCustomTerms returns the CustomTerms field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetCustomTerms() bool { + if l == nil || l.CustomTerms == nil { + return false + } + return *l.CustomTerms +} + +// GetEvaluation returns the Evaluation field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetEvaluation() bool { + if l == nil || l.Evaluation == nil { + return false + } + return *l.Evaluation +} + +// GetExpireAt returns the ExpireAt field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetExpireAt() Timestamp { + if l == nil || l.ExpireAt == nil { + return Timestamp{} + } + return *l.ExpireAt +} + +// GetInsightsEnabled returns the InsightsEnabled field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetInsightsEnabled() bool { + if l == nil || l.InsightsEnabled == nil { + return false + } + return *l.InsightsEnabled +} + +// GetInsightsExpireAt returns the InsightsExpireAt field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetInsightsExpireAt() Timestamp { + if l == nil || l.InsightsExpireAt == nil { + return Timestamp{} + } + return *l.InsightsExpireAt +} + +// GetLearningLabEvaluationExpires returns the LearningLabEvaluationExpires field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetLearningLabEvaluationExpires() Timestamp { + if l == nil || l.LearningLabEvaluationExpires == nil { + return Timestamp{} + } + return *l.LearningLabEvaluationExpires +} + +// GetLearningLabSeats returns the LearningLabSeats field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetLearningLabSeats() int { + if l == nil || l.LearningLabSeats == nil { + return 0 + } + return *l.LearningLabSeats +} + +// GetPerpetual returns the Perpetual field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetPerpetual() bool { + if l == nil || l.Perpetual == nil { + return false + } + return *l.Perpetual +} + +// GetReferenceNumber returns the ReferenceNumber field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetReferenceNumber() string { + if l == nil || l.ReferenceNumber == nil { + return "" + } + return *l.ReferenceNumber +} + +// GetSeats returns the Seats field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetSeats() int { + if l == nil || l.Seats == nil { + return 0 + } + return *l.Seats +} + +// GetSSHAllowed returns the SSHAllowed field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetSSHAllowed() bool { + if l == nil || l.SSHAllowed == nil { + return false + } + return *l.SSHAllowed +} + +// GetSupportKey returns the SupportKey field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetSupportKey() string { + if l == nil || l.SupportKey == nil { + return "" + } + return *l.SupportKey +} + +// GetUnlimitedSeating returns the UnlimitedSeating field if it's non-nil, zero value otherwise. +func (l *LicenseStatus) GetUnlimitedSeating() bool { + if l == nil || l.UnlimitedSeating == nil { + return false + } + return *l.UnlimitedSeating +} + // GetFrom returns the From field if it's non-nil, zero value otherwise. func (l *LinearHistoryRequirementEnforcementLevelChanges) GetFrom() string { if l == nil || l.From == nil { @@ -11982,6 +13486,102 @@ func (l *LockBranch) GetEnabled() bool { return *l.Enabled } +// GetHostname returns the Hostname field if it's non-nil, zero value otherwise. +func (m *MaintenanceOperationStatus) GetHostname() string { + if m == nil || m.Hostname == nil { + return "" + } + return *m.Hostname +} + +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (m *MaintenanceOperationStatus) GetMessage() string { + if m == nil || m.Message == nil { + return "" + } + return *m.Message +} + +// GetUUID returns the UUID field if it's non-nil, zero value otherwise. +func (m *MaintenanceOperationStatus) GetUUID() string { + if m == nil || m.UUID == nil { + return "" + } + return *m.UUID +} + +// GetMaintenanceModeMessage returns the MaintenanceModeMessage field if it's non-nil, zero value otherwise. +func (m *MaintenanceOptions) GetMaintenanceModeMessage() string { + if m == nil || m.MaintenanceModeMessage == nil { + return "" + } + return *m.MaintenanceModeMessage +} + +// GetUUID returns the UUID field if it's non-nil, zero value otherwise. +func (m *MaintenanceOptions) GetUUID() string { + if m == nil || m.UUID == nil { + return "" + } + return *m.UUID +} + +// GetWhen returns the When field if it's non-nil, zero value otherwise. +func (m *MaintenanceOptions) GetWhen() string { + if m == nil || m.When == nil { + return "" + } + return *m.When +} + +// GetCanUnsetMaintenance returns the CanUnsetMaintenance field if it's non-nil, zero value otherwise. +func (m *MaintenanceStatus) GetCanUnsetMaintenance() bool { + if m == nil || m.CanUnsetMaintenance == nil { + return false + } + return *m.CanUnsetMaintenance +} + +// GetHostname returns the Hostname field if it's non-nil, zero value otherwise. +func (m *MaintenanceStatus) GetHostname() string { + if m == nil || m.Hostname == nil { + return "" + } + return *m.Hostname +} + +// GetMaintenanceModeMessage returns the MaintenanceModeMessage field if it's non-nil, zero value otherwise. +func (m *MaintenanceStatus) GetMaintenanceModeMessage() string { + if m == nil || m.MaintenanceModeMessage == nil { + return "" + } + return *m.MaintenanceModeMessage +} + +// GetScheduledTime returns the ScheduledTime field if it's non-nil, zero value otherwise. +func (m *MaintenanceStatus) GetScheduledTime() Timestamp { + if m == nil || m.ScheduledTime == nil { + return Timestamp{} + } + return *m.ScheduledTime +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (m *MaintenanceStatus) GetStatus() string { + if m == nil || m.Status == nil { + return "" + } + return *m.Status +} + +// GetUUID returns the UUID field if it's non-nil, zero value otherwise. +func (m *MaintenanceStatus) GetUUID() string { + if m == nil || m.UUID == nil { + return "" + } + return *m.UUID +} + // GetEffectiveDate returns the EffectiveDate field if it's non-nil, zero value otherwise. func (m *MarketplacePendingChange) GetEffectiveDate() Timestamp { if m == nil || m.EffectiveDate == nil { @@ -12638,6 +14238,14 @@ func (m *MergeGroupEvent) GetOrg() *Organization { return m.Org } +// GetReason returns the Reason field if it's non-nil, zero value otherwise. +func (m *MergeGroupEvent) GetReason() string { + if m == nil || m.Reason == nil { + return "" + } + return *m.Reason +} + // GetRepo returns the Repo field. func (m *MergeGroupEvent) GetRepo() *Repository { if m == nil { @@ -13110,6 +14718,102 @@ func (m *MostRecentInstance) GetState() string { return *m.State } +// GetComputeService returns the ComputeService field. +func (n *NetworkConfiguration) GetComputeService() *ComputeService { + if n == nil { + return nil + } + return n.ComputeService +} + +// GetCreatedOn returns the CreatedOn field if it's non-nil, zero value otherwise. +func (n *NetworkConfiguration) GetCreatedOn() Timestamp { + if n == nil || n.CreatedOn == nil { + return Timestamp{} + } + return *n.CreatedOn +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (n *NetworkConfiguration) GetID() string { + if n == nil || n.ID == nil { + return "" + } + return *n.ID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (n *NetworkConfiguration) GetName() string { + if n == nil || n.Name == nil { + return "" + } + return *n.Name +} + +// GetComputeService returns the ComputeService field. +func (n *NetworkConfigurationRequest) GetComputeService() *ComputeService { + if n == nil { + return nil + } + return n.ComputeService +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (n *NetworkConfigurationRequest) GetName() string { + if n == nil || n.Name == nil { + return "" + } + return *n.Name +} + +// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. +func (n *NetworkConfigurations) GetTotalCount() int64 { + if n == nil || n.TotalCount == nil { + return 0 + } + return *n.TotalCount +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (n *NetworkSettingsResource) GetID() string { + if n == nil || n.ID == nil { + return "" + } + return *n.ID +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (n *NetworkSettingsResource) GetName() string { + if n == nil || n.Name == nil { + return "" + } + return *n.Name +} + +// GetNetworkConfigurationID returns the NetworkConfigurationID field if it's non-nil, zero value otherwise. +func (n *NetworkSettingsResource) GetNetworkConfigurationID() string { + if n == nil || n.NetworkConfigurationID == nil { + return "" + } + return *n.NetworkConfigurationID +} + +// GetRegion returns the Region field if it's non-nil, zero value otherwise. +func (n *NetworkSettingsResource) GetRegion() string { + if n == nil || n.Region == nil { + return "" + } + return *n.Region +} + +// GetSubnetID returns the SubnetID field if it's non-nil, zero value otherwise. +func (n *NetworkSettingsResource) GetSubnetID() string { + if n == nil || n.SubnetID == nil { + return "" + } + return *n.SubnetID +} + // GetBase returns the Base field if it's non-nil, zero value otherwise. func (n *NewPullRequest) GetBase() string { if n == nil || n.Base == nil { @@ -13203,23 +14907,79 @@ func (n *NewTeam) GetParentTeamID() int64 { if n == nil || n.ParentTeamID == nil { return 0 } - return *n.ParentTeamID + return *n.ParentTeamID +} + +// GetPermission returns the Permission field if it's non-nil, zero value otherwise. +func (n *NewTeam) GetPermission() string { + if n == nil || n.Permission == nil { + return "" + } + return *n.Permission +} + +// GetPrivacy returns the Privacy field if it's non-nil, zero value otherwise. +func (n *NewTeam) GetPrivacy() string { + if n == nil || n.Privacy == nil { + return "" + } + return *n.Privacy +} + +// GetHostname returns the Hostname field if it's non-nil, zero value otherwise. +func (n *NodeDetails) GetHostname() string { + if n == nil || n.Hostname == nil { + return "" + } + return *n.Hostname +} + +// GetUUID returns the UUID field if it's non-nil, zero value otherwise. +func (n *NodeDetails) GetUUID() string { + if n == nil || n.UUID == nil { + return "" + } + return *n.UUID +} + +// GetTopology returns the Topology field if it's non-nil, zero value otherwise. +func (n *NodeMetadataStatus) GetTopology() string { + if n == nil || n.Topology == nil { + return "" + } + return *n.Topology +} + +// GetClusterRoles returns the ClusterRoles field if it's non-nil, zero value otherwise. +func (n *NodeQueryOptions) GetClusterRoles() string { + if n == nil || n.ClusterRoles == nil { + return "" + } + return *n.ClusterRoles } -// GetPermission returns the Permission field if it's non-nil, zero value otherwise. -func (n *NewTeam) GetPermission() string { - if n == nil || n.Permission == nil { +// GetUUID returns the UUID field if it's non-nil, zero value otherwise. +func (n *NodeQueryOptions) GetUUID() string { + if n == nil || n.UUID == nil { return "" } - return *n.Permission + return *n.UUID } -// GetPrivacy returns the Privacy field if it's non-nil, zero value otherwise. -func (n *NewTeam) GetPrivacy() string { - if n == nil || n.Privacy == nil { +// GetHostname returns the Hostname field if it's non-nil, zero value otherwise. +func (n *NodeReleaseVersion) GetHostname() string { + if n == nil || n.Hostname == nil { return "" } - return *n.Privacy + return *n.Hostname +} + +// GetVersion returns the Version field. +func (n *NodeReleaseVersion) GetVersion() *ReleaseVersion { + if n == nil { + return nil + } + return n.Version } // GetID returns the ID field if it's non-nil, zero value otherwise. @@ -14054,6 +15814,22 @@ func (p *Package) GetCreatedAt() Timestamp { return *p.CreatedAt } +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (p *Package) GetDescription() string { + if p == nil || p.Description == nil { + return "" + } + return *p.Description +} + +// GetEcosystem returns the Ecosystem field if it's non-nil, zero value otherwise. +func (p *Package) GetEcosystem() string { + if p == nil || p.Ecosystem == nil { + return "" + } + return *p.Ecosystem +} + // GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. func (p *Package) GetHTMLURL() string { if p == nil || p.HTMLURL == nil { @@ -14078,6 +15854,14 @@ func (p *Package) GetName() string { return *p.Name } +// GetNamespace returns the Namespace field if it's non-nil, zero value otherwise. +func (p *Package) GetNamespace() string { + if p == nil || p.Namespace == nil { + return "" + } + return *p.Namespace +} + // GetOwner returns the Owner field. func (p *Package) GetOwner() *User { if p == nil { @@ -14110,14 +15894,6 @@ func (p *Package) GetRegistry() *PackageRegistry { return p.Registry } -// GetRepository returns the Repository field. -func (p *Package) GetRepository() *Repository { - if p == nil { - return nil - } - return p.Repository -} - // GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. func (p *Package) GetUpdatedAt() Timestamp { if p == nil || p.UpdatedAt == nil { @@ -14163,175 +15939,471 @@ func (p *PackageEvent) GetInstallation() *Installation { if p == nil { return nil } - return p.Installation + return p.Installation +} + +// GetOrg returns the Org field. +func (p *PackageEvent) GetOrg() *Organization { + if p == nil { + return nil + } + return p.Org +} + +// GetPackage returns the Package field. +func (p *PackageEvent) GetPackage() *Package { + if p == nil { + return nil + } + return p.Package +} + +// GetRepo returns the Repo field. +func (p *PackageEvent) GetRepo() *Repository { + if p == nil { + return nil + } + return p.Repo +} + +// GetSender returns the Sender field. +func (p *PackageEvent) GetSender() *User { + if p == nil { + return nil + } + return p.Sender +} + +// GetLabels returns the Labels map if it's non-nil, an empty map otherwise. +func (p *PackageEventContainerMetadata) GetLabels() map[string]any { + if p == nil || p.Labels == nil { + return map[string]any{} + } + return p.Labels +} + +// GetManifest returns the Manifest map if it's non-nil, an empty map otherwise. +func (p *PackageEventContainerMetadata) GetManifest() map[string]any { + if p == nil || p.Manifest == nil { + return map[string]any{} + } + return p.Manifest +} + +// GetTag returns the Tag field. +func (p *PackageEventContainerMetadata) GetTag() *PackageEventContainerMetadataTag { + if p == nil { + return nil + } + return p.Tag +} + +// GetDigest returns the Digest field if it's non-nil, zero value otherwise. +func (p *PackageEventContainerMetadataTag) GetDigest() string { + if p == nil || p.Digest == nil { + return "" + } + return *p.Digest +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (p *PackageEventContainerMetadataTag) GetName() string { + if p == nil || p.Name == nil { + return "" + } + return *p.Name +} + +// GetAuthor returns the Author field. +func (p *PackageFile) GetAuthor() *User { + if p == nil { + return nil + } + return p.Author +} + +// GetContentType returns the ContentType field if it's non-nil, zero value otherwise. +func (p *PackageFile) GetContentType() string { + if p == nil || p.ContentType == nil { + return "" + } + return *p.ContentType +} + +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (p *PackageFile) GetCreatedAt() Timestamp { + if p == nil || p.CreatedAt == nil { + return Timestamp{} + } + return *p.CreatedAt +} + +// GetDownloadURL returns the DownloadURL field if it's non-nil, zero value otherwise. +func (p *PackageFile) GetDownloadURL() string { + if p == nil || p.DownloadURL == nil { + return "" + } + return *p.DownloadURL +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (p *PackageFile) GetID() int64 { + if p == nil || p.ID == nil { + return 0 + } + return *p.ID +} + +// GetMD5 returns the MD5 field if it's non-nil, zero value otherwise. +func (p *PackageFile) GetMD5() string { + if p == nil || p.MD5 == nil { + return "" + } + return *p.MD5 +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (p *PackageFile) GetName() string { + if p == nil || p.Name == nil { + return "" + } + return *p.Name +} + +// GetSHA1 returns the SHA1 field if it's non-nil, zero value otherwise. +func (p *PackageFile) GetSHA1() string { + if p == nil || p.SHA1 == nil { + return "" + } + return *p.SHA1 +} + +// GetSHA256 returns the SHA256 field if it's non-nil, zero value otherwise. +func (p *PackageFile) GetSHA256() string { + if p == nil || p.SHA256 == nil { + return "" + } + return *p.SHA256 +} + +// GetSize returns the Size field if it's non-nil, zero value otherwise. +func (p *PackageFile) GetSize() int64 { + if p == nil || p.Size == nil { + return 0 + } + return *p.Size +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (p *PackageFile) GetState() string { + if p == nil || p.State == nil { + return "" + } + return *p.State +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (p *PackageFile) GetUpdatedAt() Timestamp { + if p == nil || p.UpdatedAt == nil { + return Timestamp{} + } + return *p.UpdatedAt +} + +// GetPackageType returns the PackageType field if it's non-nil, zero value otherwise. +func (p *PackageListOptions) GetPackageType() string { + if p == nil || p.PackageType == nil { + return "" + } + return *p.PackageType +} + +// GetState returns the State field if it's non-nil, zero value otherwise. +func (p *PackageListOptions) GetState() string { + if p == nil || p.State == nil { + return "" + } + return *p.State +} + +// GetVisibility returns the Visibility field if it's non-nil, zero value otherwise. +func (p *PackageListOptions) GetVisibility() string { + if p == nil || p.Visibility == nil { + return "" + } + return *p.Visibility +} + +// GetContainer returns the Container field. +func (p *PackageMetadata) GetContainer() *PackageContainerMetadata { + if p == nil { + return nil + } + return p.Container +} + +// GetPackageType returns the PackageType field if it's non-nil, zero value otherwise. +func (p *PackageMetadata) GetPackageType() string { + if p == nil || p.PackageType == nil { + return "" + } + return *p.PackageType +} + +// GetAuthor returns the Author map if it's non-nil, an empty map otherwise. +func (p *PackageNPMMetadata) GetAuthor() map[string]string { + if p == nil || p.Author == nil { + return map[string]string{} + } + return p.Author +} + +// GetBin returns the Bin map if it's non-nil, an empty map otherwise. +func (p *PackageNPMMetadata) GetBin() map[string]any { + if p == nil || p.Bin == nil { + return map[string]any{} + } + return p.Bin +} + +// GetBugs returns the Bugs map if it's non-nil, an empty map otherwise. +func (p *PackageNPMMetadata) GetBugs() map[string]string { + if p == nil || p.Bugs == nil { + return map[string]string{} + } + return p.Bugs +} + +// GetCommitOID returns the CommitOID field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetCommitOID() string { + if p == nil || p.CommitOID == nil { + return "" + } + return *p.CommitOID +} + +// GetDeletedByID returns the DeletedByID field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetDeletedByID() int64 { + if p == nil || p.DeletedByID == nil { + return 0 + } + return *p.DeletedByID +} + +// GetDependencies returns the Dependencies map if it's non-nil, an empty map otherwise. +func (p *PackageNPMMetadata) GetDependencies() map[string]string { + if p == nil || p.Dependencies == nil { + return map[string]string{} + } + return p.Dependencies } -// GetOrg returns the Org field. -func (p *PackageEvent) GetOrg() *Organization { - if p == nil { - return nil +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetDescription() string { + if p == nil || p.Description == nil { + return "" } - return p.Org + return *p.Description } -// GetPackage returns the Package field. -func (p *PackageEvent) GetPackage() *Package { - if p == nil { - return nil +// GetDevDependencies returns the DevDependencies map if it's non-nil, an empty map otherwise. +func (p *PackageNPMMetadata) GetDevDependencies() map[string]string { + if p == nil || p.DevDependencies == nil { + return map[string]string{} } - return p.Package + return p.DevDependencies } -// GetRepo returns the Repo field. -func (p *PackageEvent) GetRepo() *Repository { - if p == nil { - return nil +// GetDirectories returns the Directories map if it's non-nil, an empty map otherwise. +func (p *PackageNPMMetadata) GetDirectories() map[string]string { + if p == nil || p.Directories == nil { + return map[string]string{} } - return p.Repo + return p.Directories } -// GetSender returns the Sender field. -func (p *PackageEvent) GetSender() *User { - if p == nil { - return nil +// GetDist returns the Dist map if it's non-nil, an empty map otherwise. +func (p *PackageNPMMetadata) GetDist() map[string]string { + if p == nil || p.Dist == nil { + return map[string]string{} } - return p.Sender + return p.Dist } -// GetAuthor returns the Author field. -func (p *PackageFile) GetAuthor() *User { - if p == nil { - return nil +// GetEngines returns the Engines map if it's non-nil, an empty map otherwise. +func (p *PackageNPMMetadata) GetEngines() map[string]string { + if p == nil || p.Engines == nil { + return map[string]string{} } - return p.Author + return p.Engines } -// GetContentType returns the ContentType field if it's non-nil, zero value otherwise. -func (p *PackageFile) GetContentType() string { - if p == nil || p.ContentType == nil { +// GetGitHead returns the GitHead field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetGitHead() string { + if p == nil || p.GitHead == nil { return "" } - return *p.ContentType + return *p.GitHead } -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (p *PackageFile) GetCreatedAt() Timestamp { - if p == nil || p.CreatedAt == nil { - return Timestamp{} +// GetHasShrinkwrap returns the HasShrinkwrap field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetHasShrinkwrap() bool { + if p == nil || p.HasShrinkwrap == nil { + return false } - return *p.CreatedAt + return *p.HasShrinkwrap } -// GetDownloadURL returns the DownloadURL field if it's non-nil, zero value otherwise. -func (p *PackageFile) GetDownloadURL() string { - if p == nil || p.DownloadURL == nil { +// GetHomepage returns the Homepage field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetHomepage() string { + if p == nil || p.Homepage == nil { return "" } - return *p.DownloadURL + return *p.Homepage } // GetID returns the ID field if it's non-nil, zero value otherwise. -func (p *PackageFile) GetID() int64 { +func (p *PackageNPMMetadata) GetID() string { if p == nil || p.ID == nil { - return 0 + return "" } return *p.ID } -// GetMD5 returns the MD5 field if it's non-nil, zero value otherwise. -func (p *PackageFile) GetMD5() string { - if p == nil || p.MD5 == nil { +// GetInstallationCommand returns the InstallationCommand field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetInstallationCommand() string { + if p == nil || p.InstallationCommand == nil { return "" } - return *p.MD5 + return *p.InstallationCommand +} + +// GetLicense returns the License field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetLicense() string { + if p == nil || p.License == nil { + return "" + } + return *p.License +} + +// GetMain returns the Main field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetMain() string { + if p == nil || p.Main == nil { + return "" + } + return *p.Main +} + +// GetMan returns the Man map if it's non-nil, an empty map otherwise. +func (p *PackageNPMMetadata) GetMan() map[string]any { + if p == nil || p.Man == nil { + return map[string]any{} + } + return p.Man } // GetName returns the Name field if it's non-nil, zero value otherwise. -func (p *PackageFile) GetName() string { +func (p *PackageNPMMetadata) GetName() string { if p == nil || p.Name == nil { return "" } return *p.Name } -// GetSHA1 returns the SHA1 field if it's non-nil, zero value otherwise. -func (p *PackageFile) GetSHA1() string { - if p == nil || p.SHA1 == nil { +// GetNodeVersion returns the NodeVersion field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetNodeVersion() string { + if p == nil || p.NodeVersion == nil { return "" } - return *p.SHA1 + return *p.NodeVersion } -// GetSHA256 returns the SHA256 field if it's non-nil, zero value otherwise. -func (p *PackageFile) GetSHA256() string { - if p == nil || p.SHA256 == nil { +// GetNPMUser returns the NPMUser field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetNPMUser() string { + if p == nil || p.NPMUser == nil { return "" } - return *p.SHA256 + return *p.NPMUser } -// GetSize returns the Size field if it's non-nil, zero value otherwise. -func (p *PackageFile) GetSize() int64 { - if p == nil || p.Size == nil { - return 0 +// GetNPMVersion returns the NPMVersion field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetNPMVersion() string { + if p == nil || p.NPMVersion == nil { + return "" } - return *p.Size + return *p.NPMVersion } -// GetState returns the State field if it's non-nil, zero value otherwise. -func (p *PackageFile) GetState() string { - if p == nil || p.State == nil { - return "" +// GetOptionalDependencies returns the OptionalDependencies map if it's non-nil, an empty map otherwise. +func (p *PackageNPMMetadata) GetOptionalDependencies() map[string]string { + if p == nil || p.OptionalDependencies == nil { + return map[string]string{} } - return *p.State + return p.OptionalDependencies } -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (p *PackageFile) GetUpdatedAt() Timestamp { - if p == nil || p.UpdatedAt == nil { - return Timestamp{} +// GetPeerDependencies returns the PeerDependencies map if it's non-nil, an empty map otherwise. +func (p *PackageNPMMetadata) GetPeerDependencies() map[string]string { + if p == nil || p.PeerDependencies == nil { + return map[string]string{} } - return *p.UpdatedAt + return p.PeerDependencies } -// GetPackageType returns the PackageType field if it's non-nil, zero value otherwise. -func (p *PackageListOptions) GetPackageType() string { - if p == nil || p.PackageType == nil { - return "" +// GetPublishedViaActions returns the PublishedViaActions field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetPublishedViaActions() bool { + if p == nil || p.PublishedViaActions == nil { + return false } - return *p.PackageType + return *p.PublishedViaActions } -// GetState returns the State field if it's non-nil, zero value otherwise. -func (p *PackageListOptions) GetState() string { - if p == nil || p.State == nil { +// GetReadme returns the Readme field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetReadme() string { + if p == nil || p.Readme == nil { return "" } - return *p.State + return *p.Readme } -// GetVisibility returns the Visibility field if it's non-nil, zero value otherwise. -func (p *PackageListOptions) GetVisibility() string { - if p == nil || p.Visibility == nil { - return "" +// GetReleaseID returns the ReleaseID field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetReleaseID() int64 { + if p == nil || p.ReleaseID == nil { + return 0 } - return *p.Visibility + return *p.ReleaseID } -// GetContainer returns the Container field. -func (p *PackageMetadata) GetContainer() *PackageContainerMetadata { - if p == nil { - return nil +// GetRepository returns the Repository map if it's non-nil, an empty map otherwise. +func (p *PackageNPMMetadata) GetRepository() map[string]string { + if p == nil || p.Repository == nil { + return map[string]string{} } - return p.Container + return p.Repository } -// GetPackageType returns the PackageType field if it's non-nil, zero value otherwise. -func (p *PackageMetadata) GetPackageType() string { - if p == nil || p.PackageType == nil { +// GetScripts returns the Scripts map if it's non-nil, an empty map otherwise. +func (p *PackageNPMMetadata) GetScripts() map[string]any { + if p == nil || p.Scripts == nil { + return map[string]any{} + } + return p.Scripts +} + +// GetVersion returns the Version field if it's non-nil, zero value otherwise. +func (p *PackageNPMMetadata) GetVersion() string { + if p == nil || p.Version == nil { return "" } - return *p.PackageType + return *p.Version +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (p *PackageNugetMetadata) GetName() string { + if p == nil || p.Name == nil { + return "" + } + return *p.Name } // GetAboutURL returns the AboutURL field if it's non-nil, zero value otherwise. @@ -14470,14 +16542,6 @@ func (p *PackageVersion) GetAuthor() *User { return p.Author } -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (p *PackageVersion) GetBody() string { - if p == nil || p.Body == nil { - return "" - } - return *p.Body -} - // GetBodyHTML returns the BodyHTML field if it's non-nil, zero value otherwise. func (p *PackageVersion) GetBodyHTML() string { if p == nil || p.BodyHTML == nil { @@ -14486,6 +16550,14 @@ func (p *PackageVersion) GetBodyHTML() string { return *p.BodyHTML } +// GetContainerMetadata returns the ContainerMetadata field. +func (p *PackageVersion) GetContainerMetadata() *PackageEventContainerMetadata { + if p == nil { + return nil + } + return p.ContainerMetadata +} + // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. func (p *PackageVersion) GetCreatedAt() Timestamp { if p == nil || p.CreatedAt == nil { @@ -14494,6 +16566,22 @@ func (p *PackageVersion) GetCreatedAt() Timestamp { return *p.CreatedAt } +// GetDeletedAt returns the DeletedAt field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetDeletedAt() Timestamp { + if p == nil || p.DeletedAt == nil { + return Timestamp{} + } + return *p.DeletedAt +} + +// GetDescription returns the Description field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetDescription() string { + if p == nil || p.Description == nil { + return "" + } + return *p.Description +} + // GetDraft returns the Draft field if it's non-nil, zero value otherwise. func (p *PackageVersion) GetDraft() bool { if p == nil || p.Draft == nil { @@ -14526,6 +16614,14 @@ func (p *PackageVersion) GetInstallationCommand() string { return *p.InstallationCommand } +// GetLicense returns the License field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetLicense() string { + if p == nil || p.License == nil { + return "" + } + return *p.License +} + // GetManifest returns the Manifest field if it's non-nil, zero value otherwise. func (p *PackageVersion) GetManifest() string { if p == nil || p.Manifest == nil { @@ -14534,14 +16630,6 @@ func (p *PackageVersion) GetManifest() string { return *p.Manifest } -// GetMetadata returns the Metadata field. -func (p *PackageVersion) GetMetadata() *PackageMetadata { - if p == nil { - return nil - } - return p.Metadata -} - // GetName returns the Name field if it's non-nil, zero value otherwise. func (p *PackageVersion) GetName() string { if p == nil || p.Name == nil { @@ -14550,6 +16638,14 @@ func (p *PackageVersion) GetName() string { return *p.Name } +// GetNPMMetadata returns the NPMMetadata field. +func (p *PackageVersion) GetNPMMetadata() *PackageNPMMetadata { + if p == nil { + return nil + } + return p.NPMMetadata +} + // GetPackageHTMLURL returns the PackageHTMLURL field if it's non-nil, zero value otherwise. func (p *PackageVersion) GetPackageHTMLURL() string { if p == nil || p.PackageHTMLURL == nil { @@ -14558,6 +16654,14 @@ func (p *PackageVersion) GetPackageHTMLURL() string { return *p.PackageHTMLURL } +// GetPackageURL returns the PackageURL field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetPackageURL() string { + if p == nil || p.PackageURL == nil { + return "" + } + return *p.PackageURL +} + // GetPrerelease returns the Prerelease field if it's non-nil, zero value otherwise. func (p *PackageVersion) GetPrerelease() bool { if p == nil || p.Prerelease == nil { @@ -14574,60 +16678,148 @@ func (p *PackageVersion) GetRelease() *PackageRelease { return p.Release } +// GetRubyMetadata returns the RubyMetadata map if it's non-nil, an empty map otherwise. +func (p *PackageVersion) GetRubyMetadata() map[string]any { + if p == nil || p.RubyMetadata == nil { + return map[string]any{} + } + return p.RubyMetadata +} + +// GetSourceURL returns the SourceURL field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetSourceURL() string { + if p == nil || p.SourceURL == nil { + return "" + } + return *p.SourceURL +} + // GetSummary returns the Summary field if it's non-nil, zero value otherwise. func (p *PackageVersion) GetSummary() string { if p == nil || p.Summary == nil { return "" } - return *p.Summary + return *p.Summary +} + +// GetTagName returns the TagName field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetTagName() string { + if p == nil || p.TagName == nil { + return "" + } + return *p.TagName +} + +// GetTargetCommitish returns the TargetCommitish field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetTargetCommitish() string { + if p == nil || p.TargetCommitish == nil { + return "" + } + return *p.TargetCommitish +} + +// GetTargetOID returns the TargetOID field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetTargetOID() string { + if p == nil || p.TargetOID == nil { + return "" + } + return *p.TargetOID +} + +// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetUpdatedAt() Timestamp { + if p == nil || p.UpdatedAt == nil { + return Timestamp{} + } + return *p.UpdatedAt +} + +// GetURL returns the URL field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetURL() string { + if p == nil || p.URL == nil { + return "" + } + return *p.URL +} + +// GetVersion returns the Version field if it's non-nil, zero value otherwise. +func (p *PackageVersion) GetVersion() string { + if p == nil || p.Version == nil { + return "" + } + return *p.Version +} + +// GetInfo returns the Info field. +func (p *PackageVersionBody) GetInfo() *PackageVersionBodyInfo { + if p == nil { + return nil + } + return p.Info +} + +// GetRepo returns the Repo field. +func (p *PackageVersionBody) GetRepo() *Repository { + if p == nil { + return nil + } + return p.Repo +} + +// GetCollection returns the Collection field if it's non-nil, zero value otherwise. +func (p *PackageVersionBodyInfo) GetCollection() bool { + if p == nil || p.Collection == nil { + return false + } + return *p.Collection } -// GetTagName returns the TagName field if it's non-nil, zero value otherwise. -func (p *PackageVersion) GetTagName() string { - if p == nil || p.TagName == nil { - return "" +// GetMode returns the Mode field if it's non-nil, zero value otherwise. +func (p *PackageVersionBodyInfo) GetMode() int64 { + if p == nil || p.Mode == nil { + return 0 } - return *p.TagName + return *p.Mode } -// GetTargetCommitish returns the TargetCommitish field if it's non-nil, zero value otherwise. -func (p *PackageVersion) GetTargetCommitish() string { - if p == nil || p.TargetCommitish == nil { +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (p *PackageVersionBodyInfo) GetName() string { + if p == nil || p.Name == nil { return "" } - return *p.TargetCommitish + return *p.Name } -// GetTargetOID returns the TargetOID field if it's non-nil, zero value otherwise. -func (p *PackageVersion) GetTargetOID() string { - if p == nil || p.TargetOID == nil { +// GetOID returns the OID field if it's non-nil, zero value otherwise. +func (p *PackageVersionBodyInfo) GetOID() string { + if p == nil || p.OID == nil { return "" } - return *p.TargetOID + return *p.OID } -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (p *PackageVersion) GetUpdatedAt() Timestamp { - if p == nil || p.UpdatedAt == nil { - return Timestamp{} +// GetPath returns the Path field if it's non-nil, zero value otherwise. +func (p *PackageVersionBodyInfo) GetPath() string { + if p == nil || p.Path == nil { + return "" } - return *p.UpdatedAt + return *p.Path } -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (p *PackageVersion) GetURL() string { - if p == nil || p.URL == nil { - return "" +// GetSize returns the Size field if it's non-nil, zero value otherwise. +func (p *PackageVersionBodyInfo) GetSize() int64 { + if p == nil || p.Size == nil { + return 0 } - return *p.URL + return *p.Size } -// GetVersion returns the Version field if it's non-nil, zero value otherwise. -func (p *PackageVersion) GetVersion() string { - if p == nil || p.Version == nil { +// GetType returns the Type field if it's non-nil, zero value otherwise. +func (p *PackageVersionBodyInfo) GetType() string { + if p == nil || p.Type == nil { return "" } - return *p.Version + return *p.Type } // GetAction returns the Action field if it's non-nil, zero value otherwise. @@ -15206,6 +17398,54 @@ func (p *PagesUpdate) GetSource() *PagesSource { return p.Source } +// GetBuildType returns the BuildType field if it's non-nil, zero value otherwise. +func (p *PagesUpdateWithoutCNAME) GetBuildType() string { + if p == nil || p.BuildType == nil { + return "" + } + return *p.BuildType +} + +// GetHTTPSEnforced returns the HTTPSEnforced field if it's non-nil, zero value otherwise. +func (p *PagesUpdateWithoutCNAME) GetHTTPSEnforced() bool { + if p == nil || p.HTTPSEnforced == nil { + return false + } + return *p.HTTPSEnforced +} + +// GetPublic returns the Public field if it's non-nil, zero value otherwise. +func (p *PagesUpdateWithoutCNAME) GetPublic() bool { + if p == nil || p.Public == nil { + return false + } + return *p.Public +} + +// GetSource returns the Source field. +func (p *PagesUpdateWithoutCNAME) GetSource() *PagesSource { + if p == nil { + return nil + } + return p.Source +} + +// GetName returns the Name field if it's non-nil, zero value otherwise. +func (p *PatternRuleParameters) GetName() string { + if p == nil || p.Name == nil { + return "" + } + return *p.Name +} + +// GetNegate returns the Negate field if it's non-nil, zero value otherwise. +func (p *PatternRuleParameters) GetNegate() bool { + if p == nil || p.Negate == nil { + return false + } + return *p.Negate +} + // GetCurrentUserCanApprove returns the CurrentUserCanApprove field if it's non-nil, zero value otherwise. func (p *PendingDeployment) GetCurrentUserCanApprove() bool { if p == nil || p.CurrentUserCanApprove == nil { @@ -15342,6 +17582,14 @@ func (p *PersonalAccessToken) GetTokenExpiresAt() Timestamp { return *p.TokenExpiresAt } +// GetTokenID returns the TokenID field if it's non-nil, zero value otherwise. +func (p *PersonalAccessToken) GetTokenID() int64 { + if p == nil || p.TokenID == nil { + return 0 + } + return *p.TokenID +} + // GetTokenLastUsedAt returns the TokenLastUsedAt field if it's non-nil, zero value otherwise. func (p *PersonalAccessToken) GetTokenLastUsedAt() Timestamp { if p == nil || p.TokenLastUsedAt == nil { @@ -15350,6 +17598,14 @@ func (p *PersonalAccessToken) GetTokenLastUsedAt() Timestamp { return *p.TokenLastUsedAt } +// GetTokenName returns the TokenName field if it's non-nil, zero value otherwise. +func (p *PersonalAccessToken) GetTokenName() string { + if p == nil || p.TokenName == nil { + return "" + } + return *p.TokenName +} + // GetOrg returns the Org map if it's non-nil, an empty map otherwise. func (p *PersonalAccessTokenPermissions) GetOrg() map[string]string { if p == nil || p.Org == nil { @@ -17750,6 +20006,14 @@ func (p *PullRequestReviewThreadEvent) GetThread() *PullRequestThread { return p.Thread } +// GetAutomaticCopilotCodeReviewEnabled returns the AutomaticCopilotCodeReviewEnabled field if it's non-nil, zero value otherwise. +func (p *PullRequestRuleParameters) GetAutomaticCopilotCodeReviewEnabled() bool { + if p == nil || p.AutomaticCopilotCodeReviewEnabled == nil { + return false + } + return *p.AutomaticCopilotCodeReviewEnabled +} + // GetAction returns the Action field if it's non-nil, zero value otherwise. func (p *PullRequestTargetEvent) GetAction() string { if p == nil || p.Action == nil { @@ -17886,12 +20150,12 @@ func (p *PullRequestThread) GetNodeID() string { return *p.NodeID } -// GetMergablePulls returns the MergablePulls field if it's non-nil, zero value otherwise. -func (p *PullStats) GetMergablePulls() int { - if p == nil || p.MergablePulls == nil { +// GetMergeablePulls returns the MergeablePulls field if it's non-nil, zero value otherwise. +func (p *PullStats) GetMergeablePulls() int { + if p == nil || p.MergeablePulls == nil { return 0 } - return *p.MergablePulls + return *p.MergeablePulls } // GetMergedPulls returns the MergedPulls field if it's non-nil, zero value otherwise. @@ -17910,12 +20174,12 @@ func (p *PullStats) GetTotalPulls() int { return *p.TotalPulls } -// GetUnmergablePulls returns the UnmergablePulls field if it's non-nil, zero value otherwise. -func (p *PullStats) GetUnmergablePulls() int { - if p == nil || p.UnmergablePulls == nil { +// GetUnmergeablePulls returns the UnmergeablePulls field if it's non-nil, zero value otherwise. +func (p *PullStats) GetUnmergeablePulls() int { + if p == nil || p.UnmergeablePulls == nil { return 0 } - return *p.UnmergablePulls + return *p.UnmergeablePulls } // GetCommits returns the Commits field if it's non-nil, zero value otherwise. @@ -18502,6 +20766,14 @@ func (r *Reaction) GetContent() string { return *r.Content } +// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. +func (r *Reaction) GetCreatedAt() Timestamp { + if r == nil || r.CreatedAt == nil { + return Timestamp{} + } + return *r.CreatedAt +} + // GetID returns the ID field if it's non-nil, zero value otherwise. func (r *Reaction) GetID() int64 { if r == nil || r.ID == nil { @@ -18830,6 +21102,38 @@ func (r *ReleaseEvent) GetSender() *User { return r.Sender } +// GetBuildDate returns the BuildDate field if it's non-nil, zero value otherwise. +func (r *ReleaseVersion) GetBuildDate() string { + if r == nil || r.BuildDate == nil { + return "" + } + return *r.BuildDate +} + +// GetBuildID returns the BuildID field if it's non-nil, zero value otherwise. +func (r *ReleaseVersion) GetBuildID() string { + if r == nil || r.BuildID == nil { + return "" + } + return *r.BuildID +} + +// GetPlatform returns the Platform field if it's non-nil, zero value otherwise. +func (r *ReleaseVersion) GetPlatform() string { + if r == nil || r.Platform == nil { + return "" + } + return *r.Platform +} + +// GetVersion returns the Version field if it's non-nil, zero value otherwise. +func (r *ReleaseVersion) GetVersion() string { + if r == nil || r.Version == nil { + return "" + } + return *r.Version +} + // GetExpiresAt returns the ExpiresAt field if it's non-nil, zero value otherwise. func (r *RemoveToken) GetExpiresAt() Timestamp { if r == nil || r.ExpiresAt == nil { @@ -20806,12 +23110,12 @@ func (r *RepositoryRelease) GetZipballURL() string { return *r.ZipballURL } -// GetParameters returns the Parameters field if it's non-nil, zero value otherwise. -func (r *RepositoryRule) GetParameters() json.RawMessage { - if r == nil || r.Parameters == nil { - return json.RawMessage{} +// GetConditions returns the Conditions field. +func (r *RepositoryRuleset) GetConditions() *RepositoryRulesetConditions { + if r == nil { + return nil } - return *r.Parameters + return r.Conditions } // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. @@ -20822,16 +23126,24 @@ func (r *RepositoryRuleset) GetCreatedAt() Timestamp { return *r.CreatedAt } -// GetCurrentUserCanBypass returns the CurrentUserCanBypass field if it's non-nil, zero value otherwise. -func (r *RepositoryRuleset) GetCurrentUserCanBypass() string { - if r == nil || r.CurrentUserCanBypass == nil { - return "" +// GetCurrentUserCanBypass returns the CurrentUserCanBypass field. +func (r *RepositoryRuleset) GetCurrentUserCanBypass() *BypassMode { + if r == nil { + return nil + } + return r.CurrentUserCanBypass +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (r *RepositoryRuleset) GetID() int64 { + if r == nil || r.ID == nil { + return 0 } - return *r.CurrentUserCanBypass + return *r.ID } // GetLinks returns the Links field. -func (r *RepositoryRuleset) GetLinks() *RepositoryRulesetLink { +func (r *RepositoryRuleset) GetLinks() *RepositoryRulesetLinks { if r == nil { return nil } @@ -20846,20 +23158,28 @@ func (r *RepositoryRuleset) GetNodeID() string { return *r.NodeID } -// GetSourceType returns the SourceType field if it's non-nil, zero value otherwise. -func (r *RepositoryRuleset) GetSourceType() string { - if r == nil || r.SourceType == nil { - return "" +// GetRules returns the Rules field. +func (r *RepositoryRuleset) GetRules() *RepositoryRulesetRules { + if r == nil { + return nil } - return *r.SourceType + return r.Rules } -// GetTarget returns the Target field if it's non-nil, zero value otherwise. -func (r *RepositoryRuleset) GetTarget() string { - if r == nil || r.Target == nil { - return "" +// GetSourceType returns the SourceType field. +func (r *RepositoryRuleset) GetSourceType() *RulesetSourceType { + if r == nil { + return nil } - return *r.Target + return r.SourceType +} + +// GetTarget returns the Target field. +func (r *RepositoryRuleset) GetTarget() *RulesetTarget { + if r == nil { + return nil + } + return r.Target } // GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. @@ -20870,16 +23190,32 @@ func (r *RepositoryRuleset) GetUpdatedAt() Timestamp { return *r.UpdatedAt } -// GetParameters returns the Parameters field. -func (r *RepositoryRulesetCodeScanningRule) GetParameters() *RuleCodeScanningParameters { +// GetConfiguration returns the Configuration field. +func (r *RepositoryRulesetChangedRule) GetConfiguration() *RepositoryRulesetChangeSource { + if r == nil { + return nil + } + return r.Configuration +} + +// GetPattern returns the Pattern field. +func (r *RepositoryRulesetChangedRule) GetPattern() *RepositoryRulesetChangeSource { + if r == nil { + return nil + } + return r.Pattern +} + +// GetRuleType returns the RuleType field. +func (r *RepositoryRulesetChangedRule) GetRuleType() *RepositoryRulesetChangeSource { if r == nil { return nil } - return r.Parameters + return r.RuleType } // GetConditions returns the Conditions field. -func (r *RepositoryRulesetEditedChanges) GetConditions() *RepositoryRulesetEditedConditions { +func (r *RepositoryRulesetChanges) GetConditions() *RepositoryRulesetChangedConditions { if r == nil { return nil } @@ -20887,7 +23223,7 @@ func (r *RepositoryRulesetEditedChanges) GetConditions() *RepositoryRulesetEdite } // GetEnforcement returns the Enforcement field. -func (r *RepositoryRulesetEditedChanges) GetEnforcement() *RepositoryRulesetEditedSource { +func (r *RepositoryRulesetChanges) GetEnforcement() *RepositoryRulesetChangeSource { if r == nil { return nil } @@ -20895,7 +23231,7 @@ func (r *RepositoryRulesetEditedChanges) GetEnforcement() *RepositoryRulesetEdit } // GetName returns the Name field. -func (r *RepositoryRulesetEditedChanges) GetName() *RepositoryRulesetEditedSource { +func (r *RepositoryRulesetChanges) GetName() *RepositoryRulesetChangeSource { if r == nil { return nil } @@ -20903,59 +23239,67 @@ func (r *RepositoryRulesetEditedChanges) GetName() *RepositoryRulesetEditedSourc } // GetRules returns the Rules field. -func (r *RepositoryRulesetEditedChanges) GetRules() *RepositoryRulesetEditedRules { +func (r *RepositoryRulesetChanges) GetRules() *RepositoryRulesetChangedRules { if r == nil { return nil } return r.Rules } -// GetConfiguration returns the Configuration field. -func (r *RepositoryRulesetEditedRuleChanges) GetConfiguration() *RepositoryRulesetEditedSources { +// GetFrom returns the From field if it's non-nil, zero value otherwise. +func (r *RepositoryRulesetChangeSource) GetFrom() string { + if r == nil || r.From == nil { + return "" + } + return *r.From +} + +// GetOrganizationID returns the OrganizationID field. +func (r *RepositoryRulesetConditions) GetOrganizationID() *RepositoryRulesetOrganizationIDsConditionParameters { if r == nil { return nil } - return r.Configuration + return r.OrganizationID } -// GetPattern returns the Pattern field. -func (r *RepositoryRulesetEditedRuleChanges) GetPattern() *RepositoryRulesetEditedSources { +// GetOrganizationName returns the OrganizationName field. +func (r *RepositoryRulesetConditions) GetOrganizationName() *RepositoryRulesetOrganizationNamesConditionParameters { if r == nil { return nil } - return r.Pattern + return r.OrganizationName } -// GetRuleType returns the RuleType field. -func (r *RepositoryRulesetEditedRuleChanges) GetRuleType() *RepositoryRulesetEditedSources { +// GetRefName returns the RefName field. +func (r *RepositoryRulesetConditions) GetRefName() *RepositoryRulesetRefConditionParameters { if r == nil { return nil } - return r.RuleType + return r.RefName } -// GetFrom returns the From field if it's non-nil, zero value otherwise. -func (r *RepositoryRulesetEditedSource) GetFrom() string { - if r == nil || r.From == nil { - return "" +// GetRepositoryID returns the RepositoryID field. +func (r *RepositoryRulesetConditions) GetRepositoryID() *RepositoryRulesetRepositoryIDsConditionParameters { + if r == nil { + return nil } - return *r.From + return r.RepositoryID } -// GetChanges returns the Changes field. -func (r *RepositoryRulesetEditedUpdatedConditions) GetChanges() *RepositoryRulesetUpdatedConditionsEdited { +// GetRepositoryName returns the RepositoryName field. +func (r *RepositoryRulesetConditions) GetRepositoryName() *RepositoryRulesetRepositoryNamesConditionParameters { if r == nil { return nil } - return r.Changes + return r.RepositoryName } -// GetCondition returns the Condition field. -func (r *RepositoryRulesetEditedUpdatedConditions) GetCondition() *RepositoryRulesetRefCondition { +// GetRepositoryProperty returns the RepositoryProperty field. +func (r *RepositoryRulesetConditions) GetRepositoryProperty() *RepositoryRulesetRepositoryPropertyConditionParameters { if r == nil { return nil } - return r.Condition + return r.RepositoryProperty } // GetAction returns the Action field if it's non-nil, zero value otherwise. @@ -20967,7 +23311,7 @@ func (r *RepositoryRulesetEvent) GetAction() string { } // GetChanges returns the Changes field. -func (r *RepositoryRulesetEvent) GetChanges() *RepositoryRulesetEditedChanges { +func (r *RepositoryRulesetEvent) GetChanges() *RepositoryRulesetChanges { if r == nil { return nil } @@ -21022,24 +23366,16 @@ func (r *RepositoryRulesetEvent) GetSender() *User { return r.Sender } -// GetParameters returns the Parameters field. -func (r *RepositoryRulesetFileExtensionRestrictionRule) GetParameters() *RuleFileExtensionRestrictionParameters { - if r == nil { - return nil - } - return r.Parameters -} - -// GetParameters returns the Parameters field. -func (r *RepositoryRulesetFilePathRestrictionRule) GetParameters() *RuleFileParameters { - if r == nil { - return nil +// GetHRef returns the HRef field if it's non-nil, zero value otherwise. +func (r *RepositoryRulesetLink) GetHRef() string { + if r == nil || r.HRef == nil { + return "" } - return r.Parameters + return *r.HRef } // GetHTML returns the HTML field. -func (r *RepositoryRulesetLink) GetHTML() *RulesetLink { +func (r *RepositoryRulesetLinks) GetHTML() *RepositoryRulesetLink { if r == nil { return nil } @@ -21047,79 +23383,31 @@ func (r *RepositoryRulesetLink) GetHTML() *RulesetLink { } // GetSelf returns the Self field. -func (r *RepositoryRulesetLink) GetSelf() *RulesetLink { +func (r *RepositoryRulesetLinks) GetSelf() *RepositoryRulesetLink { if r == nil { return nil } return r.Self } -// GetParameters returns the Parameters field. -func (r *RepositoryRulesetMaxFilePathLengthRule) GetParameters() *RuleMaxFilePathLengthParameters { - if r == nil { - return nil - } - return r.Parameters -} - -// GetParameters returns the Parameters field. -func (r *RepositoryRulesetMaxFileSizeRule) GetParameters() *RuleMaxFileSizeParameters { - if r == nil { - return nil - } - return r.Parameters -} - -// GetParameters returns the Parameters field. -func (r *RepositoryRulesetMergeQueueRule) GetParameters() *MergeQueueRuleParameters { - if r == nil { - return nil - } - return r.Parameters -} - -// GetParameters returns the Parameters field. -func (r *RepositoryRulesetPatternRule) GetParameters() *RulePatternParameters { - if r == nil { - return nil - } - return r.Parameters -} - -// GetParameters returns the Parameters field. -func (r *RepositoryRulesetPullRequestRule) GetParameters() *PullRequestRuleParameters { - if r == nil { - return nil - } - return r.Parameters -} - -// GetRefName returns the RefName field. -func (r *RepositoryRulesetRefCondition) GetRefName() *RulesetRefConditionParameters { - if r == nil { - return nil - } - return r.RefName -} - -// GetParameters returns the Parameters field. -func (r *RepositoryRulesetRequiredDeploymentsRule) GetParameters() *RequiredDeploymentEnvironmentsRuleParameters { - if r == nil { - return nil +// GetProtected returns the Protected field if it's non-nil, zero value otherwise. +func (r *RepositoryRulesetRepositoryNamesConditionParameters) GetProtected() bool { + if r == nil || r.Protected == nil { + return false } - return r.Parameters + return *r.Protected } -// GetParameters returns the Parameters field. -func (r *RepositoryRulesetRequiredStatusChecksRule) GetParameters() *RequiredStatusChecksRuleParameters { - if r == nil { - return nil +// GetSource returns the Source field if it's non-nil, zero value otherwise. +func (r *RepositoryRulesetRepositoryPropertyTargetParameters) GetSource() string { + if r == nil || r.Source == nil { + return "" } - return r.Parameters + return *r.Source } // GetBranchNamePattern returns the BranchNamePattern field. -func (r *RepositoryRulesetRule) GetBranchNamePattern() *RepositoryRulesetPatternRule { +func (r *RepositoryRulesetRules) GetBranchNamePattern() *PatternRuleParameters { if r == nil { return nil } @@ -21127,7 +23415,7 @@ func (r *RepositoryRulesetRule) GetBranchNamePattern() *RepositoryRulesetPattern } // GetCodeScanning returns the CodeScanning field. -func (r *RepositoryRulesetRule) GetCodeScanning() *RepositoryRulesetCodeScanningRule { +func (r *RepositoryRulesetRules) GetCodeScanning() *CodeScanningRuleParameters { if r == nil { return nil } @@ -21135,7 +23423,7 @@ func (r *RepositoryRulesetRule) GetCodeScanning() *RepositoryRulesetCodeScanning } // GetCommitAuthorEmailPattern returns the CommitAuthorEmailPattern field. -func (r *RepositoryRulesetRule) GetCommitAuthorEmailPattern() *RepositoryRulesetPatternRule { +func (r *RepositoryRulesetRules) GetCommitAuthorEmailPattern() *PatternRuleParameters { if r == nil { return nil } @@ -21143,7 +23431,7 @@ func (r *RepositoryRulesetRule) GetCommitAuthorEmailPattern() *RepositoryRuleset } // GetCommitMessagePattern returns the CommitMessagePattern field. -func (r *RepositoryRulesetRule) GetCommitMessagePattern() *RepositoryRulesetPatternRule { +func (r *RepositoryRulesetRules) GetCommitMessagePattern() *PatternRuleParameters { if r == nil { return nil } @@ -21151,7 +23439,7 @@ func (r *RepositoryRulesetRule) GetCommitMessagePattern() *RepositoryRulesetPatt } // GetCommitterEmailPattern returns the CommitterEmailPattern field. -func (r *RepositoryRulesetRule) GetCommitterEmailPattern() *RepositoryRulesetPatternRule { +func (r *RepositoryRulesetRules) GetCommitterEmailPattern() *PatternRuleParameters { if r == nil { return nil } @@ -21159,7 +23447,7 @@ func (r *RepositoryRulesetRule) GetCommitterEmailPattern() *RepositoryRulesetPat } // GetCreation returns the Creation field. -func (r *RepositoryRulesetRule) GetCreation() *RepositoryRulesetRuleType { +func (r *RepositoryRulesetRules) GetCreation() *EmptyRuleParameters { if r == nil { return nil } @@ -21167,7 +23455,7 @@ func (r *RepositoryRulesetRule) GetCreation() *RepositoryRulesetRuleType { } // GetDeletion returns the Deletion field. -func (r *RepositoryRulesetRule) GetDeletion() *RepositoryRulesetRuleType { +func (r *RepositoryRulesetRules) GetDeletion() *EmptyRuleParameters { if r == nil { return nil } @@ -21175,7 +23463,7 @@ func (r *RepositoryRulesetRule) GetDeletion() *RepositoryRulesetRuleType { } // GetFileExtensionRestriction returns the FileExtensionRestriction field. -func (r *RepositoryRulesetRule) GetFileExtensionRestriction() *RepositoryRulesetFileExtensionRestrictionRule { +func (r *RepositoryRulesetRules) GetFileExtensionRestriction() *FileExtensionRestrictionRuleParameters { if r == nil { return nil } @@ -21183,7 +23471,7 @@ func (r *RepositoryRulesetRule) GetFileExtensionRestriction() *RepositoryRuleset } // GetFilePathRestriction returns the FilePathRestriction field. -func (r *RepositoryRulesetRule) GetFilePathRestriction() *RepositoryRulesetFilePathRestrictionRule { +func (r *RepositoryRulesetRules) GetFilePathRestriction() *FilePathRestrictionRuleParameters { if r == nil { return nil } @@ -21191,7 +23479,7 @@ func (r *RepositoryRulesetRule) GetFilePathRestriction() *RepositoryRulesetFileP } // GetMaxFilePathLength returns the MaxFilePathLength field. -func (r *RepositoryRulesetRule) GetMaxFilePathLength() *RepositoryRulesetMaxFilePathLengthRule { +func (r *RepositoryRulesetRules) GetMaxFilePathLength() *MaxFilePathLengthRuleParameters { if r == nil { return nil } @@ -21199,7 +23487,7 @@ func (r *RepositoryRulesetRule) GetMaxFilePathLength() *RepositoryRulesetMaxFile } // GetMaxFileSize returns the MaxFileSize field. -func (r *RepositoryRulesetRule) GetMaxFileSize() *RepositoryRulesetMaxFileSizeRule { +func (r *RepositoryRulesetRules) GetMaxFileSize() *MaxFileSizeRuleParameters { if r == nil { return nil } @@ -21207,7 +23495,7 @@ func (r *RepositoryRulesetRule) GetMaxFileSize() *RepositoryRulesetMaxFileSizeRu } // GetMergeQueue returns the MergeQueue field. -func (r *RepositoryRulesetRule) GetMergeQueue() *RepositoryRulesetMergeQueueRule { +func (r *RepositoryRulesetRules) GetMergeQueue() *MergeQueueRuleParameters { if r == nil { return nil } @@ -21215,7 +23503,7 @@ func (r *RepositoryRulesetRule) GetMergeQueue() *RepositoryRulesetMergeQueueRule } // GetNonFastForward returns the NonFastForward field. -func (r *RepositoryRulesetRule) GetNonFastForward() *RepositoryRulesetRuleType { +func (r *RepositoryRulesetRules) GetNonFastForward() *EmptyRuleParameters { if r == nil { return nil } @@ -21223,7 +23511,7 @@ func (r *RepositoryRulesetRule) GetNonFastForward() *RepositoryRulesetRuleType { } // GetPullRequest returns the PullRequest field. -func (r *RepositoryRulesetRule) GetPullRequest() *RepositoryRulesetPullRequestRule { +func (r *RepositoryRulesetRules) GetPullRequest() *PullRequestRuleParameters { if r == nil { return nil } @@ -21231,7 +23519,7 @@ func (r *RepositoryRulesetRule) GetPullRequest() *RepositoryRulesetPullRequestRu } // GetRequiredDeployments returns the RequiredDeployments field. -func (r *RepositoryRulesetRule) GetRequiredDeployments() *RepositoryRulesetRequiredDeploymentsRule { +func (r *RepositoryRulesetRules) GetRequiredDeployments() *RequiredDeploymentsRuleParameters { if r == nil { return nil } @@ -21239,7 +23527,7 @@ func (r *RepositoryRulesetRule) GetRequiredDeployments() *RepositoryRulesetRequi } // GetRequiredLinearHistory returns the RequiredLinearHistory field. -func (r *RepositoryRulesetRule) GetRequiredLinearHistory() *RepositoryRulesetRuleType { +func (r *RepositoryRulesetRules) GetRequiredLinearHistory() *EmptyRuleParameters { if r == nil { return nil } @@ -21247,7 +23535,7 @@ func (r *RepositoryRulesetRule) GetRequiredLinearHistory() *RepositoryRulesetRul } // GetRequiredSignatures returns the RequiredSignatures field. -func (r *RepositoryRulesetRule) GetRequiredSignatures() *RepositoryRulesetRuleType { +func (r *RepositoryRulesetRules) GetRequiredSignatures() *EmptyRuleParameters { if r == nil { return nil } @@ -21255,7 +23543,7 @@ func (r *RepositoryRulesetRule) GetRequiredSignatures() *RepositoryRulesetRuleTy } // GetRequiredStatusChecks returns the RequiredStatusChecks field. -func (r *RepositoryRulesetRule) GetRequiredStatusChecks() *RepositoryRulesetRequiredStatusChecksRule { +func (r *RepositoryRulesetRules) GetRequiredStatusChecks() *RequiredStatusChecksRuleParameters { if r == nil { return nil } @@ -21263,7 +23551,7 @@ func (r *RepositoryRulesetRule) GetRequiredStatusChecks() *RepositoryRulesetRequ } // GetTagNamePattern returns the TagNamePattern field. -func (r *RepositoryRulesetRule) GetTagNamePattern() *RepositoryRulesetPatternRule { +func (r *RepositoryRulesetRules) GetTagNamePattern() *PatternRuleParameters { if r == nil { return nil } @@ -21271,7 +23559,7 @@ func (r *RepositoryRulesetRule) GetTagNamePattern() *RepositoryRulesetPatternRul } // GetUpdate returns the Update field. -func (r *RepositoryRulesetRule) GetUpdate() *RepositoryRulesetUpdateRule { +func (r *RepositoryRulesetRules) GetUpdate() *UpdateRuleParameters { if r == nil { return nil } @@ -21279,7 +23567,7 @@ func (r *RepositoryRulesetRule) GetUpdate() *RepositoryRulesetUpdateRule { } // GetWorkflows returns the Workflows field. -func (r *RepositoryRulesetRule) GetWorkflows() *RepositoryRulesetWorkflowsRule { +func (r *RepositoryRulesetRules) GetWorkflows() *WorkflowsRuleParameters { if r == nil { return nil } @@ -21287,7 +23575,7 @@ func (r *RepositoryRulesetRule) GetWorkflows() *RepositoryRulesetWorkflowsRule { } // GetConditionType returns the ConditionType field. -func (r *RepositoryRulesetUpdatedConditionsEdited) GetConditionType() *RepositoryRulesetEditedSource { +func (r *RepositoryRulesetUpdatedCondition) GetConditionType() *RepositoryRulesetChangeSource { if r == nil { return nil } @@ -21295,7 +23583,7 @@ func (r *RepositoryRulesetUpdatedConditionsEdited) GetConditionType() *Repositor } // GetExclude returns the Exclude field. -func (r *RepositoryRulesetUpdatedConditionsEdited) GetExclude() *RepositoryRulesetEditedSources { +func (r *RepositoryRulesetUpdatedCondition) GetExclude() *RepositoryRulesetChangeSources { if r == nil { return nil } @@ -21303,7 +23591,7 @@ func (r *RepositoryRulesetUpdatedConditionsEdited) GetExclude() *RepositoryRules } // GetInclude returns the Include field. -func (r *RepositoryRulesetUpdatedConditionsEdited) GetInclude() *RepositoryRulesetEditedSources { +func (r *RepositoryRulesetUpdatedCondition) GetInclude() *RepositoryRulesetChangeSources { if r == nil { return nil } @@ -21311,7 +23599,7 @@ func (r *RepositoryRulesetUpdatedConditionsEdited) GetInclude() *RepositoryRules } // GetTarget returns the Target field. -func (r *RepositoryRulesetUpdatedConditionsEdited) GetTarget() *RepositoryRulesetEditedSource { +func (r *RepositoryRulesetUpdatedCondition) GetTarget() *RepositoryRulesetChangeSource { if r == nil { return nil } @@ -21319,35 +23607,35 @@ func (r *RepositoryRulesetUpdatedConditionsEdited) GetTarget() *RepositoryRulese } // GetChanges returns the Changes field. -func (r *RepositoryRulesetUpdatedRules) GetChanges() *RepositoryRulesetEditedRuleChanges { +func (r *RepositoryRulesetUpdatedConditions) GetChanges() *RepositoryRulesetUpdatedCondition { if r == nil { return nil } return r.Changes } -// GetRule returns the Rule field. -func (r *RepositoryRulesetUpdatedRules) GetRule() *RepositoryRulesetRule { +// GetCondition returns the Condition field. +func (r *RepositoryRulesetUpdatedConditions) GetCondition() *RepositoryRulesetConditions { if r == nil { return nil } - return r.Rule + return r.Condition } -// GetParameters returns the Parameters field. -func (r *RepositoryRulesetUpdateRule) GetParameters() *UpdateAllowsFetchAndMergeRuleParameters { +// GetChanges returns the Changes field. +func (r *RepositoryRulesetUpdatedRules) GetChanges() *RepositoryRulesetChangedRule { if r == nil { return nil } - return r.Parameters + return r.Changes } -// GetParameters returns the Parameters field. -func (r *RepositoryRulesetWorkflowsRule) GetParameters() *RequiredWorkflowsRuleParameters { +// GetRule returns the Rule field. +func (r *RepositoryRulesetUpdatedRules) GetRule() *RepositoryRule { if r == nil { return nil } - return r.Parameters + return r.Rule } // GetCommit returns the Commit field. @@ -21838,32 +24126,8 @@ func (r *Rule) GetSeverity() string { return *r.Severity } -// GetRestrictedFilePaths returns the RestrictedFilePaths field if it's non-nil, zero value otherwise. -func (r *RuleFileParameters) GetRestrictedFilePaths() []string { - if r == nil || r.RestrictedFilePaths == nil { - return nil - } - return *r.RestrictedFilePaths -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (r *RulePatternParameters) GetName() string { - if r == nil || r.Name == nil { - return "" - } - return *r.Name -} - -// GetNegate returns the Negate field if it's non-nil, zero value otherwise. -func (r *RulePatternParameters) GetNegate() bool { - if r == nil || r.Negate == nil { - return false - } - return *r.Negate -} - // GetIntegrationID returns the IntegrationID field if it's non-nil, zero value otherwise. -func (r *RuleRequiredStatusChecks) GetIntegrationID() int64 { +func (r *RuleStatusCheck) GetIntegrationID() int64 { if r == nil || r.IntegrationID == nil { return 0 } @@ -21871,7 +24135,7 @@ func (r *RuleRequiredStatusChecks) GetIntegrationID() int64 { } // GetRef returns the Ref field if it's non-nil, zero value otherwise. -func (r *RuleRequiredWorkflow) GetRef() string { +func (r *RuleWorkflow) GetRef() string { if r == nil || r.Ref == nil { return "" } @@ -21879,147 +24143,19 @@ func (r *RuleRequiredWorkflow) GetRef() string { } // GetRepositoryID returns the RepositoryID field if it's non-nil, zero value otherwise. -func (r *RuleRequiredWorkflow) GetRepositoryID() int64 { +func (r *RuleWorkflow) GetRepositoryID() int64 { if r == nil || r.RepositoryID == nil { return 0 } return *r.RepositoryID } -// GetSha returns the Sha field if it's non-nil, zero value otherwise. -func (r *RuleRequiredWorkflow) GetSha() string { - if r == nil || r.Sha == nil { - return "" - } - return *r.Sha -} - -// GetConditions returns the Conditions field. -func (r *Ruleset) GetConditions() *RulesetConditions { - if r == nil { - return nil - } - return r.Conditions -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (r *Ruleset) GetCreatedAt() Timestamp { - if r == nil || r.CreatedAt == nil { - return Timestamp{} - } - return *r.CreatedAt -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (r *Ruleset) GetID() int64 { - if r == nil || r.ID == nil { - return 0 - } - return *r.ID -} - -// GetLinks returns the Links field. -func (r *Ruleset) GetLinks() *RulesetLinks { - if r == nil { - return nil - } - return r.Links -} - -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (r *Ruleset) GetNodeID() string { - if r == nil || r.NodeID == nil { - return "" - } - return *r.NodeID -} - -// GetSourceType returns the SourceType field if it's non-nil, zero value otherwise. -func (r *Ruleset) GetSourceType() string { - if r == nil || r.SourceType == nil { - return "" - } - return *r.SourceType -} - -// GetTarget returns the Target field if it's non-nil, zero value otherwise. -func (r *Ruleset) GetTarget() string { - if r == nil || r.Target == nil { - return "" - } - return *r.Target -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (r *Ruleset) GetUpdatedAt() Timestamp { - if r == nil || r.UpdatedAt == nil { - return Timestamp{} - } - return *r.UpdatedAt -} - -// GetRefName returns the RefName field. -func (r *RulesetConditions) GetRefName() *RulesetRefConditionParameters { - if r == nil { - return nil - } - return r.RefName -} - -// GetRepositoryID returns the RepositoryID field. -func (r *RulesetConditions) GetRepositoryID() *RulesetRepositoryIDsConditionParameters { - if r == nil { - return nil - } - return r.RepositoryID -} - -// GetRepositoryName returns the RepositoryName field. -func (r *RulesetConditions) GetRepositoryName() *RulesetRepositoryNamesConditionParameters { - if r == nil { - return nil - } - return r.RepositoryName -} - -// GetRepositoryProperty returns the RepositoryProperty field. -func (r *RulesetConditions) GetRepositoryProperty() *RulesetRepositoryPropertyConditionParameters { - if r == nil { - return nil - } - return r.RepositoryProperty -} - -// GetHRef returns the HRef field if it's non-nil, zero value otherwise. -func (r *RulesetLink) GetHRef() string { - if r == nil || r.HRef == nil { - return "" - } - return *r.HRef -} - -// GetSelf returns the Self field. -func (r *RulesetLinks) GetSelf() *RulesetLink { - if r == nil { - return nil - } - return r.Self -} - -// GetProtected returns the Protected field if it's non-nil, zero value otherwise. -func (r *RulesetRepositoryNamesConditionParameters) GetProtected() bool { - if r == nil || r.Protected == nil { - return false - } - return *r.Protected -} - -// GetSource returns the Source field if it's non-nil, zero value otherwise. -func (r *RulesetRepositoryPropertyTargetParameters) GetSource() string { - if r == nil || r.Source == nil { +// GetSHA returns the SHA field if it's non-nil, zero value otherwise. +func (r *RuleWorkflow) GetSHA() string { + if r == nil || r.SHA == nil { return "" } - return *r.Source + return *r.SHA } // GetBusy returns the Busy field if it's non-nil, zero value otherwise. @@ -22470,6 +24606,46 @@ func (s *ScanningAnalysis) GetWarning() string { return *s.Warning } +// GetDisplay returns the Display field if it's non-nil, zero value otherwise. +func (s *SCIMDisplayReference) GetDisplay() string { + if s == nil || s.Display == nil { + return "" + } + return *s.Display +} + +// GetDisplayName returns the DisplayName field if it's non-nil, zero value otherwise. +func (s *SCIMGroupAttributes) GetDisplayName() string { + if s == nil || s.DisplayName == nil { + return "" + } + return *s.DisplayName +} + +// GetExternalID returns the ExternalID field if it's non-nil, zero value otherwise. +func (s *SCIMGroupAttributes) GetExternalID() string { + if s == nil || s.ExternalID == nil { + return "" + } + return *s.ExternalID +} + +// GetID returns the ID field if it's non-nil, zero value otherwise. +func (s *SCIMGroupAttributes) GetID() string { + if s == nil || s.ID == nil { + return "" + } + return *s.ID +} + +// GetMeta returns the Meta field. +func (s *SCIMGroupAttributes) GetMeta() *SCIMMeta { + if s == nil { + return nil + } + return s.Meta +} + // GetCreated returns the Created field if it's non-nil, zero value otherwise. func (s *SCIMMeta) GetCreated() Timestamp { if s == nil || s.Created == nil { @@ -22502,6 +24678,30 @@ func (s *SCIMMeta) GetResourceType() string { return *s.ResourceType } +// GetItemsPerPage returns the ItemsPerPage field if it's non-nil, zero value otherwise. +func (s *SCIMProvisionedGroups) GetItemsPerPage() int { + if s == nil || s.ItemsPerPage == nil { + return 0 + } + return *s.ItemsPerPage +} + +// GetStartIndex returns the StartIndex field if it's non-nil, zero value otherwise. +func (s *SCIMProvisionedGroups) GetStartIndex() int { + if s == nil || s.StartIndex == nil { + return 0 + } + return *s.StartIndex +} + +// GetTotalResults returns the TotalResults field if it's non-nil, zero value otherwise. +func (s *SCIMProvisionedGroups) GetTotalResults() int { + if s == nil || s.TotalResults == nil { + return 0 + } + return *s.TotalResults +} + // GetItemsPerPage returns the ItemsPerPage field if it's non-nil, zero value otherwise. func (s *SCIMProvisionedIdentities) GetItemsPerPage() int { if s == nil || s.ItemsPerPage == nil { @@ -23534,6 +25734,38 @@ func (s *SponsorshipTier) GetFrom() string { return *s.From } +// GetHostname returns the Hostname field if it's non-nil, zero value otherwise. +func (s *SSHKeyStatus) GetHostname() string { + if s == nil || s.Hostname == nil { + return "" + } + return *s.Hostname +} + +// GetMessage returns the Message field if it's non-nil, zero value otherwise. +func (s *SSHKeyStatus) GetMessage() string { + if s == nil || s.Message == nil { + return "" + } + return *s.Message +} + +// GetModified returns the Modified field if it's non-nil, zero value otherwise. +func (s *SSHKeyStatus) GetModified() bool { + if s == nil || s.Modified == nil { + return false + } + return *s.Modified +} + +// GetUUID returns the UUID field if it's non-nil, zero value otherwise. +func (s *SSHKeyStatus) GetUUID() string { + if s == nil || s.UUID == nil { + return "" + } + return *s.UUID +} + // GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. func (s *SSHSigningKey) GetCreatedAt() Timestamp { if s == nil || s.CreatedAt == nil { @@ -23814,6 +26046,46 @@ func (s *Subscription) GetURL() string { return *s.URL } +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (s *SystemRequirements) GetStatus() string { + if s == nil || s.Status == nil { + return "" + } + return *s.Status +} + +// GetHostname returns the Hostname field if it's non-nil, zero value otherwise. +func (s *SystemRequirementsNode) GetHostname() string { + if s == nil || s.Hostname == nil { + return "" + } + return *s.Hostname +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (s *SystemRequirementsNode) GetStatus() string { + if s == nil || s.Status == nil { + return "" + } + return *s.Status +} + +// GetRole returns the Role field if it's non-nil, zero value otherwise. +func (s *SystemRequirementsNodeRoleStatus) GetRole() string { + if s == nil || s.Role == nil { + return "" + } + return *s.Role +} + +// GetStatus returns the Status field if it's non-nil, zero value otherwise. +func (s *SystemRequirementsNodeRoleStatus) GetStatus() string { + if s == nil || s.Status == nil { + return "" + } + return *s.Status +} + // GetMessage returns the Message field if it's non-nil, zero value otherwise. func (t *Tag) GetMessage() string { if t == nil || t.Message == nil { @@ -24014,6 +26286,14 @@ func (t *Team) GetNodeID() string { return *t.NodeID } +// GetNotificationSetting returns the NotificationSetting field if it's non-nil, zero value otherwise. +func (t *Team) GetNotificationSetting() string { + if t == nil || t.NotificationSetting == nil { + return "" + } + return *t.NotificationSetting +} + // GetOrganization returns the Organization field. func (t *Team) GetOrganization() *Organization { if t == nil { @@ -25390,14 +27670,6 @@ func (u *User) GetID() int64 { return *u.ID } -// GetInheritedFrom returns the InheritedFrom field. -func (u *User) GetInheritedFrom() *Team { - if u == nil { - return nil - } - return u.InheritedFrom -} - // GetLdapDn returns the LdapDn field if it's non-nil, zero value otherwise. func (u *User) GetLdapDn() string { if u == nil || u.LdapDn == nil { @@ -26942,6 +29214,14 @@ func (w *Workflows) GetTotalCount() int { return *w.TotalCount } +// GetDoNotEnforceOnCreate returns the DoNotEnforceOnCreate field if it's non-nil, zero value otherwise. +func (w *WorkflowsRuleParameters) GetDoNotEnforceOnCreate() bool { + if w == nil || w.DoNotEnforceOnCreate == nil { + return false + } + return *w.DoNotEnforceOnCreate +} + // GetBillable returns the Billable field. func (w *WorkflowUsage) GetBillable() *WorkflowBillMap { if w == nil { diff --git a/vendor/github.com/google/go-github/v68/github/github.go b/vendor/github.com/google/go-github/v70/github/github.go similarity index 86% rename from vendor/github.com/google/go-github/v68/github/github.go rename to vendor/github.com/google/go-github/v70/github/github.go index 4e5a33c67e..f31473edff 100644 --- a/vendor/github.com/google/go-github/v68/github/github.go +++ b/vendor/github.com/google/go-github/v70/github/github.go @@ -19,6 +19,7 @@ import ( "net/http" "net/url" "reflect" + "regexp" "strconv" "strings" "sync" @@ -28,7 +29,7 @@ import ( ) const ( - Version = "v68.0.0" + Version = "v70.0.0" defaultAPIVersion = "2022-11-28" defaultBaseURL = "https://api.github.com/" @@ -38,7 +39,9 @@ const ( headerAPIVersion = "X-Github-Api-Version" headerRateLimit = "X-Ratelimit-Limit" headerRateRemaining = "X-Ratelimit-Remaining" + headerRateUsed = "X-Ratelimit-Used" headerRateReset = "X-Ratelimit-Reset" + headerRateResource = "X-Ratelimit-Resource" headerOTP = "X-Github-Otp" headerRetryAfter = "Retry-After" @@ -155,8 +158,9 @@ var errNonNilContext = errors.New("context must be non-nil") // A Client manages communication with the GitHub API. type Client struct { - clientMu sync.Mutex // clientMu protects the client during calls that modify the CheckRedirect func. - client *http.Client // HTTP client used to communicate with the API. + clientMu sync.Mutex // clientMu protects the client during calls that modify the CheckRedirect func. + client *http.Client // HTTP client used to communicate with the API. + clientIgnoreRedirects *http.Client // HTTP client used to communicate with the API on endpoints where we don't want to follow redirects. // Base URL for API requests. Defaults to the public GitHub API, but can be // set to a domain endpoint to use with GitHub Enterprise. BaseURL should @@ -173,6 +177,13 @@ type Client struct { rateLimits [Categories]Rate // Rate limits for the client as determined by the most recent API calls. secondaryRateLimitReset time.Time // Secondary rate limit reset for the client as determined by the most recent API calls. + // If specified, Client will block requests for at most this duration in case of reaching a secondary + // rate limit + MaxSecondaryRateLimitRetryAfterDuration time.Duration + + // Whether to respect rate limit headers on endpoints that return 302 redirections to artifacts + RateLimitRedirectionalEndpoints bool + common service // Reuse a single struct instead of allocating one for each service on the heap. // Services used for talking to different parts of the GitHub API. @@ -394,6 +405,14 @@ func (c *Client) initialize() { if c.client == nil { c.client = &http.Client{} } + // Copy the main http client into the IgnoreRedirects one, overriding the `CheckRedirect` func + c.clientIgnoreRedirects = &http.Client{} + c.clientIgnoreRedirects.Transport = c.client.Transport + c.clientIgnoreRedirects.Timeout = c.client.Timeout + c.clientIgnoreRedirects.Jar = c.client.Jar + c.clientIgnoreRedirects.CheckRedirect = func(req *http.Request, via []*http.Request) error { + return http.ErrUseLastResponse + } if c.BaseURL == nil { c.BaseURL, _ = url.Parse(defaultBaseURL) } @@ -448,11 +467,12 @@ func (c *Client) copy() *Client { c.clientMu.Lock() // can't use *c here because that would copy mutexes by value. clone := Client{ - client: &http.Client{}, - UserAgent: c.UserAgent, - BaseURL: c.BaseURL, - UploadURL: c.UploadURL, - secondaryRateLimitReset: c.secondaryRateLimitReset, + client: &http.Client{}, + UserAgent: c.UserAgent, + BaseURL: c.BaseURL, + UploadURL: c.UploadURL, + RateLimitRedirectionalEndpoints: c.RateLimitRedirectionalEndpoints, + secondaryRateLimitReset: c.secondaryRateLimitReset, } c.clientMu.Unlock() if c.client != nil { @@ -506,7 +526,7 @@ func WithVersion(version string) RequestOption { // request body. func (c *Client) NewRequest(method, urlStr string, body interface{}, opts ...RequestOption) (*http.Request, error) { if !strings.HasSuffix(c.BaseURL.Path, "/") { - return nil, fmt.Errorf("BaseURL must have a trailing slash, but %q does not", c.BaseURL) + return nil, fmt.Errorf("baseURL must have a trailing slash, but %q does not", c.BaseURL) } u, err := c.BaseURL.Parse(urlStr) @@ -552,7 +572,7 @@ func (c *Client) NewRequest(method, urlStr string, body interface{}, opts ...Req // Body is sent with Content-Type: application/x-www-form-urlencoded. func (c *Client) NewFormRequest(urlStr string, body io.Reader, opts ...RequestOption) (*http.Request, error) { if !strings.HasSuffix(c.BaseURL.Path, "/") { - return nil, fmt.Errorf("BaseURL must have a trailing slash, but %q does not", c.BaseURL) + return nil, fmt.Errorf("baseURL must have a trailing slash, but %q does not", c.BaseURL) } u, err := c.BaseURL.Parse(urlStr) @@ -584,7 +604,7 @@ func (c *Client) NewFormRequest(urlStr string, body io.Reader, opts ...RequestOp // Relative URLs should always be specified without a preceding slash. func (c *Client) NewUploadRequest(urlStr string, reader io.Reader, size int64, mediaType string, opts ...RequestOption) (*http.Request, error) { if !strings.HasSuffix(c.UploadURL.Path, "/") { - return nil, fmt.Errorf("UploadURL must have a trailing slash, but %q does not", c.UploadURL) + return nil, fmt.Errorf("uploadURL must have a trailing slash, but %q does not", c.UploadURL) } u, err := c.UploadURL.Parse(urlStr) if err != nil { @@ -750,11 +770,17 @@ func parseRate(r *http.Response) Rate { if remaining := r.Header.Get(headerRateRemaining); remaining != "" { rate.Remaining, _ = strconv.Atoi(remaining) } + if used := r.Header.Get(headerRateUsed); used != "" { + rate.Used, _ = strconv.Atoi(used) + } if reset := r.Header.Get(headerRateReset); reset != "" { if v, _ := strconv.ParseInt(reset, 10, 64); v != 0 { rate.Reset = Timestamp{time.Unix(v, 0)} } } + if resource := r.Header.Get(headerRateResource); resource != "" { + rate.Resource = resource + } return rate } @@ -801,19 +827,23 @@ func parseTokenExpiration(r *http.Response) Timestamp { type requestContext uint8 const ( - bypassRateLimitCheck requestContext = iota + // BypassRateLimitCheck prevents a pre-emptive check for exceeded primary rate limits + // Specify this by providing a context with this key, e.g. + // context.WithValue(context.Background(), github.BypassRateLimitCheck, true) + BypassRateLimitCheck requestContext = iota + SleepUntilPrimaryRateLimitResetWhenRateLimited ) -// BareDo sends an API request and lets you handle the api response. If an error -// or API Error occurs, the error will contain more information. Otherwise you -// are supposed to read and close the response's Body. If rate limit is exceeded -// and reset time is in the future, BareDo returns *RateLimitError immediately -// without making a network API call. +// bareDo sends an API request using `caller` http.Client passed in the parameters +// and lets you handle the api response. If an error or API Error occurs, the error +// will contain more information. Otherwise you are supposed to read and close the +// response's Body. If rate limit is exceeded and reset time is in the future, +// bareDo returns *RateLimitError immediately without making a network API call. // // The provided ctx must be non-nil, if it is nil an error is returned. If it is // canceled or times out, ctx.Err() will be returned. -func (c *Client) BareDo(ctx context.Context, req *http.Request) (*Response, error) { +func (c *Client) bareDo(ctx context.Context, caller *http.Client, req *http.Request) (*Response, error) { if ctx == nil { return nil, errNonNilContext } @@ -822,7 +852,7 @@ func (c *Client) BareDo(ctx context.Context, req *http.Request) (*Response, erro rateLimitCategory := GetRateLimitCategory(req.Method, req.URL.Path) - if bypass := ctx.Value(bypassRateLimitCheck); bypass == nil { + if bypass := ctx.Value(BypassRateLimitCheck); bypass == nil { // If we've hit rate limit, don't make further requests before Reset time. if err := c.checkRateLimitBeforeDo(req, rateLimitCategory); err != nil { return &Response{ @@ -838,7 +868,7 @@ func (c *Client) BareDo(ctx context.Context, req *http.Request) (*Response, erro } } - resp, err := c.client.Do(req) + resp, err := caller.Do(req) var response *Response if resp != nil { response = newResponse(resp) @@ -897,12 +927,16 @@ func (c *Client) BareDo(ctx context.Context, req *http.Request) (*Response, erro return response, err } // retry the request once when the rate limit has reset - return c.BareDo(context.WithValue(req.Context(), SleepUntilPrimaryRateLimitResetWhenRateLimited, nil), req) + return c.bareDo(context.WithValue(req.Context(), SleepUntilPrimaryRateLimitResetWhenRateLimited, nil), caller, req) } // Update the secondary rate limit if we hit it. rerr, ok := err.(*AbuseRateLimitError) if ok && rerr.RetryAfter != nil { + // if a max duration is specified, make sure that we are waiting at most this duration + if c.MaxSecondaryRateLimitRetryAfterDuration > 0 && rerr.GetRetryAfter() > c.MaxSecondaryRateLimitRetryAfterDuration { + rerr.RetryAfter = &c.MaxSecondaryRateLimitRetryAfterDuration + } c.rateMu.Lock() c.secondaryRateLimitReset = time.Now().Add(*rerr.RetryAfter) c.rateMu.Unlock() @@ -911,6 +945,72 @@ func (c *Client) BareDo(ctx context.Context, req *http.Request) (*Response, erro return response, err } +// BareDo sends an API request and lets you handle the api response. If an error +// or API Error occurs, the error will contain more information. Otherwise you +// are supposed to read and close the response's Body. If rate limit is exceeded +// and reset time is in the future, BareDo returns *RateLimitError immediately +// without making a network API call. +// +// The provided ctx must be non-nil, if it is nil an error is returned. If it is +// canceled or times out, ctx.Err() will be returned. +func (c *Client) BareDo(ctx context.Context, req *http.Request) (*Response, error) { + return c.bareDo(ctx, c.client, req) +} + +// bareDoIgnoreRedirects has the exact same behavior as BareDo but stops at the first +// redirection code returned by the API. If a redirection is returned by the api, bareDoIgnoreRedirects +// returns a *RedirectionError. +// +// The provided ctx must be non-nil, if it is nil an error is returned. If it is +// canceled or times out, ctx.Err() will be returned. +func (c *Client) bareDoIgnoreRedirects(ctx context.Context, req *http.Request) (*Response, error) { + return c.bareDo(ctx, c.clientIgnoreRedirects, req) +} + +var errInvalidLocation = errors.New("invalid or empty Location header in redirection response") + +// bareDoUntilFound has the exact same behavior as BareDo but only follows 301s, up to maxRedirects times. If it receives +// a 302, it will parse the Location header into a *url.URL and return that. +// This is useful for endpoints that return a 302 in successful cases but still might return 301s for +// permanent redirections. +// +// The provided ctx must be non-nil, if it is nil an error is returned. If it is +// canceled or times out, ctx.Err() will be returned. +func (c *Client) bareDoUntilFound(ctx context.Context, req *http.Request, maxRedirects int) (*url.URL, *Response, error) { + response, err := c.bareDoIgnoreRedirects(ctx, req) + if err != nil { + rerr, ok := err.(*RedirectionError) + if ok { + // If we receive a 302, transform potential relative locations into absolute and return it. + if rerr.StatusCode == http.StatusFound { + if rerr.Location == nil { + return nil, nil, errInvalidLocation + } + newURL := c.BaseURL.ResolveReference(rerr.Location) + return newURL, response, nil + } + // If permanent redirect response is returned, follow it + if maxRedirects > 0 && rerr.StatusCode == http.StatusMovedPermanently { + if rerr.Location == nil { + return nil, nil, errInvalidLocation + } + newURL := c.BaseURL.ResolveReference(rerr.Location) + newRequest := req.Clone(ctx) + newRequest.URL = newURL + return c.bareDoUntilFound(ctx, newRequest, maxRedirects-1) + } + // If we reached the maximum amount of redirections, return an error + if maxRedirects <= 0 && rerr.StatusCode == http.StatusMovedPermanently { + return nil, response, fmt.Errorf("reached the maximum amount of redirections: %w", err) + } + return nil, response, fmt.Errorf("unexpected redirection response: %w", err) + } + } + + // If we don't receive a redirection, forward the response and potential error + return nil, response, err +} + // Do sends an API request and returns the API response. The API response is // JSON decoded and stored in the value pointed to by v, or returned as an // error if an API error has occurred. If v implements the io.Writer interface, @@ -1034,7 +1134,8 @@ GitHub API docs: https://docs.github.com/rest/#client-errors type ErrorResponse struct { Response *http.Response `json:"-"` // HTTP response that caused this error Message string `json:"message"` // error message - Errors []Error `json:"errors"` // more detail on individual errors + //nolint:sliceofpointers + Errors []Error `json:"errors"` // more detail on individual errors // Block is only populated on certain types of errors such as code 451. Block *ErrorBlock `json:"block,omitempty"` // Most errors will also include a documentation_url field pointing @@ -1196,6 +1297,40 @@ func (r *AbuseRateLimitError) Is(target error) bool { compareHTTPResponse(r.Response, v.Response) } +// RedirectionError represents a response that returned a redirect status code: +// +// 301 (Moved Permanently) +// 302 (Found) +// 303 (See Other) +// 307 (Temporary Redirect) +// 308 (Permanent Redirect) +// +// If there was a valid Location header included, it will be parsed to a URL. You should use +// `BaseURL.ResolveReference()` to enrich it with the correct hostname where needed. +type RedirectionError struct { + Response *http.Response // HTTP response that caused this error + StatusCode int + Location *url.URL // location header of the redirection if present +} + +func (r *RedirectionError) Error() string { + return fmt.Sprintf("%v %v: %d location %v", + r.Response.Request.Method, sanitizeURL(r.Response.Request.URL), + r.StatusCode, sanitizeURL(r.Location)) +} + +// Is returns whether the provided error equals this error. +func (r *RedirectionError) Is(target error) bool { + v, ok := target.(*RedirectionError) + if !ok { + return false + } + + return r.StatusCode == v.StatusCode && + (r.Location == v.Location || // either both locations are nil or exactly the same pointer + r.Location != nil && v.Location != nil && r.Location.String() == v.Location.String()) // or they are both not nil and marshaled identically +} + // sanitizeURL redacts the client_secret parameter from the URL which may be // exposed to the user. func sanitizeURL(uri *url.URL) *url.URL { @@ -1260,7 +1395,8 @@ func (e *Error) UnmarshalJSON(data []byte) error { // // The error type will be *RateLimitError for rate limit exceeded errors, // *AcceptedError for 202 Accepted status codes, -// and *TwoFactorAuthError for two-factor authentication errors. +// *TwoFactorAuthError for two-factor authentication errors, +// and *RedirectionError for redirect status codes (only happens when ignoring redirections). func CheckResponse(r *http.Response) error { if r.StatusCode == http.StatusAccepted { return &AcceptedError{} @@ -1302,6 +1438,25 @@ func CheckResponse(r *http.Response) error { abuseRateLimitError.RetryAfter = retryAfter } return abuseRateLimitError + // Check that the status code is a redirection and return a sentinel error that can be used to handle special cases + // where 302 is considered a successful result. + // This should never happen with the default `CheckRedirect`, because it would return a `url.Error` that should be handled upstream. + case r.StatusCode == http.StatusMovedPermanently || + r.StatusCode == http.StatusFound || + r.StatusCode == http.StatusSeeOther || + r.StatusCode == http.StatusTemporaryRedirect || + r.StatusCode == http.StatusPermanentRedirect: + + locationStr := r.Header.Get("Location") + var location *url.URL + if locationStr != "" { + location, _ = url.Parse(locationStr) + } + return &RedirectionError{ + Response: errorResponse.Response, + StatusCode: r.StatusCode, + Location: location, + } default: return errorResponse } @@ -1616,3 +1771,18 @@ type roundTripperFunc func(*http.Request) (*http.Response, error) func (fn roundTripperFunc) RoundTrip(r *http.Request) (*http.Response, error) { return fn(r) } + +var runIDFromURLRE = regexp.MustCompile(`repos/.*/actions/runs/(\d+)/deployment_protection_rule$`) + +// GetRunID is a Helper Function used to extract the workflow RunID from the *DeploymentProtectionRuleEvent.DeploymentCallBackURL. +func (e *DeploymentProtectionRuleEvent) GetRunID() (int64, error) { + match := runIDFromURLRE.FindStringSubmatch(*e.DeploymentCallbackURL) + if len(match) != 2 { + return -1, errors.New("no match") + } + runID, err := strconv.ParseInt(match[1], 10, 64) + if err != nil { + return -1, err + } + return runID, nil +} diff --git a/vendor/github.com/google/go-github/v68/github/gitignore.go b/vendor/github.com/google/go-github/v70/github/gitignore.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/gitignore.go rename to vendor/github.com/google/go-github/v70/github/gitignore.go diff --git a/vendor/github.com/google/go-github/v68/github/interactions.go b/vendor/github.com/google/go-github/v70/github/interactions.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/interactions.go rename to vendor/github.com/google/go-github/v70/github/interactions.go diff --git a/vendor/github.com/google/go-github/v68/github/interactions_orgs.go b/vendor/github.com/google/go-github/v70/github/interactions_orgs.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/interactions_orgs.go rename to vendor/github.com/google/go-github/v70/github/interactions_orgs.go diff --git a/vendor/github.com/google/go-github/v68/github/interactions_repos.go b/vendor/github.com/google/go-github/v70/github/interactions_repos.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/interactions_repos.go rename to vendor/github.com/google/go-github/v70/github/interactions_repos.go diff --git a/vendor/github.com/google/go-github/v68/github/issue_import.go b/vendor/github.com/google/go-github/v70/github/issue_import.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/issue_import.go rename to vendor/github.com/google/go-github/v70/github/issue_import.go diff --git a/vendor/github.com/google/go-github/v68/github/issues.go b/vendor/github.com/google/go-github/v70/github/issues.go similarity index 96% rename from vendor/github.com/google/go-github/v68/github/issues.go rename to vendor/github.com/google/go-github/v70/github/issues.go index a2652b3497..6d3a6b15c5 100644 --- a/vendor/github.com/google/go-github/v68/github/issues.go +++ b/vendor/github.com/google/go-github/v70/github/issues.go @@ -55,6 +55,7 @@ type Issue struct { Assignees []*User `json:"assignees,omitempty"` NodeID *string `json:"node_id,omitempty"` Draft *bool `json:"draft,omitempty"` + Type *IssueType `json:"type,omitempty"` // TextMatches is only populated from search results that request text matches // See: search.go and https://docs.github.com/rest/search/#text-match-metadata @@ -129,6 +130,18 @@ type PullRequestLinks struct { MergedAt *Timestamp `json:"merged_at,omitempty"` } +// IssueType represents the type of issue. +// For now it shows up when receiveing an Issue event. +type IssueType struct { + ID *int64 `json:"id,omitempty"` + NodeID *string `json:"node_id,omitempty"` + Name *string `json:"name,omitempty"` + Description *string `json:"description,omitempty"` + Color *string `json:"color,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` +} + // List the issues for the authenticated user. If all is true, list issues // across all the user's visible repositories including owned, member, and // organization repositories; if false, list only owned and member diff --git a/vendor/github.com/google/go-github/v68/github/issues_assignees.go b/vendor/github.com/google/go-github/v70/github/issues_assignees.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/issues_assignees.go rename to vendor/github.com/google/go-github/v70/github/issues_assignees.go diff --git a/vendor/github.com/google/go-github/v68/github/issues_comments.go b/vendor/github.com/google/go-github/v70/github/issues_comments.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/issues_comments.go rename to vendor/github.com/google/go-github/v70/github/issues_comments.go diff --git a/vendor/github.com/google/go-github/v68/github/issues_events.go b/vendor/github.com/google/go-github/v70/github/issues_events.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/issues_events.go rename to vendor/github.com/google/go-github/v70/github/issues_events.go diff --git a/vendor/github.com/google/go-github/v68/github/issues_labels.go b/vendor/github.com/google/go-github/v70/github/issues_labels.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/issues_labels.go rename to vendor/github.com/google/go-github/v70/github/issues_labels.go diff --git a/vendor/github.com/google/go-github/v68/github/issues_milestones.go b/vendor/github.com/google/go-github/v70/github/issues_milestones.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/issues_milestones.go rename to vendor/github.com/google/go-github/v70/github/issues_milestones.go diff --git a/vendor/github.com/google/go-github/v68/github/issues_timeline.go b/vendor/github.com/google/go-github/v70/github/issues_timeline.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/issues_timeline.go rename to vendor/github.com/google/go-github/v70/github/issues_timeline.go diff --git a/vendor/github.com/google/go-github/v68/github/licenses.go b/vendor/github.com/google/go-github/v70/github/licenses.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/licenses.go rename to vendor/github.com/google/go-github/v70/github/licenses.go diff --git a/vendor/github.com/google/go-github/v68/github/markdown.go b/vendor/github.com/google/go-github/v70/github/markdown.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/markdown.go rename to vendor/github.com/google/go-github/v70/github/markdown.go diff --git a/vendor/github.com/google/go-github/v68/github/messages.go b/vendor/github.com/google/go-github/v70/github/messages.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/messages.go rename to vendor/github.com/google/go-github/v70/github/messages.go diff --git a/vendor/github.com/google/go-github/v68/github/meta.go b/vendor/github.com/google/go-github/v70/github/meta.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/meta.go rename to vendor/github.com/google/go-github/v70/github/meta.go diff --git a/vendor/github.com/google/go-github/v68/github/migrations.go b/vendor/github.com/google/go-github/v70/github/migrations.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/migrations.go rename to vendor/github.com/google/go-github/v70/github/migrations.go diff --git a/vendor/github.com/google/go-github/v68/github/migrations_source_import.go b/vendor/github.com/google/go-github/v70/github/migrations_source_import.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/migrations_source_import.go rename to vendor/github.com/google/go-github/v70/github/migrations_source_import.go diff --git a/vendor/github.com/google/go-github/v68/github/migrations_user.go b/vendor/github.com/google/go-github/v70/github/migrations_user.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/migrations_user.go rename to vendor/github.com/google/go-github/v70/github/migrations_user.go diff --git a/vendor/github.com/google/go-github/v68/github/orgs.go b/vendor/github.com/google/go-github/v70/github/orgs.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/orgs.go rename to vendor/github.com/google/go-github/v70/github/orgs.go diff --git a/vendor/github.com/google/go-github/v68/github/orgs_actions_allowed.go b/vendor/github.com/google/go-github/v70/github/orgs_actions_allowed.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/orgs_actions_allowed.go rename to vendor/github.com/google/go-github/v70/github/orgs_actions_allowed.go diff --git a/vendor/github.com/google/go-github/v68/github/orgs_actions_permissions.go b/vendor/github.com/google/go-github/v70/github/orgs_actions_permissions.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/orgs_actions_permissions.go rename to vendor/github.com/google/go-github/v70/github/orgs_actions_permissions.go diff --git a/vendor/github.com/google/go-github/v68/github/orgs_attestations.go b/vendor/github.com/google/go-github/v70/github/orgs_attestations.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/orgs_attestations.go rename to vendor/github.com/google/go-github/v70/github/orgs_attestations.go diff --git a/vendor/github.com/google/go-github/v68/github/orgs_audit_log.go b/vendor/github.com/google/go-github/v70/github/orgs_audit_log.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/orgs_audit_log.go rename to vendor/github.com/google/go-github/v70/github/orgs_audit_log.go diff --git a/vendor/github.com/google/go-github/v68/github/orgs_codesecurity_configurations.go b/vendor/github.com/google/go-github/v70/github/orgs_codesecurity_configurations.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/orgs_codesecurity_configurations.go rename to vendor/github.com/google/go-github/v70/github/orgs_codesecurity_configurations.go diff --git a/vendor/github.com/google/go-github/v68/github/orgs_credential_authorizations.go b/vendor/github.com/google/go-github/v70/github/orgs_credential_authorizations.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/orgs_credential_authorizations.go rename to vendor/github.com/google/go-github/v70/github/orgs_credential_authorizations.go diff --git a/vendor/github.com/google/go-github/v68/github/orgs_custom_repository_roles.go b/vendor/github.com/google/go-github/v70/github/orgs_custom_repository_roles.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/orgs_custom_repository_roles.go rename to vendor/github.com/google/go-github/v70/github/orgs_custom_repository_roles.go diff --git a/vendor/github.com/google/go-github/v68/github/orgs_hooks.go b/vendor/github.com/google/go-github/v70/github/orgs_hooks.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/orgs_hooks.go rename to vendor/github.com/google/go-github/v70/github/orgs_hooks.go diff --git a/vendor/github.com/google/go-github/v68/github/orgs_hooks_configuration.go b/vendor/github.com/google/go-github/v70/github/orgs_hooks_configuration.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/orgs_hooks_configuration.go rename to vendor/github.com/google/go-github/v70/github/orgs_hooks_configuration.go diff --git a/vendor/github.com/google/go-github/v68/github/orgs_hooks_deliveries.go b/vendor/github.com/google/go-github/v70/github/orgs_hooks_deliveries.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/orgs_hooks_deliveries.go rename to vendor/github.com/google/go-github/v70/github/orgs_hooks_deliveries.go diff --git a/vendor/github.com/google/go-github/v68/github/orgs_members.go b/vendor/github.com/google/go-github/v70/github/orgs_members.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/orgs_members.go rename to vendor/github.com/google/go-github/v70/github/orgs_members.go diff --git a/vendor/github.com/google/go-github/v70/github/orgs_network_configurations.go b/vendor/github.com/google/go-github/v70/github/orgs_network_configurations.go new file mode 100644 index 0000000000..82a819c204 --- /dev/null +++ b/vendor/github.com/google/go-github/v70/github/orgs_network_configurations.go @@ -0,0 +1,236 @@ +// Copyright 2025 The go-github 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 github + +import ( + "context" + "errors" + "fmt" + "regexp" +) + +// ComputeService represents a hosted compute service the network configuration supports. +type ComputeService string + +const ( + ComputeServiceNone ComputeService = "none" + ComputeServiceActions ComputeService = "actions" + ComputeServiceCodespaces ComputeService = "codespaces" +) + +// NetworkConfigurations represents a hosted compute network configuration. This type is identical +// for enterprise and organization endpoints. +type NetworkConfigurations struct { + TotalCount *int64 `json:"total_count,omitempty"` + NetworkConfigurations []*NetworkConfiguration `json:"network_configurations,omitempty"` +} + +// NetworkConfiguration represents a hosted compute network configurations. This type is identical +// for enterprise and organization endpoints. +type NetworkConfiguration struct { + ID *string `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + ComputeService *ComputeService `json:"compute_service,omitempty"` + NetworkSettingsIDs []string `json:"network_settings_ids,omitempty"` + CreatedOn *Timestamp `json:"created_on"` +} + +// NetworkSettingsResource represents a hosted compute network settings resource. This type is identical +// for enterprise and organization endpoints. +type NetworkSettingsResource struct { + ID *string `json:"id,omitempty"` + NetworkConfigurationID *string `json:"network_configuration_id,omitempty"` + Name *string `json:"name,omitempty"` + SubnetID *string `json:"subnet_id,omitempty"` + Region *string `json:"region,omitempty"` +} + +func validateComputeService(compute *ComputeService) error { + if compute == nil { + return nil + } + if *compute != ComputeServiceNone && *compute != ComputeServiceActions { + return errors.New("compute service can only be one of: none, actions") + } + return nil +} + +var validNetworkNameRE = regexp.MustCompile(`^[a-zA-Z0-9._-]+$`) + +func validateNetworkName(name string) error { + if len(name) < 1 || len(name) > 100 { + return errors.New("must be between 1 and 100 characters") + } + if !validNetworkNameRE.MatchString(name) { + return errors.New("may only contain upper and lowercase letters a-z, numbers 0-9, '.', '-', and '_'") + } + return nil +} + +func validateNetworkSettingsID(settingsID []string) error { + if len(settingsID) != 1 { + return errors.New("exactly one network settings id must be specified") + } + return nil +} + +func validateNetworkConfigurationRequest(req NetworkConfigurationRequest) error { + networkName := req.GetName() + if err := validateNetworkName(networkName); err != nil { + return err + } + + computeService := req.GetComputeService() + if err := validateComputeService(computeService); err != nil { + return err + } + + networkIDs := req.NetworkSettingsIDs + if err := validateNetworkSettingsID(networkIDs); err != nil { + return err + } + return nil +} + +// NetworkConfigurationRequest represents a request to create or update a network configuration for an organization. +type NetworkConfigurationRequest struct { + Name *string `json:"name,omitempty"` + ComputeService *ComputeService `json:"compute_service,omitempty"` + NetworkSettingsIDs []string `json:"network_settings_ids,omitempty"` +} + +// ListNetworkConfigurations lists all hosted compute network configurations configured in an organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/network-configurations#list-hosted-compute-network-configurations-for-an-organization +// +//meta:operation GET /orgs/{org}/settings/network-configurations +func (s *OrganizationsService) ListNetworkConfigurations(ctx context.Context, org string, opts *ListOptions) (*NetworkConfigurations, *Response, error) { + u := fmt.Sprintf("orgs/%v/settings/network-configurations", org) + u, err := addOptions(u, opts) + if err != nil { + return nil, nil, err + } + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + configurations := &NetworkConfigurations{} + resp, err := s.client.Do(ctx, req, configurations) + if err != nil { + return nil, resp, err + } + return configurations, resp, nil +} + +// CreateNetworkConfiguration creates a hosted compute network configuration for an organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/network-configurations#create-a-hosted-compute-network-configuration-for-an-organization +// +//meta:operation POST /orgs/{org}/settings/network-configurations +func (s *OrganizationsService) CreateNetworkConfiguration(ctx context.Context, org string, createReq NetworkConfigurationRequest) (*NetworkConfiguration, *Response, error) { + if err := validateNetworkConfigurationRequest(createReq); err != nil { + return nil, nil, fmt.Errorf("validation failed: %w", err) + } + + u := fmt.Sprintf("orgs/%v/settings/network-configurations", org) + req, err := s.client.NewRequest("POST", u, createReq) + if err != nil { + return nil, nil, err + } + + configuration := &NetworkConfiguration{} + resp, err := s.client.Do(ctx, req, configuration) + if err != nil { + return nil, resp, err + } + return configuration, resp, nil +} + +// GetNetworkConfiguration gets a hosted compute network configuration configured in an organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/network-configurations#get-a-hosted-compute-network-configuration-for-an-organization +// +//meta:operation GET /orgs/{org}/settings/network-configurations/{network_configuration_id} +func (s *OrganizationsService) GetNetworkConfiguration(ctx context.Context, org, networkID string) (*NetworkConfiguration, *Response, error) { + u := fmt.Sprintf("orgs/%v/settings/network-configurations/%v", org, networkID) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + configuration := &NetworkConfiguration{} + resp, err := s.client.Do(ctx, req, configuration) + if err != nil { + return nil, resp, err + } + return configuration, resp, nil +} + +// UpdateNetworkConfiguration updates a hosted compute network configuration for an organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/network-configurations#update-a-hosted-compute-network-configuration-for-an-organization +// +//meta:operation PATCH /orgs/{org}/settings/network-configurations/{network_configuration_id} +func (s *OrganizationsService) UpdateNetworkConfiguration(ctx context.Context, org, networkID string, updateReq NetworkConfigurationRequest) (*NetworkConfiguration, *Response, error) { + if err := validateNetworkConfigurationRequest(updateReq); err != nil { + return nil, nil, fmt.Errorf("validation failed: %w", err) + } + + u := fmt.Sprintf("orgs/%v/settings/network-configurations/%v", org, networkID) + req, err := s.client.NewRequest("PATCH", u, updateReq) + if err != nil { + return nil, nil, err + } + + configuration := &NetworkConfiguration{} + resp, err := s.client.Do(ctx, req, configuration) + if err != nil { + return nil, resp, err + } + return configuration, resp, nil +} + +// DeleteNetworkConfigurations deletes a hosted compute network configuration from an organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/network-configurations#delete-a-hosted-compute-network-configuration-from-an-organization +// +//meta:operation DELETE /orgs/{org}/settings/network-configurations/{network_configuration_id} +func (s *OrganizationsService) DeleteNetworkConfigurations(ctx context.Context, org, networkID string) (*Response, error) { + u := fmt.Sprintf("orgs/%v/settings/network-configurations/%v", org, networkID) + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + configuration := &NetworkConfiguration{} + resp, err := s.client.Do(ctx, req, configuration) + if err != nil { + return resp, err + } + return resp, nil +} + +// GetNetworkConfigurationResource gets a hosted compute network settings resource configured for an organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/network-configurations#get-a-hosted-compute-network-settings-resource-for-an-organization +// +//meta:operation GET /orgs/{org}/settings/network-settings/{network_settings_id} +func (s *OrganizationsService) GetNetworkConfigurationResource(ctx context.Context, org, networkID string) (*NetworkSettingsResource, *Response, error) { + u := fmt.Sprintf("orgs/%v/settings/network-settings/%v", org, networkID) + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + resource := &NetworkSettingsResource{} + resp, err := s.client.Do(ctx, req, resource) + if err != nil { + return nil, resp, err + } + return resource, resp, nil +} diff --git a/vendor/github.com/google/go-github/v68/github/orgs_organization_roles.go b/vendor/github.com/google/go-github/v70/github/orgs_organization_roles.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/orgs_organization_roles.go rename to vendor/github.com/google/go-github/v70/github/orgs_organization_roles.go diff --git a/vendor/github.com/google/go-github/v68/github/orgs_outside_collaborators.go b/vendor/github.com/google/go-github/v70/github/orgs_outside_collaborators.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/orgs_outside_collaborators.go rename to vendor/github.com/google/go-github/v70/github/orgs_outside_collaborators.go diff --git a/vendor/github.com/google/go-github/v68/github/orgs_packages.go b/vendor/github.com/google/go-github/v70/github/orgs_packages.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/orgs_packages.go rename to vendor/github.com/google/go-github/v70/github/orgs_packages.go diff --git a/vendor/github.com/google/go-github/v68/github/orgs_personal_access_tokens.go b/vendor/github.com/google/go-github/v70/github/orgs_personal_access_tokens.go similarity index 98% rename from vendor/github.com/google/go-github/v68/github/orgs_personal_access_tokens.go rename to vendor/github.com/google/go-github/v70/github/orgs_personal_access_tokens.go index af083744e8..276fbbb494 100644 --- a/vendor/github.com/google/go-github/v68/github/orgs_personal_access_tokens.go +++ b/vendor/github.com/google/go-github/v70/github/orgs_personal_access_tokens.go @@ -44,6 +44,12 @@ type PersonalAccessToken struct { // Date and time when the associated fine-grained personal access token expires. TokenExpiresAt *Timestamp `json:"token_expires_at"` + // TokenID + TokenID *int64 `json:"token_id"` + + // TokenName + TokenName *string `json:"token_name"` + // Date and time when the associated fine-grained personal access token was last used for authentication. TokenLastUsedAt *Timestamp `json:"token_last_used_at"` } diff --git a/vendor/github.com/google/go-github/v68/github/orgs_properties.go b/vendor/github.com/google/go-github/v70/github/orgs_properties.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/orgs_properties.go rename to vendor/github.com/google/go-github/v70/github/orgs_properties.go diff --git a/vendor/github.com/google/go-github/v70/github/orgs_rules.go b/vendor/github.com/google/go-github/v70/github/orgs_rules.go new file mode 100644 index 0000000000..8cb2e5d1f9 --- /dev/null +++ b/vendor/github.com/google/go-github/v70/github/orgs_rules.go @@ -0,0 +1,140 @@ +// Copyright 2023 The go-github 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 github + +import ( + "context" + "fmt" +) + +// GetAllRepositoryRulesets gets all the repository rulesets for the specified organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/rules#get-all-organization-repository-rulesets +// +//meta:operation GET /orgs/{org}/rulesets +func (s *OrganizationsService) GetAllRepositoryRulesets(ctx context.Context, org string) ([]*RepositoryRuleset, *Response, error) { + u := fmt.Sprintf("orgs/%v/rulesets", org) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var rulesets []*RepositoryRuleset + resp, err := s.client.Do(ctx, req, &rulesets) + if err != nil { + return nil, resp, err + } + + return rulesets, resp, nil +} + +// CreateRepositoryRuleset creates a repository ruleset for the specified organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/rules#create-an-organization-repository-ruleset +// +//meta:operation POST /orgs/{org}/rulesets +func (s *OrganizationsService) CreateRepositoryRuleset(ctx context.Context, org string, ruleset RepositoryRuleset) (*RepositoryRuleset, *Response, error) { + u := fmt.Sprintf("orgs/%v/rulesets", org) + + req, err := s.client.NewRequest("POST", u, ruleset) + if err != nil { + return nil, nil, err + } + + var rs *RepositoryRuleset + resp, err := s.client.Do(ctx, req, &rs) + if err != nil { + return nil, resp, err + } + + return rs, resp, nil +} + +// GetRepositoryRuleset gets a repository ruleset for the specified organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/rules#get-an-organization-repository-ruleset +// +//meta:operation GET /orgs/{org}/rulesets/{ruleset_id} +func (s *OrganizationsService) GetRepositoryRuleset(ctx context.Context, org string, rulesetID int64) (*RepositoryRuleset, *Response, error) { + u := fmt.Sprintf("orgs/%v/rulesets/%v", org, rulesetID) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var ruleset *RepositoryRuleset + resp, err := s.client.Do(ctx, req, &ruleset) + if err != nil { + return nil, resp, err + } + + return ruleset, resp, nil +} + +// UpdateRepositoryRuleset updates a repository ruleset for the specified organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/rules#update-an-organization-repository-ruleset +// +//meta:operation PUT /orgs/{org}/rulesets/{ruleset_id} +func (s *OrganizationsService) UpdateRepositoryRuleset(ctx context.Context, org string, rulesetID int64, ruleset RepositoryRuleset) (*RepositoryRuleset, *Response, error) { + u := fmt.Sprintf("orgs/%v/rulesets/%v", org, rulesetID) + + req, err := s.client.NewRequest("PUT", u, ruleset) + if err != nil { + return nil, nil, err + } + + var rs *RepositoryRuleset + resp, err := s.client.Do(ctx, req, &rs) + if err != nil { + return nil, resp, err + } + + return rs, resp, nil +} + +// UpdateRepositoryRulesetClearBypassActor clears the bypass actors for a repository ruleset for the specified organization. +// +// This function is necessary as the UpdateRepositoryRuleset function does not marshal ByPassActor if passed as an empty array. +// +// GitHub API docs: https://docs.github.com/rest/orgs/rules#update-an-organization-repository-ruleset +// +//meta:operation PUT /orgs/{org}/rulesets/{ruleset_id} +func (s *OrganizationsService) UpdateRepositoryRulesetClearBypassActor(ctx context.Context, org string, rulesetID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/rulesets/%v", org, rulesetID) + + rsClearBypassActor := rulesetClearBypassActors{} + + req, err := s.client.NewRequest("PUT", u, rsClearBypassActor) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(ctx, req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// DeleteRepositoryRuleset deletes a repository ruleset from the specified organization. +// +// GitHub API docs: https://docs.github.com/rest/orgs/rules#delete-an-organization-repository-ruleset +// +//meta:operation DELETE /orgs/{org}/rulesets/{ruleset_id} +func (s *OrganizationsService) DeleteRepositoryRuleset(ctx context.Context, org string, rulesetID int64) (*Response, error) { + u := fmt.Sprintf("orgs/%v/rulesets/%v", org, rulesetID) + + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/v68/github/orgs_security_managers.go b/vendor/github.com/google/go-github/v70/github/orgs_security_managers.go similarity index 88% rename from vendor/github.com/google/go-github/v68/github/orgs_security_managers.go rename to vendor/github.com/google/go-github/v70/github/orgs_security_managers.go index 0803772732..b8562a2fd7 100644 --- a/vendor/github.com/google/go-github/v68/github/orgs_security_managers.go +++ b/vendor/github.com/google/go-github/v70/github/orgs_security_managers.go @@ -12,6 +12,8 @@ import ( // ListSecurityManagerTeams lists all security manager teams for an organization. // +// Deprecated: Please use `client.Organizations.ListTeamsAssignedToOrgRole` instead. +// // GitHub API docs: https://docs.github.com/rest/orgs/security-managers#list-security-manager-teams // //meta:operation GET /orgs/{org}/security-managers @@ -34,6 +36,8 @@ func (s *OrganizationsService) ListSecurityManagerTeams(ctx context.Context, org // AddSecurityManagerTeam adds a team to the list of security managers for an organization. // +// Deprecated: Please use `client.Organizations.AssignOrgRoleToTeam` instead. +// // GitHub API docs: https://docs.github.com/rest/orgs/security-managers#add-a-security-manager-team // //meta:operation PUT /orgs/{org}/security-managers/teams/{team_slug} @@ -49,6 +53,8 @@ func (s *OrganizationsService) AddSecurityManagerTeam(ctx context.Context, org, // RemoveSecurityManagerTeam removes a team from the list of security managers for an organization. // +// Deprecated: Please use `client.Organizations.RemoveOrgRoleFromTeam` instead. +// // GitHub API docs: https://docs.github.com/rest/orgs/security-managers#remove-a-security-manager-team // //meta:operation DELETE /orgs/{org}/security-managers/teams/{team_slug} diff --git a/vendor/github.com/google/go-github/v68/github/orgs_users_blocking.go b/vendor/github.com/google/go-github/v70/github/orgs_users_blocking.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/orgs_users_blocking.go rename to vendor/github.com/google/go-github/v70/github/orgs_users_blocking.go diff --git a/vendor/github.com/google/go-github/v70/github/packages.go b/vendor/github.com/google/go-github/v70/github/packages.go new file mode 100644 index 0000000000..e7c327963c --- /dev/null +++ b/vendor/github.com/google/go-github/v70/github/packages.go @@ -0,0 +1,318 @@ +// Copyright 2020 The go-github 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 github + +import ( + "encoding/json" +) + +// Package represents a GitHub package. +type Package struct { + ID *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + PackageType *string `json:"package_type,omitempty"` // One of "npm", "maven", "rubygems", "docker", "nuget", "container". For webhook events "container" is "CONTAINER" + HTMLURL *string `json:"html_url,omitempty"` + Visibility *string `json:"visibility,omitempty"` + Owner *User `json:"owner,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + + // The following are only populated for webhook events + Namespace *string `json:"namespace,omitempty"` + Description *string `json:"description,omitempty"` + Ecosystem *string `json:"ecosystem,omitempty"` + PackageVersion *PackageVersion `json:"package_version,omitempty"` + Registry *PackageRegistry `json:"registry,omitempty"` + + // The following are NOT populated for webhook events + URL *string `json:"url,omitempty"` + VersionCount *int64 `json:"version_count,omitempty"` +} + +func (p Package) String() string { + return Stringify(p) +} + +// PackageVersion represents a GitHub package version. +type PackageVersion struct { + ID *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + URL *string `json:"url,omitempty"` + PackageHTMLURL *string `json:"package_html_url,omitempty"` + License *string `json:"license,omitempty"` + Description *string `json:"description,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + Metadata json.RawMessage `json:"metadata,omitempty"` // For webhook events this will be []interface, else it will be of type PackageMetadata + + // The following are only populated for webhook events + Version *string `json:"version,omitempty"` + Summary *string `json:"summary,omitempty"` + Body json.RawMessage `json:"body,omitempty"` // Can either be a string or of type PackageVersionBody + BodyHTML *string `json:"body_html,omitempty"` + Release *PackageRelease `json:"release,omitempty"` + Manifest *string `json:"manifest,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + TagName *string `json:"tag_name,omitempty"` + TargetCommitish *string `json:"target_commitish,omitempty"` + TargetOID *string `json:"target_oid,omitempty"` + Draft *bool `json:"draft,omitempty"` + Prerelease *bool `json:"prerelease,omitempty"` + ContainerMetadata *PackageEventContainerMetadata `json:"container_metadata,omitempty"` + DockerMetadata []interface{} `json:"docker_metadata,omitempty"` + NPMMetadata *PackageNPMMetadata `json:"npm_metadata,omitempty"` + NugetMetadata []*PackageNugetMetadata `json:"nuget_metadata,omitempty"` + RubyMetadata map[string]any `json:"ruby_metadata,omitempty"` + PackageFiles []*PackageFile `json:"package_files,omitempty"` + PackageURL *string `json:"package_url,omitempty"` + Author *User `json:"author,omitempty"` + SourceURL *string `json:"source_url,omitempty"` + InstallationCommand *string `json:"installation_command,omitempty"` + + // The following are NOT populated for webhook events + DeletedAt *Timestamp `json:"deleted_at,omitempty"` +} + +// GetBody returns the body field as a string if it's valid. +func (pv *PackageVersion) GetBody() (body string, ok bool) { + if pv == nil || pv.Body == nil { + return "", false + } + + if err := json.Unmarshal(pv.Body, &body); err != nil { + return "", false + } + + return body, true +} + +// GetBodyAsPackageVersionBody returns the body field as a PackageVersionBody if it's valid. +func (pv *PackageVersion) GetBodyAsPackageVersionBody() (body *PackageVersionBody, ok bool) { + if pv == nil || pv.Body == nil { + return nil, false + } + + if err := json.Unmarshal(pv.Body, &body); err != nil { + return nil, false + } + + return body, true +} + +// GetMetadata returns the metadata field as PackageMetadata if it's valid. +func (pv *PackageVersion) GetMetadata() (metadata *PackageMetadata, ok bool) { + if pv == nil || pv.Metadata == nil { + return nil, false + } + + if err := json.Unmarshal(pv.Metadata, &metadata); err != nil { + return nil, false + } + + return metadata, true +} + +// GetRawMetadata returns the metadata field as a json.RawMessage. +func (pv *PackageVersion) GetRawMetadata() json.RawMessage { + if pv == nil || pv.Metadata == nil { + return json.RawMessage{} + } + + return pv.Metadata +} + +func (pv PackageVersion) String() string { + return Stringify(pv) +} + +// PackageRelease represents a GitHub package version release. +type PackageRelease struct { + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + ID *int64 `json:"id,omitempty"` + TagName *string `json:"tag_name,omitempty"` + TargetCommitish *string `json:"target_commitish,omitempty"` + Name *string `json:"name,omitempty"` + Draft *bool `json:"draft,omitempty"` + Author *User `json:"author,omitempty"` + Prerelease *bool `json:"prerelease,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + PublishedAt *Timestamp `json:"published_at,omitempty"` +} + +func (r PackageRelease) String() string { + return Stringify(r) +} + +// PackageFile represents a GitHub package version release file. +type PackageFile struct { + DownloadURL *string `json:"download_url,omitempty"` + ID *int64 `json:"id,omitempty"` + Name *string `json:"name,omitempty"` + SHA256 *string `json:"sha256,omitempty"` + SHA1 *string `json:"sha1,omitempty"` + MD5 *string `json:"md5,omitempty"` + ContentType *string `json:"content_type,omitempty"` + State *string `json:"state,omitempty"` + Author *User `json:"author,omitempty"` + Size *int64 `json:"size,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` +} + +func (pf PackageFile) String() string { + return Stringify(pf) +} + +// PackageRegistry represents a GitHub package registry. +type PackageRegistry struct { + AboutURL *string `json:"about_url,omitempty"` + Name *string `json:"name,omitempty"` + Type *string `json:"type,omitempty"` + URL *string `json:"url,omitempty"` + Vendor *string `json:"vendor,omitempty"` +} + +func (r PackageRegistry) String() string { + return Stringify(r) +} + +// PackageListOptions represents the optional list options for a package. +type PackageListOptions struct { + // Visibility of packages "public", "internal" or "private". + Visibility *string `url:"visibility,omitempty"` + + // PackageType represents the type of package. + // It can be one of "npm", "maven", "rubygems", "nuget", "docker", or "container". + PackageType *string `url:"package_type,omitempty"` + + // State of package either "active" or "deleted". + State *string `url:"state,omitempty"` + + ListOptions +} + +// PackageMetadata represents metadata from a package. +type PackageMetadata struct { + PackageType *string `json:"package_type,omitempty"` + Container *PackageContainerMetadata `json:"container,omitempty"` +} + +func (r PackageMetadata) String() string { + return Stringify(r) +} + +// PackageContainerMetadata represents container metadata for docker container packages. +type PackageContainerMetadata struct { + Tags []string `json:"tags,omitempty"` +} + +func (r PackageContainerMetadata) String() string { + return Stringify(r) +} + +// PackageVersionBody represents the body field of a package version. +type PackageVersionBody struct { + Repo *Repository `json:"repository,omitempty"` + Info *PackageVersionBodyInfo `json:"info,omitempty"` +} + +func (b PackageVersionBody) String() string { + return Stringify(b) +} + +// PackageVersionBodyInfo represents the info field of a PackageVersionBody. +type PackageVersionBodyInfo struct { + Type *string `json:"type,omitempty"` + OID *string `json:"oid,omitempty"` + Mode *int64 `json:"mode,omitempty"` + Name *string `json:"name,omitempty"` + Path *string `json:"path,omitempty"` + Size *int64 `json:"size,omitempty"` + Collection *bool `json:"collection,omitempty"` +} + +func (bi PackageVersionBodyInfo) String() string { + return Stringify(bi) +} + +// PackageEventContainerMetadata represents metadata for container packages as part of a webhook event. +// See also PackageContainerMetadata. +type PackageEventContainerMetadata struct { + Labels map[string]any `json:"labels,omitempty"` + Manifest map[string]any `json:"manifest,omitempty"` + Tag *PackageEventContainerMetadataTag `json:"tag,omitempty"` +} + +func (m PackageEventContainerMetadata) String() string { + return Stringify(m) +} + +// PackageEventContainerMetadataTag represents a tag of a GitHub container package. +type PackageEventContainerMetadataTag struct { + Name *string `json:"name,omitempty"` + Digest *string `json:"digest,omitempty"` +} + +func (mt PackageEventContainerMetadataTag) String() string { + return Stringify(mt) +} + +// PackageNugetMetadata represents nuget metadata for a GitHub package. +type PackageNugetMetadata struct { + ID json.RawMessage `json:"id,omitempty"` // Can either be a int64 or string + Name *string `json:"name,omitempty"` + Value json.RawMessage `json:"value,omitempty"` // Can either be a bool, string, integer or object +} + +func (nm PackageNugetMetadata) String() string { + return Stringify(nm) +} + +// PackageNPMMetadata represents NPM metadata for a GitHub package. +type PackageNPMMetadata struct { + Name *string `json:"name,omitempty"` + Version *string `json:"version,omitempty"` + NPMUser *string `json:"npm_user,omitempty"` + Author map[string]string `json:"author,omitempty"` + Bugs map[string]string `json:"bugs,omitempty"` + Dependencies map[string]string `json:"dependencies,omitempty"` + DevDependencies map[string]string `json:"dev_dependencies,omitempty"` + PeerDependencies map[string]string `json:"peer_dependencies,omitempty"` + OptionalDependencies map[string]string `json:"optional_dependencies,omitempty"` + Description *string `json:"description,omitempty"` + Dist map[string]string `json:"dist,omitempty"` + GitHead *string `json:"git_head,omitempty"` + Homepage *string `json:"homepage,omitempty"` + License *string `json:"license,omitempty"` + Main *string `json:"main,omitempty"` + Repository map[string]string `json:"repository,omitempty"` + Scripts map[string]any `json:"scripts,omitempty"` + ID *string `json:"id,omitempty"` + NodeVersion *string `json:"node_version,omitempty"` + NPMVersion *string `json:"npm_version,omitempty"` + HasShrinkwrap *bool `json:"has_shrinkwrap,omitempty"` + Maintainers []interface{} `json:"maintainers,omitempty"` + Contributors []interface{} `json:"contributors,omitempty"` + Engines map[string]string `json:"engines,omitempty"` + Keywords []string `json:"keywords,omitempty"` + Files []string `json:"files,omitempty"` + Bin map[string]any `json:"bin,omitempty"` + Man map[string]any `json:"man,omitempty"` + Directories map[string]string `json:"directories,omitempty"` + OS []string `json:"os,omitempty"` + CPU []string `json:"cpu,omitempty"` + Readme *string `json:"readme,omitempty"` + InstallationCommand *string `json:"installation_command,omitempty"` + ReleaseID *int64 `json:"release_id,omitempty"` + CommitOID *string `json:"commit_oid,omitempty"` + PublishedViaActions *bool `json:"published_via_actions,omitempty"` + DeletedByID *int64 `json:"deleted_by_id,omitempty"` +} + +func (nm PackageNPMMetadata) String() string { + return Stringify(nm) +} diff --git a/vendor/github.com/google/go-github/v68/github/pulls.go b/vendor/github.com/google/go-github/v70/github/pulls.go similarity index 83% rename from vendor/github.com/google/go-github/v68/github/pulls.go rename to vendor/github.com/google/go-github/v70/github/pulls.go index 35ceda4467..f3c6e929c1 100644 --- a/vendor/github.com/google/go-github/v68/github/pulls.go +++ b/vendor/github.com/google/go-github/v70/github/pulls.go @@ -28,49 +28,51 @@ type PullRequestAutoMerge struct { // PullRequest represents a GitHub pull request on a repository. type PullRequest struct { - ID *int64 `json:"id,omitempty"` - Number *int `json:"number,omitempty"` - State *string `json:"state,omitempty"` - Locked *bool `json:"locked,omitempty"` - Title *string `json:"title,omitempty"` - Body *string `json:"body,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` - ClosedAt *Timestamp `json:"closed_at,omitempty"` - MergedAt *Timestamp `json:"merged_at,omitempty"` - Labels []*Label `json:"labels,omitempty"` - User *User `json:"user,omitempty"` - Draft *bool `json:"draft,omitempty"` - Merged *bool `json:"merged,omitempty"` - Mergeable *bool `json:"mergeable,omitempty"` - MergeableState *string `json:"mergeable_state,omitempty"` - MergedBy *User `json:"merged_by,omitempty"` - MergeCommitSHA *string `json:"merge_commit_sha,omitempty"` - Rebaseable *bool `json:"rebaseable,omitempty"` - Comments *int `json:"comments,omitempty"` - Commits *int `json:"commits,omitempty"` - Additions *int `json:"additions,omitempty"` - Deletions *int `json:"deletions,omitempty"` - ChangedFiles *int `json:"changed_files,omitempty"` - URL *string `json:"url,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - IssueURL *string `json:"issue_url,omitempty"` - StatusesURL *string `json:"statuses_url,omitempty"` - DiffURL *string `json:"diff_url,omitempty"` - PatchURL *string `json:"patch_url,omitempty"` - CommitsURL *string `json:"commits_url,omitempty"` - CommentsURL *string `json:"comments_url,omitempty"` - ReviewCommentsURL *string `json:"review_comments_url,omitempty"` - ReviewCommentURL *string `json:"review_comment_url,omitempty"` - ReviewComments *int `json:"review_comments,omitempty"` - Assignee *User `json:"assignee,omitempty"` - Assignees []*User `json:"assignees,omitempty"` - Milestone *Milestone `json:"milestone,omitempty"` - MaintainerCanModify *bool `json:"maintainer_can_modify,omitempty"` - AuthorAssociation *string `json:"author_association,omitempty"` - NodeID *string `json:"node_id,omitempty"` - RequestedReviewers []*User `json:"requested_reviewers,omitempty"` - AutoMerge *PullRequestAutoMerge `json:"auto_merge,omitempty"` + ID *int64 `json:"id,omitempty"` + Number *int `json:"number,omitempty"` + State *string `json:"state,omitempty"` + Locked *bool `json:"locked,omitempty"` + Title *string `json:"title,omitempty"` + Body *string `json:"body,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + ClosedAt *Timestamp `json:"closed_at,omitempty"` + MergedAt *Timestamp `json:"merged_at,omitempty"` + Labels []*Label `json:"labels,omitempty"` + User *User `json:"user,omitempty"` + Draft *bool `json:"draft,omitempty"` + URL *string `json:"url,omitempty"` + HTMLURL *string `json:"html_url,omitempty"` + IssueURL *string `json:"issue_url,omitempty"` + StatusesURL *string `json:"statuses_url,omitempty"` + DiffURL *string `json:"diff_url,omitempty"` + PatchURL *string `json:"patch_url,omitempty"` + CommitsURL *string `json:"commits_url,omitempty"` + CommentsURL *string `json:"comments_url,omitempty"` + ReviewCommentsURL *string `json:"review_comments_url,omitempty"` + ReviewCommentURL *string `json:"review_comment_url,omitempty"` + Assignee *User `json:"assignee,omitempty"` + Assignees []*User `json:"assignees,omitempty"` + Milestone *Milestone `json:"milestone,omitempty"` + AuthorAssociation *string `json:"author_association,omitempty"` + NodeID *string `json:"node_id,omitempty"` + RequestedReviewers []*User `json:"requested_reviewers,omitempty"` + AutoMerge *PullRequestAutoMerge `json:"auto_merge,omitempty"` + + // These fields are not populated by the List operation. + Merged *bool `json:"merged,omitempty"` + Mergeable *bool `json:"mergeable,omitempty"` + MergeableState *string `json:"mergeable_state,omitempty"` + Rebaseable *bool `json:"rebaseable,omitempty"` + MergedBy *User `json:"merged_by,omitempty"` + MergeCommitSHA *string `json:"merge_commit_sha,omitempty"` + Comments *int `json:"comments,omitempty"` + Commits *int `json:"commits,omitempty"` + Additions *int `json:"additions,omitempty"` + Deletions *int `json:"deletions,omitempty"` + ChangedFiles *int `json:"changed_files,omitempty"` + MaintainerCanModify *bool `json:"maintainer_can_modify,omitempty"` + ReviewComments *int `json:"review_comments,omitempty"` // RequestedTeams is populated as part of the PullRequestEvent. // See, https://docs.github.com/developers/webhooks-and-events/github-event-types#pullrequestevent for an example. @@ -167,10 +169,12 @@ func (s *PullRequestsService) List(ctx context.Context, owner string, repo strin return pulls, resp, nil } -// ListPullRequestsWithCommit returns pull requests associated with a commit SHA. +// ListPullRequestsWithCommit returns pull requests associated with a commit SHA +// or branch name. // -// The results may include open and closed pull requests. -// By default, the PullRequestListOptions State filters for "open". +// The results may include open and closed pull requests. If the commit SHA is +// not present in the repository's default branch, the result will only include +// open pull requests. // // GitHub API docs: https://docs.github.com/rest/commits/commits#list-pull-requests-associated-with-a-commit // diff --git a/vendor/github.com/google/go-github/v68/github/pulls_comments.go b/vendor/github.com/google/go-github/v70/github/pulls_comments.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/pulls_comments.go rename to vendor/github.com/google/go-github/v70/github/pulls_comments.go diff --git a/vendor/github.com/google/go-github/v68/github/pulls_reviewers.go b/vendor/github.com/google/go-github/v70/github/pulls_reviewers.go similarity index 95% rename from vendor/github.com/google/go-github/v68/github/pulls_reviewers.go rename to vendor/github.com/google/go-github/v70/github/pulls_reviewers.go index 9dd60ae688..526047937e 100644 --- a/vendor/github.com/google/go-github/v68/github/pulls_reviewers.go +++ b/vendor/github.com/google/go-github/v70/github/pulls_reviewers.go @@ -85,11 +85,8 @@ func (s *PullRequestsService) ListReviewers(ctx context.Context, owner, repo str func (s *PullRequestsService) RemoveReviewers(ctx context.Context, owner, repo string, number int, reviewers ReviewersRequest) (*Response, error) { // reviewers.Reviewers may be empty if the caller wants to remove teams, but not users. Unlike AddReviewers, // "reviewers" is a required param here. Reference: https://github.com/google/go-github/issues/3336 - removeRequest := removeReviewersRequest{ - NodeID: reviewers.NodeID, - Reviewers: reviewers.Reviewers, - TeamReviewers: reviewers.TeamReviewers, - } + // The type `removeReviewersRequest` is required because the struct tags are different from `ReviewersRequest`. + removeRequest := removeReviewersRequest(reviewers) if removeRequest.Reviewers == nil { // GitHub accepts the empty list, but rejects null. Removing `omitempty` is not enough - we also have to promote nil to []. diff --git a/vendor/github.com/google/go-github/v68/github/pulls_reviews.go b/vendor/github.com/google/go-github/v70/github/pulls_reviews.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/pulls_reviews.go rename to vendor/github.com/google/go-github/v70/github/pulls_reviews.go diff --git a/vendor/github.com/google/go-github/v68/github/pulls_threads.go b/vendor/github.com/google/go-github/v70/github/pulls_threads.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/pulls_threads.go rename to vendor/github.com/google/go-github/v70/github/pulls_threads.go diff --git a/vendor/github.com/google/go-github/v68/github/rate_limit.go b/vendor/github.com/google/go-github/v70/github/rate_limit.go similarity index 85% rename from vendor/github.com/google/go-github/v68/github/rate_limit.go rename to vendor/github.com/google/go-github/v70/github/rate_limit.go index 5b01b573d8..6236eba8fb 100644 --- a/vendor/github.com/google/go-github/v68/github/rate_limit.go +++ b/vendor/github.com/google/go-github/v70/github/rate_limit.go @@ -12,14 +12,22 @@ type RateLimitService service // Rate represents the rate limit for the current client. type Rate struct { - // The number of requests per hour the client is currently limited to. + // The maximum number of requests that you can make per hour. Limit int `json:"limit"` - // The number of remaining requests the client can make this hour. + // The number of requests remaining in the current rate limit window. Remaining int `json:"remaining"` - // The time at which the current rate limit will reset. + // The number of requests you have made in the current rate limit window. + Used int `json:"used"` + + // The time at which the current rate limit window resets, in UTC epoch seconds. Reset Timestamp `json:"reset"` + + // The rate limit resource that the request counted against. + // For more information about the different resources, see REST API endpoints for rate limits. + // GitHub API docs: https://docs.github.com/en/rest/rate-limit/rate-limit#get-rate-limit-status-for-the-authenticated-user + Resource string `json:"resource,omitempty"` } func (r Rate) String() string { @@ -77,7 +85,7 @@ func (s *RateLimitService) Get(ctx context.Context) (*RateLimits, *Response, err }) // This resource is not subject to rate limits. - ctx = context.WithValue(ctx, bypassRateLimitCheck, true) + ctx = context.WithValue(ctx, BypassRateLimitCheck, true) resp, err := s.client.Do(ctx, req, response) if err != nil { return nil, resp, err diff --git a/vendor/github.com/google/go-github/v68/github/reactions.go b/vendor/github.com/google/go-github/v70/github/reactions.go similarity index 99% rename from vendor/github.com/google/go-github/v68/github/reactions.go rename to vendor/github.com/google/go-github/v70/github/reactions.go index 9f9f72faee..e982322037 100644 --- a/vendor/github.com/google/go-github/v68/github/reactions.go +++ b/vendor/github.com/google/go-github/v70/github/reactions.go @@ -26,7 +26,8 @@ type Reaction struct { // Content is the type of reaction. // Possible values are: // "+1", "-1", "laugh", "confused", "heart", "hooray", "rocket", or "eyes". - Content *string `json:"content,omitempty"` + Content *string `json:"content,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` } // Reactions represents a summary of GitHub reactions. diff --git a/vendor/github.com/google/go-github/v68/github/repos.go b/vendor/github.com/google/go-github/v70/github/repos.go similarity index 99% rename from vendor/github.com/google/go-github/v68/github/repos.go rename to vendor/github.com/google/go-github/v70/github/repos.go index 9faed401f8..c4ef5d5987 100644 --- a/vendor/github.com/google/go-github/v68/github/repos.go +++ b/vendor/github.com/google/go-github/v70/github/repos.go @@ -833,7 +833,7 @@ func (s *RepositoriesService) DisableVulnerabilityAlerts(ctx context.Context, ow // GetAutomatedSecurityFixes checks if the automated security fixes for a repository are enabled. // -// GitHub API docs: https://docs.github.com/rest/repos/repos#check-if-automated-security-fixes-are-enabled-for-a-repository +// GitHub API docs: https://docs.github.com/rest/repos/repos#check-if-dependabot-security-updates-are-enabled-for-a-repository // //meta:operation GET /repos/{owner}/{repo}/automated-security-fixes func (s *RepositoriesService) GetAutomatedSecurityFixes(ctx context.Context, owner, repository string) (*AutomatedSecurityFixes, *Response, error) { @@ -854,7 +854,7 @@ func (s *RepositoriesService) GetAutomatedSecurityFixes(ctx context.Context, own // EnableAutomatedSecurityFixes enables the automated security fixes for a repository. // -// GitHub API docs: https://docs.github.com/rest/repos/repos#enable-automated-security-fixes +// GitHub API docs: https://docs.github.com/rest/repos/repos#enable-dependabot-security-updates // //meta:operation PUT /repos/{owner}/{repo}/automated-security-fixes func (s *RepositoriesService) EnableAutomatedSecurityFixes(ctx context.Context, owner, repository string) (*Response, error) { @@ -870,7 +870,7 @@ func (s *RepositoriesService) EnableAutomatedSecurityFixes(ctx context.Context, // DisableAutomatedSecurityFixes disables vulnerability alerts and the dependency graph for a repository. // -// GitHub API docs: https://docs.github.com/rest/repos/repos#disable-automated-security-fixes +// GitHub API docs: https://docs.github.com/rest/repos/repos#disable-dependabot-security-updates // //meta:operation DELETE /repos/{owner}/{repo}/automated-security-fixes func (s *RepositoriesService) DisableAutomatedSecurityFixes(ctx context.Context, owner, repository string) (*Response, error) { diff --git a/vendor/github.com/google/go-github/v68/github/repos_actions_access.go b/vendor/github.com/google/go-github/v70/github/repos_actions_access.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/repos_actions_access.go rename to vendor/github.com/google/go-github/v70/github/repos_actions_access.go diff --git a/vendor/github.com/google/go-github/v68/github/repos_actions_allowed.go b/vendor/github.com/google/go-github/v70/github/repos_actions_allowed.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/repos_actions_allowed.go rename to vendor/github.com/google/go-github/v70/github/repos_actions_allowed.go diff --git a/vendor/github.com/google/go-github/v68/github/repos_actions_permissions.go b/vendor/github.com/google/go-github/v70/github/repos_actions_permissions.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/repos_actions_permissions.go rename to vendor/github.com/google/go-github/v70/github/repos_actions_permissions.go diff --git a/vendor/github.com/google/go-github/v68/github/repos_attestations.go b/vendor/github.com/google/go-github/v70/github/repos_attestations.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/repos_attestations.go rename to vendor/github.com/google/go-github/v70/github/repos_attestations.go diff --git a/vendor/github.com/google/go-github/v68/github/repos_autolinks.go b/vendor/github.com/google/go-github/v70/github/repos_autolinks.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/repos_autolinks.go rename to vendor/github.com/google/go-github/v70/github/repos_autolinks.go diff --git a/vendor/github.com/google/go-github/v68/github/repos_codeowners.go b/vendor/github.com/google/go-github/v70/github/repos_codeowners.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/repos_codeowners.go rename to vendor/github.com/google/go-github/v70/github/repos_codeowners.go diff --git a/vendor/github.com/google/go-github/v68/github/repos_collaborators.go b/vendor/github.com/google/go-github/v70/github/repos_collaborators.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/repos_collaborators.go rename to vendor/github.com/google/go-github/v70/github/repos_collaborators.go diff --git a/vendor/github.com/google/go-github/v68/github/repos_comments.go b/vendor/github.com/google/go-github/v70/github/repos_comments.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/repos_comments.go rename to vendor/github.com/google/go-github/v70/github/repos_comments.go diff --git a/vendor/github.com/google/go-github/v68/github/repos_commits.go b/vendor/github.com/google/go-github/v70/github/repos_commits.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/repos_commits.go rename to vendor/github.com/google/go-github/v70/github/repos_commits.go diff --git a/vendor/github.com/google/go-github/v68/github/repos_community_health.go b/vendor/github.com/google/go-github/v70/github/repos_community_health.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/repos_community_health.go rename to vendor/github.com/google/go-github/v70/github/repos_community_health.go diff --git a/vendor/github.com/google/go-github/v68/github/repos_contents.go b/vendor/github.com/google/go-github/v70/github/repos_contents.go similarity index 92% rename from vendor/github.com/google/go-github/v68/github/repos_contents.go rename to vendor/github.com/google/go-github/v70/github/repos_contents.go index 3a0c266b5e..013993e5be 100644 --- a/vendor/github.com/google/go-github/v68/github/repos_contents.go +++ b/vendor/github.com/google/go-github/v70/github/repos_contents.go @@ -254,7 +254,7 @@ func (s *RepositoriesService) GetContents(ctx context.Context, owner, repo, path return nil, directoryContent, resp, nil } - return nil, nil, resp, fmt.Errorf("unmarshalling failed for both file and directory content: %s and %s", fileUnmarshalError, directoryUnmarshalError) + return nil, nil, resp, fmt.Errorf("unmarshaling failed for both file and directory content: %s and %s", fileUnmarshalError, directoryUnmarshalError) } // CreateFile creates a new file in a repository at the given path and returns @@ -348,6 +348,15 @@ func (s *RepositoriesService) GetArchiveLink(ctx context.Context, owner, repo st if opts != nil && opts.Ref != "" { u += fmt.Sprintf("/%s", opts.Ref) } + + if s.client.RateLimitRedirectionalEndpoints { + return s.getArchiveLinkWithRateLimit(ctx, u, maxRedirects) + } + + return s.getArchiveLinkWithoutRateLimit(ctx, u, maxRedirects) +} + +func (s *RepositoriesService) getArchiveLinkWithoutRateLimit(ctx context.Context, u string, maxRedirects int) (*url.URL, *Response, error) { resp, err := s.client.roundTripWithOptionalFollowRedirect(ctx, u, maxRedirects) if err != nil { return nil, nil, err @@ -355,7 +364,7 @@ func (s *RepositoriesService) GetArchiveLink(ctx context.Context, owner, repo st defer resp.Body.Close() if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusFound { - return nil, newResponse(resp), fmt.Errorf("unexpected status code: %s", resp.Status) + return nil, newResponse(resp), fmt.Errorf("unexpected status code: %v", resp.Status) } parsedURL, err := url.Parse(resp.Header.Get("Location")) @@ -365,3 +374,23 @@ func (s *RepositoriesService) GetArchiveLink(ctx context.Context, owner, repo st return parsedURL, newResponse(resp), nil } + +func (s *RepositoriesService) getArchiveLinkWithRateLimit(ctx context.Context, u string, maxRedirects int) (*url.URL, *Response, error) { + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + url, resp, err := s.client.bareDoUntilFound(ctx, req, maxRedirects) + if err != nil { + return nil, resp, err + } + defer resp.Body.Close() + + // If we didn't receive a valid Location in a 302 response + if url == nil { + return nil, resp, fmt.Errorf("unexpected status code: %v", resp.Status) + } + + return url, resp, nil +} diff --git a/vendor/github.com/google/go-github/v68/github/repos_deployment_branch_policies.go b/vendor/github.com/google/go-github/v70/github/repos_deployment_branch_policies.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/repos_deployment_branch_policies.go rename to vendor/github.com/google/go-github/v70/github/repos_deployment_branch_policies.go diff --git a/vendor/github.com/google/go-github/v68/github/repos_deployment_protection_rules.go b/vendor/github.com/google/go-github/v70/github/repos_deployment_protection_rules.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/repos_deployment_protection_rules.go rename to vendor/github.com/google/go-github/v70/github/repos_deployment_protection_rules.go diff --git a/vendor/github.com/google/go-github/v68/github/repos_deployments.go b/vendor/github.com/google/go-github/v70/github/repos_deployments.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/repos_deployments.go rename to vendor/github.com/google/go-github/v70/github/repos_deployments.go diff --git a/vendor/github.com/google/go-github/v68/github/repos_environments.go b/vendor/github.com/google/go-github/v70/github/repos_environments.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/repos_environments.go rename to vendor/github.com/google/go-github/v70/github/repos_environments.go diff --git a/vendor/github.com/google/go-github/v68/github/repos_forks.go b/vendor/github.com/google/go-github/v70/github/repos_forks.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/repos_forks.go rename to vendor/github.com/google/go-github/v70/github/repos_forks.go diff --git a/vendor/github.com/google/go-github/v68/github/repos_hooks.go b/vendor/github.com/google/go-github/v70/github/repos_hooks.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/repos_hooks.go rename to vendor/github.com/google/go-github/v70/github/repos_hooks.go diff --git a/vendor/github.com/google/go-github/v68/github/repos_hooks_configuration.go b/vendor/github.com/google/go-github/v70/github/repos_hooks_configuration.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/repos_hooks_configuration.go rename to vendor/github.com/google/go-github/v70/github/repos_hooks_configuration.go diff --git a/vendor/github.com/google/go-github/v68/github/repos_hooks_deliveries.go b/vendor/github.com/google/go-github/v70/github/repos_hooks_deliveries.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/repos_hooks_deliveries.go rename to vendor/github.com/google/go-github/v70/github/repos_hooks_deliveries.go diff --git a/vendor/github.com/google/go-github/v68/github/repos_invitations.go b/vendor/github.com/google/go-github/v70/github/repos_invitations.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/repos_invitations.go rename to vendor/github.com/google/go-github/v70/github/repos_invitations.go diff --git a/vendor/github.com/google/go-github/v68/github/repos_keys.go b/vendor/github.com/google/go-github/v70/github/repos_keys.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/repos_keys.go rename to vendor/github.com/google/go-github/v70/github/repos_keys.go diff --git a/vendor/github.com/google/go-github/v68/github/repos_lfs.go b/vendor/github.com/google/go-github/v70/github/repos_lfs.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/repos_lfs.go rename to vendor/github.com/google/go-github/v70/github/repos_lfs.go diff --git a/vendor/github.com/google/go-github/v68/github/repos_merging.go b/vendor/github.com/google/go-github/v70/github/repos_merging.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/repos_merging.go rename to vendor/github.com/google/go-github/v70/github/repos_merging.go diff --git a/vendor/github.com/google/go-github/v68/github/repos_pages.go b/vendor/github.com/google/go-github/v70/github/repos_pages.go similarity index 91% rename from vendor/github.com/google/go-github/v68/github/repos_pages.go rename to vendor/github.com/google/go-github/v70/github/repos_pages.go index 6b9ba76e44..930f6000b7 100644 --- a/vendor/github.com/google/go-github/v68/github/repos_pages.go +++ b/vendor/github.com/google/go-github/v70/github/repos_pages.go @@ -170,7 +170,36 @@ func (s *RepositoriesService) UpdatePages(ctx context.Context, owner, repo strin if err != nil { return resp, err } + return resp, nil +} + +// PagesUpdateWithoutCNAME defines parameters for updating a GitHub Pages site on GitHub Enterprise Servers. +// Sending a request with a CNAME (any value, empty string, or null) results in a 400 error: "Custom domains are not available for GitHub Pages". +type PagesUpdateWithoutCNAME struct { + BuildType *string `json:"build_type,omitempty"` + Source *PagesSource `json:"source,omitempty"` + Public *bool `json:"public,omitempty"` + HTTPSEnforced *bool `json:"https_enforced,omitempty"` +} + +// UpdatePagesGHES updates GitHub Pages for the named repo in GitHub Enterprise Servers. +// +// GitHub API docs: https://docs.github.com/rest/pages/pages#update-information-about-a-github-pages-site +// +//meta:operation PUT /repos/{owner}/{repo}/pages +func (s *RepositoriesService) UpdatePagesGHES(ctx context.Context, owner, repo string, opts *PagesUpdateWithoutCNAME) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/pages", owner, repo) + req, err := s.client.NewRequest("PUT", u, opts) + + if err != nil { + return nil, err + } + + resp, err := s.client.Do(ctx, req, nil) + if err != nil { + return resp, err + } return resp, nil } diff --git a/vendor/github.com/google/go-github/v68/github/repos_prereceive_hooks.go b/vendor/github.com/google/go-github/v70/github/repos_prereceive_hooks.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/repos_prereceive_hooks.go rename to vendor/github.com/google/go-github/v70/github/repos_prereceive_hooks.go diff --git a/vendor/github.com/google/go-github/v68/github/repos_properties.go b/vendor/github.com/google/go-github/v70/github/repos_properties.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/repos_properties.go rename to vendor/github.com/google/go-github/v70/github/repos_properties.go diff --git a/vendor/github.com/google/go-github/v68/github/repos_releases.go b/vendor/github.com/google/go-github/v70/github/repos_releases.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/repos_releases.go rename to vendor/github.com/google/go-github/v70/github/repos_releases.go diff --git a/vendor/github.com/google/go-github/v70/github/repos_rules.go b/vendor/github.com/google/go-github/v70/github/repos_rules.go new file mode 100644 index 0000000000..d38e35cdd7 --- /dev/null +++ b/vendor/github.com/google/go-github/v70/github/repos_rules.go @@ -0,0 +1,227 @@ +// Copyright 2023 The go-github 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 github + +import ( + "context" + "fmt" +) + +// rulesetNoOmitBypassActors represents a GitHub ruleset object. The struct does not omit bypassActors if the field is nil or an empty array is passed. +type rulesetNoOmitBypassActors struct { + ID *int64 `json:"id,omitempty"` + Name string `json:"name"` + Target *RulesetTarget `json:"target,omitempty"` + SourceType *RulesetSourceType `json:"source_type,omitempty"` + Source string `json:"source"` + Enforcement RulesetEnforcement `json:"enforcement"` + BypassActors []*BypassActor `json:"bypass_actors"` + CurrentUserCanBypass *BypassMode `json:"current_user_can_bypass,omitempty"` + NodeID *string `json:"node_id,omitempty"` + Links *RepositoryRulesetLinks `json:"_links,omitempty"` + Conditions *RepositoryRulesetConditions `json:"conditions,omitempty"` + Rules *RepositoryRulesetRules `json:"rules,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` +} + +// rulesetClearBypassActors is used to clear the bypass actors when modifying a GitHub ruleset object. +type rulesetClearBypassActors struct { + BypassActors []*BypassActor `json:"bypass_actors"` +} + +// GetRulesForBranch gets all the repository rules that apply to the specified branch. +// +// GitHub API docs: https://docs.github.com/rest/repos/rules#get-rules-for-a-branch +// +//meta:operation GET /repos/{owner}/{repo}/rules/branches/{branch} +func (s *RepositoriesService) GetRulesForBranch(ctx context.Context, owner, repo, branch string) (*BranchRules, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/rules/branches/%v", owner, repo, branch) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var rules *BranchRules + resp, err := s.client.Do(ctx, req, &rules) + if err != nil { + return nil, resp, err + } + + return rules, resp, nil +} + +// GetAllRulesets gets all the repository rulesets for the specified repository. +// If includesParents is true, rulesets configured at the organization or enterprise level that apply to the repository will be returned. +// +// GitHub API docs: https://docs.github.com/rest/repos/rules#get-all-repository-rulesets +// +//meta:operation GET /repos/{owner}/{repo}/rulesets +func (s *RepositoriesService) GetAllRulesets(ctx context.Context, owner, repo string, includesParents bool) ([]*RepositoryRuleset, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/rulesets?includes_parents=%v", owner, repo, includesParents) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var ruleset []*RepositoryRuleset + resp, err := s.client.Do(ctx, req, &ruleset) + if err != nil { + return nil, resp, err + } + + return ruleset, resp, nil +} + +// CreateRuleset creates a repository ruleset for the specified repository. +// +// GitHub API docs: https://docs.github.com/rest/repos/rules#create-a-repository-ruleset +// +//meta:operation POST /repos/{owner}/{repo}/rulesets +func (s *RepositoriesService) CreateRuleset(ctx context.Context, owner, repo string, ruleset RepositoryRuleset) (*RepositoryRuleset, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/rulesets", owner, repo) + + req, err := s.client.NewRequest("POST", u, ruleset) + if err != nil { + return nil, nil, err + } + + var rs *RepositoryRuleset + resp, err := s.client.Do(ctx, req, &rs) + if err != nil { + return nil, resp, err + } + + return rs, resp, nil +} + +// GetRuleset gets a repository ruleset for the specified repository. +// If includesParents is true, rulesets configured at the organization or enterprise level that apply to the repository will be returned. +// +// GitHub API docs: https://docs.github.com/rest/repos/rules#get-a-repository-ruleset +// +//meta:operation GET /repos/{owner}/{repo}/rulesets/{ruleset_id} +func (s *RepositoriesService) GetRuleset(ctx context.Context, owner, repo string, rulesetID int64, includesParents bool) (*RepositoryRuleset, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/rulesets/%v?includes_parents=%v", owner, repo, rulesetID, includesParents) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + var ruleset *RepositoryRuleset + resp, err := s.client.Do(ctx, req, &ruleset) + if err != nil { + return nil, resp, err + } + + return ruleset, resp, nil +} + +// UpdateRuleset updates a repository ruleset for the specified repository. +// +// GitHub API docs: https://docs.github.com/rest/repos/rules#update-a-repository-ruleset +// +//meta:operation PUT /repos/{owner}/{repo}/rulesets/{ruleset_id} +func (s *RepositoriesService) UpdateRuleset(ctx context.Context, owner, repo string, rulesetID int64, ruleset RepositoryRuleset) (*RepositoryRuleset, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/rulesets/%v", owner, repo, rulesetID) + + req, err := s.client.NewRequest("PUT", u, ruleset) + if err != nil { + return nil, nil, err + } + + var rs *RepositoryRuleset + resp, err := s.client.Do(ctx, req, &rs) + if err != nil { + return nil, resp, err + } + + return rs, resp, nil +} + +// UpdateRulesetClearBypassActor clears the bypass actors for a repository ruleset for the specified repository. +// +// This function is necessary as the UpdateRuleset function does not marshal ByPassActor if passed as an empty array. +// +// GitHub API docs: https://docs.github.com/rest/repos/rules#update-a-repository-ruleset +// +//meta:operation PUT /repos/{owner}/{repo}/rulesets/{ruleset_id} +func (s *RepositoriesService) UpdateRulesetClearBypassActor(ctx context.Context, owner, repo string, rulesetID int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/rulesets/%v", owner, repo, rulesetID) + + rsClearBypassActor := rulesetClearBypassActors{} + + req, err := s.client.NewRequest("PUT", u, rsClearBypassActor) + if err != nil { + return nil, err + } + + resp, err := s.client.Do(ctx, req, nil) + if err != nil { + return resp, err + } + + return resp, nil +} + +// UpdateRulesetNoBypassActor updates a repository ruleset for the specified repository. +// +// This function is necessary as the UpdateRuleset function does not marshal ByPassActor if passed as an empty array. +// +// Deprecated: Use UpdateRulesetClearBypassActor instead. +// +// GitHub API docs: https://docs.github.com/rest/repos/rules#update-a-repository-ruleset +// +//meta:operation PUT /repos/{owner}/{repo}/rulesets/{ruleset_id} +func (s *RepositoriesService) UpdateRulesetNoBypassActor(ctx context.Context, owner, repo string, rulesetID int64, ruleset RepositoryRuleset) (*RepositoryRuleset, *Response, error) { + u := fmt.Sprintf("repos/%v/%v/rulesets/%v", owner, repo, rulesetID) + + rsNoBypassActor := rulesetNoOmitBypassActors{ + ID: ruleset.ID, + Name: ruleset.Name, + Target: ruleset.Target, + SourceType: ruleset.SourceType, + Source: ruleset.Source, + Enforcement: ruleset.Enforcement, + BypassActors: ruleset.BypassActors, + NodeID: ruleset.NodeID, + Links: ruleset.Links, + Conditions: ruleset.Conditions, + Rules: ruleset.Rules, + } + + req, err := s.client.NewRequest("PUT", u, rsNoBypassActor) + if err != nil { + return nil, nil, err + } + + var rs *RepositoryRuleset + resp, err := s.client.Do(ctx, req, &rs) + if err != nil { + return nil, resp, err + } + + return rs, resp, nil +} + +// DeleteRuleset deletes a repository ruleset for the specified repository. +// +// GitHub API docs: https://docs.github.com/rest/repos/rules#delete-a-repository-ruleset +// +//meta:operation DELETE /repos/{owner}/{repo}/rulesets/{ruleset_id} +func (s *RepositoriesService) DeleteRuleset(ctx context.Context, owner, repo string, rulesetID int64) (*Response, error) { + u := fmt.Sprintf("repos/%v/%v/rulesets/%v", owner, repo, rulesetID) + + req, err := s.client.NewRequest("DELETE", u, nil) + if err != nil { + return nil, err + } + + return s.client.Do(ctx, req, nil) +} diff --git a/vendor/github.com/google/go-github/v68/github/repos_stats.go b/vendor/github.com/google/go-github/v70/github/repos_stats.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/repos_stats.go rename to vendor/github.com/google/go-github/v70/github/repos_stats.go diff --git a/vendor/github.com/google/go-github/v68/github/repos_statuses.go b/vendor/github.com/google/go-github/v70/github/repos_statuses.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/repos_statuses.go rename to vendor/github.com/google/go-github/v70/github/repos_statuses.go diff --git a/vendor/github.com/google/go-github/v68/github/repos_tags.go b/vendor/github.com/google/go-github/v70/github/repos_tags.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/repos_tags.go rename to vendor/github.com/google/go-github/v70/github/repos_tags.go diff --git a/vendor/github.com/google/go-github/v68/github/repos_traffic.go b/vendor/github.com/google/go-github/v70/github/repos_traffic.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/repos_traffic.go rename to vendor/github.com/google/go-github/v70/github/repos_traffic.go diff --git a/vendor/github.com/google/go-github/v70/github/rules.go b/vendor/github.com/google/go-github/v70/github/rules.go new file mode 100644 index 0000000000..985f0aac10 --- /dev/null +++ b/vendor/github.com/google/go-github/v70/github/rules.go @@ -0,0 +1,1208 @@ +// Copyright 2025 The go-github 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 github + +import ( + "encoding/json" + "reflect" +) + +// RulesetTarget represents a GitHub ruleset target. +type RulesetTarget string + +// This is the set of GitHub ruleset targets. +const ( + RulesetTargetBranch RulesetTarget = "branch" + RulesetTargetTag RulesetTarget = "tag" + RulesetTargetPush RulesetTarget = "push" +) + +// RulesetSourceType represents a GitHub ruleset source type. +type RulesetSourceType string + +// This is the set of GitHub ruleset source types. +const ( + RulesetSourceTypeRepository RulesetSourceType = "Repository" + RulesetSourceTypeOrganization RulesetSourceType = "Organization" + RulesetSourceTypeEnterprise RulesetSourceType = "Enterprise" +) + +// RulesetEnforcement represents a GitHub ruleset enforcement. +type RulesetEnforcement string + +// This is the set of GitHub ruleset enforcements. +const ( + RulesetEnforcementDisabled RulesetEnforcement = "disabled" + RulesetEnforcementActive RulesetEnforcement = "active" + RulesetEnforcementEvaluate RulesetEnforcement = "evaluate" +) + +// BypassActorType represents a GitHub ruleset bypass actor type. +type BypassActorType string + +// This is the set of GitHub ruleset bypass actor types. +const ( + BypassActorTypeIntegration BypassActorType = "Integration" + BypassActorTypeOrganizationAdmin BypassActorType = "OrganizationAdmin" + BypassActorTypeRepositoryRole BypassActorType = "RepositoryRole" + BypassActorTypeTeam BypassActorType = "Team" + BypassActorTypeDeployKey BypassActorType = "DeployKey" +) + +// BypassMode represents a GitHub ruleset bypass mode. +type BypassMode string + +// This is the set of GitHub ruleset bypass modes. +const ( + BypassModeAlways BypassMode = "always" + BypassModePullRequest BypassMode = "pull_request" + BypassModeNever BypassMode = "never" +) + +// RepositoryRuleType represents a GitHub ruleset rule type. +type RepositoryRuleType string + +// This is the set of GitHub ruleset rule types. +const ( + RulesetRuleTypeCreation RepositoryRuleType = "creation" + RulesetRuleTypeUpdate RepositoryRuleType = "update" + RulesetRuleTypeDeletion RepositoryRuleType = "deletion" + RulesetRuleTypeRequiredLinearHistory RepositoryRuleType = "required_linear_history" + RulesetRuleTypeMergeQueue RepositoryRuleType = "merge_queue" + RulesetRuleTypeRequiredDeployments RepositoryRuleType = "required_deployments" + RulesetRuleTypeRequiredSignatures RepositoryRuleType = "required_signatures" + RulesetRuleTypePullRequest RepositoryRuleType = "pull_request" + RulesetRuleTypeRequiredStatusChecks RepositoryRuleType = "required_status_checks" + RulesetRuleTypeNonFastForward RepositoryRuleType = "non_fast_forward" + RulesetRuleTypeCommitMessagePattern RepositoryRuleType = "commit_message_pattern" + RulesetRuleTypeCommitAuthorEmailPattern RepositoryRuleType = "commit_author_email_pattern" + RulesetRuleTypeCommitterEmailPattern RepositoryRuleType = "committer_email_pattern" + RulesetRuleTypeBranchNamePattern RepositoryRuleType = "branch_name_pattern" + RulesetRuleTypeTagNamePattern RepositoryRuleType = "tag_name_pattern" + RulesetRuleTypeFilePathRestriction RepositoryRuleType = "file_path_restriction" + RulesetRuleTypeMaxFilePathLength RepositoryRuleType = "max_file_path_length" + RulesetRuleTypeFileExtensionRestriction RepositoryRuleType = "file_extension_restriction" + RulesetRuleTypeMaxFileSize RepositoryRuleType = "max_file_size" + RulesetRuleTypeWorkflows RepositoryRuleType = "workflows" + RulesetRuleTypeCodeScanning RepositoryRuleType = "code_scanning" +) + +// MergeGroupingStrategy models a GitHub merge grouping strategy. +type MergeGroupingStrategy string + +// This is the set of GitHub merge grouping strategies. +const ( + MergeGroupingStrategyAllGreen MergeGroupingStrategy = "ALLGREEN" + MergeGroupingStrategyHeadGreen MergeGroupingStrategy = "HEADGREEN" +) + +// MergeMethod models a GitHub merge method. +type MergeMethod string + +// This is the set of GitHub merge methods. +const ( + MergeMethodMerge MergeMethod = "merge" + MergeMethodRebase MergeMethod = "rebase" + MergeMethodSquash MergeMethod = "squash" +) + +// PatternRuleOperator models a GitHub pattern rule operator. +type PatternRuleOperator string + +// This is the set of GitHub pattern rule operators. +const ( + PatternRuleOperatorStartsWith PatternRuleOperator = "starts_with" + PatternRuleOperatorEndsWith PatternRuleOperator = "ends_with" + PatternRuleOperatorContains PatternRuleOperator = "contains" + PatternRuleOperatorRegex PatternRuleOperator = "regex" +) + +// CodeScanningAlertsThreshold models a GitHub code scanning alerts threshold. +type CodeScanningAlertsThreshold string + +// This is the set of GitHub code scanning alerts thresholds. +const ( + CodeScanningAlertsThresholdNone CodeScanningAlertsThreshold = "none" + CodeScanningAlertsThresholdErrors CodeScanningAlertsThreshold = "errors" + CodeScanningAlertsThresholdErrorsAndWarnings CodeScanningAlertsThreshold = "errors_and_warnings" + CodeScanningAlertsThresholdAll CodeScanningAlertsThreshold = "all" +) + +// CodeScanningSecurityAlertsThreshold models a GitHub code scanning security alerts threshold. +type CodeScanningSecurityAlertsThreshold string + +// This is the set of GitHub code scanning security alerts thresholds. +const ( + CodeScanningSecurityAlertsThresholdNone CodeScanningSecurityAlertsThreshold = "none" + CodeScanningSecurityAlertsThresholdCritical CodeScanningSecurityAlertsThreshold = "critical" + CodeScanningSecurityAlertsThresholdHighOrHigher CodeScanningSecurityAlertsThreshold = "high_or_higher" + CodeScanningSecurityAlertsThresholdMediumOrHigher CodeScanningSecurityAlertsThreshold = "medium_or_higher" + CodeScanningSecurityAlertsThresholdAll CodeScanningSecurityAlertsThreshold = "all" +) + +// RepositoryRuleset represents a GitHub ruleset object. +type RepositoryRuleset struct { + ID *int64 `json:"id,omitempty"` + Name string `json:"name"` + Target *RulesetTarget `json:"target,omitempty"` + SourceType *RulesetSourceType `json:"source_type,omitempty"` + Source string `json:"source"` + Enforcement RulesetEnforcement `json:"enforcement"` + BypassActors []*BypassActor `json:"bypass_actors,omitempty"` + CurrentUserCanBypass *BypassMode `json:"current_user_can_bypass,omitempty"` + NodeID *string `json:"node_id,omitempty"` + Links *RepositoryRulesetLinks `json:"_links,omitempty"` + Conditions *RepositoryRulesetConditions `json:"conditions,omitempty"` + Rules *RepositoryRulesetRules `json:"rules,omitempty"` + UpdatedAt *Timestamp `json:"updated_at,omitempty"` + CreatedAt *Timestamp `json:"created_at,omitempty"` +} + +// BypassActor represents the bypass actors from a ruleset. +type BypassActor struct { + ActorID *int64 `json:"actor_id,omitempty"` + ActorType *BypassActorType `json:"actor_type,omitempty"` + BypassMode *BypassMode `json:"bypass_mode,omitempty"` +} + +// RepositoryRulesetLinks represents the "_links" object in a Ruleset. +type RepositoryRulesetLinks struct { + Self *RepositoryRulesetLink `json:"self,omitempty"` + HTML *RepositoryRulesetLink `json:"html,omitempty"` +} + +// RepositoryRulesetLink represents a single link object from GitHub ruleset request _links. +type RepositoryRulesetLink struct { + HRef *string `json:"href,omitempty"` +} + +// RepositoryRulesetConditions represents the conditions object in a ruleset. +// Set either RepositoryName or RepositoryID or RepositoryProperty, not more than one. +type RepositoryRulesetConditions struct { + RefName *RepositoryRulesetRefConditionParameters `json:"ref_name,omitempty"` + RepositoryID *RepositoryRulesetRepositoryIDsConditionParameters `json:"repository_id,omitempty"` + RepositoryName *RepositoryRulesetRepositoryNamesConditionParameters `json:"repository_name,omitempty"` + RepositoryProperty *RepositoryRulesetRepositoryPropertyConditionParameters `json:"repository_property,omitempty"` + OrganizationID *RepositoryRulesetOrganizationIDsConditionParameters `json:"organization_id,omitempty"` + OrganizationName *RepositoryRulesetOrganizationNamesConditionParameters `json:"organization_name,omitempty"` +} + +// RepositoryRulesetRefConditionParameters represents the conditions object for ref_names. +type RepositoryRulesetRefConditionParameters struct { + Include []string `json:"include"` + Exclude []string `json:"exclude"` +} + +// RepositoryRulesetRepositoryIDsConditionParameters represents the conditions object for repository_id. +type RepositoryRulesetRepositoryIDsConditionParameters struct { + RepositoryIDs []int64 `json:"repository_ids,omitempty"` +} + +// RepositoryRulesetRepositoryNamesConditionParameters represents the conditions object for repository_name. +type RepositoryRulesetRepositoryNamesConditionParameters struct { + Include []string `json:"include"` + Exclude []string `json:"exclude"` + Protected *bool `json:"protected,omitempty"` +} + +// RepositoryRulesetRepositoryPropertyConditionParameters represents the conditions object for repository_property. +type RepositoryRulesetRepositoryPropertyConditionParameters struct { + Include []*RepositoryRulesetRepositoryPropertyTargetParameters `json:"include"` + Exclude []*RepositoryRulesetRepositoryPropertyTargetParameters `json:"exclude"` +} + +// RepositoryRulesetRepositoryPropertyTargetParameters represents a repository_property name and values to be used for targeting. +type RepositoryRulesetRepositoryPropertyTargetParameters struct { + Name string `json:"name"` + PropertyValues []string `json:"property_values"` + Source *string `json:"source,omitempty"` +} + +// RepositoryRulesetOrganizationIDsConditionParameters represents the conditions object for organization_id. +type RepositoryRulesetOrganizationIDsConditionParameters struct { + OrganizationIDs []int64 `json:"organization_ids,omitempty"` +} + +// RepositoryRulesetOrganizationNamesConditionParameters represents the conditions object for organization_name. +type RepositoryRulesetOrganizationNamesConditionParameters struct { + Include []string `json:"include"` + Exclude []string `json:"exclude"` +} + +// RepositoryRule represents a GitHub ruleset rule object. +type RepositoryRule struct { + Type RepositoryRuleType `json:"type"` + Parameters any `json:"parameters,omitempty"` +} + +// RepositoryRulesetRules represents a GitHub ruleset rules object. +// This type doesn't have JSON annotations as it uses custom marshaling. +type RepositoryRulesetRules struct { + Creation *EmptyRuleParameters + Update *UpdateRuleParameters + Deletion *EmptyRuleParameters + RequiredLinearHistory *EmptyRuleParameters + MergeQueue *MergeQueueRuleParameters + RequiredDeployments *RequiredDeploymentsRuleParameters + RequiredSignatures *EmptyRuleParameters + PullRequest *PullRequestRuleParameters + RequiredStatusChecks *RequiredStatusChecksRuleParameters + NonFastForward *EmptyRuleParameters + CommitMessagePattern *PatternRuleParameters + CommitAuthorEmailPattern *PatternRuleParameters + CommitterEmailPattern *PatternRuleParameters + BranchNamePattern *PatternRuleParameters + TagNamePattern *PatternRuleParameters + FilePathRestriction *FilePathRestrictionRuleParameters + MaxFilePathLength *MaxFilePathLengthRuleParameters + FileExtensionRestriction *FileExtensionRestrictionRuleParameters + MaxFileSize *MaxFileSizeRuleParameters + Workflows *WorkflowsRuleParameters + CodeScanning *CodeScanningRuleParameters +} + +// BranchRules represents the rules active for a GitHub repository branch. +// This type doesn't have JSON annotations as it uses custom marshaling. +type BranchRules struct { + Creation []*BranchRuleMetadata + Update []*UpdateBranchRule + Deletion []*BranchRuleMetadata + RequiredLinearHistory []*BranchRuleMetadata + MergeQueue []*MergeQueueBranchRule + RequiredDeployments []*RequiredDeploymentsBranchRule + RequiredSignatures []*BranchRuleMetadata + PullRequest []*PullRequestBranchRule + RequiredStatusChecks []*RequiredStatusChecksBranchRule + NonFastForward []*BranchRuleMetadata + CommitMessagePattern []*PatternBranchRule + CommitAuthorEmailPattern []*PatternBranchRule + CommitterEmailPattern []*PatternBranchRule + BranchNamePattern []*PatternBranchRule + TagNamePattern []*PatternBranchRule + FilePathRestriction []*FilePathRestrictionBranchRule + MaxFilePathLength []*MaxFilePathLengthBranchRule + FileExtensionRestriction []*FileExtensionRestrictionBranchRule + MaxFileSize []*MaxFileSizeBranchRule + Workflows []*WorkflowsBranchRule + CodeScanning []*CodeScanningBranchRule +} + +// BranchRuleMetadata represents the metadata for a branch rule. +type BranchRuleMetadata struct { + RulesetSourceType RulesetSourceType `json:"ruleset_source_type"` + RulesetSource string `json:"ruleset_source"` + RulesetID int64 `json:"ruleset_id"` +} + +// UpdateBranchRule represents an update branch rule. +type UpdateBranchRule struct { + BranchRuleMetadata + Parameters UpdateRuleParameters `json:"parameters"` +} + +// MergeQueueBranchRule represents a merge queue branch rule. +type MergeQueueBranchRule struct { + BranchRuleMetadata + Parameters MergeQueueRuleParameters `json:"parameters"` +} + +// RequiredDeploymentsBranchRule represents a required deployments branch rule. +type RequiredDeploymentsBranchRule struct { + BranchRuleMetadata + Parameters RequiredDeploymentsRuleParameters `json:"parameters"` +} + +// PullRequestBranchRule represents a pull request branch rule. +type PullRequestBranchRule struct { + BranchRuleMetadata + Parameters PullRequestRuleParameters `json:"parameters"` +} + +// RequiredStatusChecksBranchRule represents a required status checks branch rule. +type RequiredStatusChecksBranchRule struct { + BranchRuleMetadata + Parameters RequiredStatusChecksRuleParameters `json:"parameters"` +} + +// PatternBranchRule represents a pattern branch rule. +type PatternBranchRule struct { + BranchRuleMetadata + Parameters PatternRuleParameters `json:"parameters"` +} + +// FilePathRestrictionBranchRule represents a file path restriction branch rule. +type FilePathRestrictionBranchRule struct { + BranchRuleMetadata + Parameters FilePathRestrictionRuleParameters `json:"parameters"` +} + +// MaxFilePathLengthBranchRule represents a max file path length branch rule. +type MaxFilePathLengthBranchRule struct { + BranchRuleMetadata + Parameters MaxFilePathLengthRuleParameters `json:"parameters"` +} + +// FileExtensionRestrictionBranchRule represents a file extension restriction branch rule. +type FileExtensionRestrictionBranchRule struct { + BranchRuleMetadata + Parameters FileExtensionRestrictionRuleParameters `json:"parameters"` +} + +// MaxFileSizeBranchRule represents a max file size branch rule. +type MaxFileSizeBranchRule struct { + BranchRuleMetadata + Parameters MaxFileSizeRuleParameters `json:"parameters"` +} + +// WorkflowsBranchRule represents a workflows branch rule. +type WorkflowsBranchRule struct { + BranchRuleMetadata + Parameters WorkflowsRuleParameters `json:"parameters"` +} + +// CodeScanningBranchRule represents a code scanning branch rule. +type CodeScanningBranchRule struct { + BranchRuleMetadata + Parameters CodeScanningRuleParameters `json:"parameters"` +} + +// EmptyRuleParameters represents the parameters for a rule with no options. +type EmptyRuleParameters struct{} + +// UpdateRuleParameters represents the update rule parameters. +type UpdateRuleParameters struct { + UpdateAllowsFetchAndMerge bool `json:"update_allows_fetch_and_merge,omitempty"` +} + +// MergeQueueRuleParameters represents the merge_queue rule parameters. +type MergeQueueRuleParameters struct { + CheckResponseTimeoutMinutes int `json:"check_response_timeout_minutes"` + GroupingStrategy MergeGroupingStrategy `json:"grouping_strategy"` + MaxEntriesToBuild int `json:"max_entries_to_build"` + MaxEntriesToMerge int `json:"max_entries_to_merge"` + MergeMethod MergeMethod `json:"merge_method"` + MinEntriesToMerge int `json:"min_entries_to_merge"` + MinEntriesToMergeWaitMinutes int `json:"min_entries_to_merge_wait_minutes"` +} + +// RequiredDeploymentsRuleParameters represents the required deployments rule parameters. +type RequiredDeploymentsRuleParameters struct { + RequiredDeploymentEnvironments []string `json:"required_deployment_environments"` +} + +// PullRequestRuleParameters represents the pull_request rule parameters. +type PullRequestRuleParameters struct { + AllowedMergeMethods []MergeMethod `json:"allowed_merge_methods"` + AutomaticCopilotCodeReviewEnabled *bool `json:"automatic_copilot_code_review_enabled,omitempty"` + DismissStaleReviewsOnPush bool `json:"dismiss_stale_reviews_on_push"` + RequireCodeOwnerReview bool `json:"require_code_owner_review"` + RequireLastPushApproval bool `json:"require_last_push_approval"` + RequiredApprovingReviewCount int `json:"required_approving_review_count"` + RequiredReviewThreadResolution bool `json:"required_review_thread_resolution"` +} + +// RequiredStatusChecksRuleParameters represents the required status checks rule parameters. +type RequiredStatusChecksRuleParameters struct { + DoNotEnforceOnCreate *bool `json:"do_not_enforce_on_create,omitempty"` + RequiredStatusChecks []*RuleStatusCheck `json:"required_status_checks"` + StrictRequiredStatusChecksPolicy bool `json:"strict_required_status_checks_policy"` +} + +// RuleStatusCheck represents a status checks for the required status checks rule parameters. +type RuleStatusCheck struct { + Context string `json:"context"` + IntegrationID *int64 `json:"integration_id,omitempty"` +} + +// PatternRuleParameters represents the parameters for a pattern rule. +type PatternRuleParameters struct { + Name *string `json:"name,omitempty"` + // If Negate is true, the rule will fail if the pattern matches. + Negate *bool `json:"negate,omitempty"` + Operator PatternRuleOperator `json:"operator"` + Pattern string `json:"pattern"` +} + +// FilePathRestrictionRuleParameters represents the file path restriction rule parameters. +type FilePathRestrictionRuleParameters struct { + RestrictedFilePaths []string `json:"restricted_file_paths"` +} + +// MaxFilePathLengthRuleParameters represents the max file path length rule parameters. +type MaxFilePathLengthRuleParameters struct { + MaxFilePathLength int `json:"max_file_path_length"` +} + +// FileExtensionRestrictionRuleParameters represents the file extension restriction rule parameters. +type FileExtensionRestrictionRuleParameters struct { + RestrictedFileExtensions []string `json:"restricted_file_extensions"` +} + +// MaxFileSizeRuleParameters represents the max file size rule parameters. +type MaxFileSizeRuleParameters struct { + MaxFileSize int64 `json:"max_file_size"` +} + +// WorkflowsRuleParameters represents the workflows rule parameters. +type WorkflowsRuleParameters struct { + DoNotEnforceOnCreate *bool `json:"do_not_enforce_on_create,omitempty"` + Workflows []*RuleWorkflow `json:"workflows"` +} + +// RuleWorkflow represents a Workflow for the workflows rule parameters. +type RuleWorkflow struct { + Path string `json:"path"` + Ref *string `json:"ref,omitempty"` + RepositoryID *int64 `json:"repository_id,omitempty"` + SHA *string `json:"sha,omitempty"` +} + +// CodeScanningRuleParameters represents the code scanning rule parameters. +type CodeScanningRuleParameters struct { + CodeScanningTools []*RuleCodeScanningTool `json:"code_scanning_tools"` +} + +// RuleCodeScanningTool represents a single code scanning tool for the code scanning parameters. +type RuleCodeScanningTool struct { + AlertsThreshold CodeScanningAlertsThreshold `json:"alerts_threshold"` + SecurityAlertsThreshold CodeScanningSecurityAlertsThreshold `json:"security_alerts_threshold"` + Tool string `json:"tool"` +} + +// repositoryRulesetRuleWrapper is a helper type to marshal & unmarshal a ruleset rule. +type repositoryRulesetRuleWrapper struct { + Type RepositoryRuleType `json:"type"` + Parameters json.RawMessage `json:"parameters,omitempty"` +} + +// MarshalJSON is a custom JSON marshaler for RulesetRules. +func (r *RepositoryRulesetRules) MarshalJSON() ([]byte, error) { + // The RepositoryRulesetRules type marshals to between 1 and 21 rules. + // If new rules are added to RepositoryRulesetRules the capacity below needs increasing + rawRules := make([]json.RawMessage, 0, 21) + + if r.Creation != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeCreation, r.Creation) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.Update != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeUpdate, r.Update) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.Deletion != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeDeletion, r.Deletion) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.RequiredLinearHistory != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeRequiredLinearHistory, r.RequiredLinearHistory) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.MergeQueue != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeMergeQueue, r.MergeQueue) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.RequiredDeployments != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeRequiredDeployments, r.RequiredDeployments) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.RequiredSignatures != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeRequiredSignatures, r.RequiredSignatures) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.PullRequest != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypePullRequest, r.PullRequest) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.RequiredStatusChecks != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeRequiredStatusChecks, r.RequiredStatusChecks) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.NonFastForward != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeNonFastForward, r.NonFastForward) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.CommitMessagePattern != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeCommitMessagePattern, r.CommitMessagePattern) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.CommitAuthorEmailPattern != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeCommitAuthorEmailPattern, r.CommitAuthorEmailPattern) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.CommitterEmailPattern != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeCommitterEmailPattern, r.CommitterEmailPattern) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.BranchNamePattern != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeBranchNamePattern, r.BranchNamePattern) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.TagNamePattern != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeTagNamePattern, r.TagNamePattern) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.FilePathRestriction != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeFilePathRestriction, r.FilePathRestriction) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.MaxFilePathLength != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeMaxFilePathLength, r.MaxFilePathLength) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.FileExtensionRestriction != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeFileExtensionRestriction, r.FileExtensionRestriction) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.MaxFileSize != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeMaxFileSize, r.MaxFileSize) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.Workflows != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeWorkflows, r.Workflows) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + if r.CodeScanning != nil { + bytes, err := marshalRepositoryRulesetRule(RulesetRuleTypeCodeScanning, r.CodeScanning) + if err != nil { + return nil, err + } + rawRules = append(rawRules, json.RawMessage(bytes)) + } + + return json.Marshal(rawRules) +} + +// marshalRepositoryRulesetRule is a helper function to marshal a ruleset rule. +// +// TODO: Benchmark the code that uses reflection. +// TODO: Use a generator here instead of reflection if there is a significant performance hit. +func marshalRepositoryRulesetRule[T any](t RepositoryRuleType, params T) ([]byte, error) { + paramsType := reflect.TypeFor[T]() + + if paramsType.Kind() == reflect.Pointer && (reflect.ValueOf(params).IsNil() || reflect.ValueOf(params).Elem().IsZero()) { + return json.Marshal(repositoryRulesetRuleWrapper{Type: t}) + } + + bytes, err := json.Marshal(params) + if err != nil { + return nil, err + } + + return json.Marshal(repositoryRulesetRuleWrapper{Type: t, Parameters: json.RawMessage(bytes)}) +} + +// UnmarshalJSON is a custom JSON unmarshaler for RulesetRules. +func (r *RepositoryRulesetRules) UnmarshalJSON(data []byte) error { + var wrappers []*repositoryRulesetRuleWrapper + + if err := json.Unmarshal(data, &wrappers); err != nil { + return err + } + + for _, w := range wrappers { + switch w.Type { + case RulesetRuleTypeCreation: + r.Creation = &EmptyRuleParameters{} + case RulesetRuleTypeUpdate: + r.Update = &UpdateRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.Update); err != nil { + return err + } + } + case RulesetRuleTypeDeletion: + r.Deletion = &EmptyRuleParameters{} + case RulesetRuleTypeRequiredLinearHistory: + r.RequiredLinearHistory = &EmptyRuleParameters{} + case RulesetRuleTypeMergeQueue: + r.MergeQueue = &MergeQueueRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.MergeQueue); err != nil { + return err + } + } + case RulesetRuleTypeRequiredDeployments: + r.RequiredDeployments = &RequiredDeploymentsRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.RequiredDeployments); err != nil { + return err + } + } + case RulesetRuleTypeRequiredSignatures: + r.RequiredSignatures = &EmptyRuleParameters{} + case RulesetRuleTypePullRequest: + r.PullRequest = &PullRequestRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.PullRequest); err != nil { + return err + } + } + case RulesetRuleTypeRequiredStatusChecks: + r.RequiredStatusChecks = &RequiredStatusChecksRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.RequiredStatusChecks); err != nil { + return err + } + } + case RulesetRuleTypeNonFastForward: + r.NonFastForward = &EmptyRuleParameters{} + case RulesetRuleTypeCommitMessagePattern: + r.CommitMessagePattern = &PatternRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.CommitMessagePattern); err != nil { + return err + } + } + case RulesetRuleTypeCommitAuthorEmailPattern: + r.CommitAuthorEmailPattern = &PatternRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.CommitAuthorEmailPattern); err != nil { + return err + } + } + case RulesetRuleTypeCommitterEmailPattern: + r.CommitterEmailPattern = &PatternRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.CommitterEmailPattern); err != nil { + return err + } + } + case RulesetRuleTypeBranchNamePattern: + r.BranchNamePattern = &PatternRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.BranchNamePattern); err != nil { + return err + } + } + case RulesetRuleTypeTagNamePattern: + r.TagNamePattern = &PatternRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.TagNamePattern); err != nil { + return err + } + } + case RulesetRuleTypeFilePathRestriction: + r.FilePathRestriction = &FilePathRestrictionRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.FilePathRestriction); err != nil { + return err + } + } + case RulesetRuleTypeMaxFilePathLength: + r.MaxFilePathLength = &MaxFilePathLengthRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.MaxFilePathLength); err != nil { + return err + } + } + case RulesetRuleTypeFileExtensionRestriction: + r.FileExtensionRestriction = &FileExtensionRestrictionRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.FileExtensionRestriction); err != nil { + return err + } + } + case RulesetRuleTypeMaxFileSize: + r.MaxFileSize = &MaxFileSizeRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.MaxFileSize); err != nil { + return err + } + } + case RulesetRuleTypeWorkflows: + r.Workflows = &WorkflowsRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.Workflows); err != nil { + return err + } + } + case RulesetRuleTypeCodeScanning: + r.CodeScanning = &CodeScanningRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, r.CodeScanning); err != nil { + return err + } + } + } + } + + return nil +} + +// branchRuleWrapper is a helper type to unmarshal a branch rule. +type branchRuleWrapper struct { + Type RepositoryRuleType `json:"type"` + BranchRuleMetadata + Parameters json.RawMessage `json:"parameters,omitempty"` +} + +// UnmarshalJSON is a custom JSON unmarshaler for BranchRules. +func (r *BranchRules) UnmarshalJSON(data []byte) error { + var wrappers []*branchRuleWrapper + + if err := json.Unmarshal(data, &wrappers); err != nil { + return err + } + + for _, w := range wrappers { + switch w.Type { + case RulesetRuleTypeCreation: + r.Creation = append(r.Creation, &BranchRuleMetadata{RulesetSourceType: w.RulesetSourceType, RulesetSource: w.RulesetSource, RulesetID: w.RulesetID}) + case RulesetRuleTypeUpdate: + params := &UpdateRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.Update = append(r.Update, &UpdateBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeDeletion: + r.Deletion = append(r.Deletion, &BranchRuleMetadata{RulesetSourceType: w.RulesetSourceType, RulesetSource: w.RulesetSource, RulesetID: w.RulesetID}) + case RulesetRuleTypeRequiredLinearHistory: + r.RequiredLinearHistory = append(r.RequiredLinearHistory, &BranchRuleMetadata{RulesetSourceType: w.RulesetSourceType, RulesetSource: w.RulesetSource, RulesetID: w.RulesetID}) + case RulesetRuleTypeMergeQueue: + params := &MergeQueueRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.MergeQueue = append(r.MergeQueue, &MergeQueueBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeRequiredDeployments: + params := &RequiredDeploymentsRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.RequiredDeployments = append(r.RequiredDeployments, &RequiredDeploymentsBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeRequiredSignatures: + r.RequiredSignatures = append(r.RequiredSignatures, &BranchRuleMetadata{RulesetSourceType: w.RulesetSourceType, RulesetSource: w.RulesetSource, RulesetID: w.RulesetID}) + case RulesetRuleTypePullRequest: + params := &PullRequestRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.PullRequest = append(r.PullRequest, &PullRequestBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeRequiredStatusChecks: + params := &RequiredStatusChecksRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.RequiredStatusChecks = append(r.RequiredStatusChecks, &RequiredStatusChecksBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeNonFastForward: + r.NonFastForward = append(r.NonFastForward, &BranchRuleMetadata{RulesetSourceType: w.RulesetSourceType, RulesetSource: w.RulesetSource, RulesetID: w.RulesetID}) + case RulesetRuleTypeCommitMessagePattern: + params := &PatternRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.CommitMessagePattern = append(r.CommitMessagePattern, &PatternBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeCommitAuthorEmailPattern: + params := &PatternRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.CommitAuthorEmailPattern = append(r.CommitAuthorEmailPattern, &PatternBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeCommitterEmailPattern: + params := &PatternRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.CommitterEmailPattern = append(r.CommitterEmailPattern, &PatternBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeBranchNamePattern: + params := &PatternRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.BranchNamePattern = append(r.BranchNamePattern, &PatternBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeTagNamePattern: + params := &PatternRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.TagNamePattern = append(r.TagNamePattern, &PatternBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeFilePathRestriction: + params := &FilePathRestrictionRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.FilePathRestriction = append(r.FilePathRestriction, &FilePathRestrictionBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeMaxFilePathLength: + params := &MaxFilePathLengthRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.MaxFilePathLength = append(r.MaxFilePathLength, &MaxFilePathLengthBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeFileExtensionRestriction: + params := &FileExtensionRestrictionRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.FileExtensionRestriction = append(r.FileExtensionRestriction, &FileExtensionRestrictionBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeMaxFileSize: + params := &MaxFileSizeRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.MaxFileSize = append(r.MaxFileSize, &MaxFileSizeBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeWorkflows: + params := &WorkflowsRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.Workflows = append(r.Workflows, &WorkflowsBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + case RulesetRuleTypeCodeScanning: + params := &CodeScanningRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, params); err != nil { + return err + } + } + + r.CodeScanning = append(r.CodeScanning, &CodeScanningBranchRule{BranchRuleMetadata: w.BranchRuleMetadata, Parameters: *params}) + } + } + + return nil +} + +// UnmarshalJSON is a custom JSON unmarshaler for RulesetRule. +func (r *RepositoryRule) UnmarshalJSON(data []byte) error { + w := repositoryRulesetRuleWrapper{} + + if err := json.Unmarshal(data, &w); err != nil { + return err + } + + r.Type = w.Type + + switch r.Type { + case RulesetRuleTypeCreation: + r.Parameters = nil + case RulesetRuleTypeUpdate: + p := &UpdateRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + + r.Parameters = p + case RulesetRuleTypeDeletion: + r.Parameters = nil + case RulesetRuleTypeRequiredLinearHistory: + r.Parameters = nil + case RulesetRuleTypeMergeQueue: + p := &MergeQueueRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + + r.Parameters = p + case RulesetRuleTypeRequiredDeployments: + p := &RequiredDeploymentsRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + + r.Parameters = p + case RulesetRuleTypeRequiredSignatures: + r.Parameters = nil + case RulesetRuleTypePullRequest: + p := &PullRequestRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + + r.Parameters = p + case RulesetRuleTypeRequiredStatusChecks: + p := &RequiredStatusChecksRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + + r.Parameters = p + case RulesetRuleTypeNonFastForward: + r.Parameters = nil + case RulesetRuleTypeCommitMessagePattern: + p := &PatternRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + + r.Parameters = p + case RulesetRuleTypeCommitAuthorEmailPattern: + p := &PatternRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + + r.Parameters = p + case RulesetRuleTypeCommitterEmailPattern: + p := &PatternRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + + r.Parameters = p + case RulesetRuleTypeBranchNamePattern: + p := &PatternRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + + r.Parameters = p + case RulesetRuleTypeTagNamePattern: + p := &PatternRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + + r.Parameters = p + case RulesetRuleTypeFilePathRestriction: + p := &FilePathRestrictionRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + + r.Parameters = p + case RulesetRuleTypeMaxFilePathLength: + p := &MaxFilePathLengthRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + + r.Parameters = p + case RulesetRuleTypeFileExtensionRestriction: + p := &FileExtensionRestrictionRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + + r.Parameters = p + case RulesetRuleTypeMaxFileSize: + p := &MaxFileSizeRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + + r.Parameters = p + case RulesetRuleTypeWorkflows: + p := &WorkflowsRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + + r.Parameters = p + case RulesetRuleTypeCodeScanning: + p := &CodeScanningRuleParameters{} + + if w.Parameters != nil { + if err := json.Unmarshal(w.Parameters, p); err != nil { + return err + } + } + + r.Parameters = p + } + + return nil +} diff --git a/vendor/github.com/google/go-github/v68/github/scim.go b/vendor/github.com/google/go-github/v70/github/scim.go similarity index 78% rename from vendor/github.com/google/go-github/v68/github/scim.go rename to vendor/github.com/google/go-github/v70/github/scim.go index 4b34c1663c..70f063caa9 100644 --- a/vendor/github.com/google/go-github/v68/github/scim.go +++ b/vendor/github.com/google/go-github/v70/github/scim.go @@ -17,6 +17,26 @@ import ( // GitHub API docs: https://docs.github.com/rest/scim type SCIMService service +// SCIMGroupAttributes represents supported SCIM Group attributes. +// +// GitHub API docs: https://docs.github.com/en/enterprise-cloud@latest/rest/enterprise-admin/scim#list-provisioned-scim-groups-for-an-enterprise +type SCIMGroupAttributes struct { + DisplayName *string `json:"displayName,omitempty"` // The name of the group, suitable for display to end-users. (Optional.) + Members []*SCIMDisplayReference `json:"members,omitempty"` // (Optional.) + Schemas []string `json:"schemas,omitempty"` // (Optional.) + ExternalID *string `json:"externalId,omitempty"` // (Optional.) + // Only populated as a result of calling ListSCIMProvisionedIdentitiesOptions: + ID *string `json:"id,omitempty"` + Meta *SCIMMeta `json:"meta,omitempty"` +} + +// SCIMDisplayReference represents a JSON SCIM (System for Cross-domain Identity Management) resource. +type SCIMDisplayReference struct { + Value string `json:"value"` // (Required.) + Ref string `json:"$ref"` // (Required.) + Display *string `json:"display,omitempty"` // (Optional.) +} + // SCIMUserAttributes represents supported SCIM User attributes. // // GitHub API docs: https://docs.github.com/rest/scim#supported-scim-user-attributes @@ -56,6 +76,15 @@ type SCIMMeta struct { Location *string `json:"location,omitempty"` } +// SCIMProvisionedGroups represents the result of calling ListSCIMProvisionedGroupsForEnterprise. +type SCIMProvisionedGroups struct { + Schemas []string `json:"schemas,omitempty"` + TotalResults *int `json:"totalResults,omitempty"` + ItemsPerPage *int `json:"itemsPerPage,omitempty"` + StartIndex *int `json:"startIndex,omitempty"` + Resources []*SCIMGroupAttributes `json:"Resources,omitempty"` +} + // SCIMProvisionedIdentities represents the result of calling ListSCIMProvisionedIdentities. type SCIMProvisionedIdentities struct { Schemas []string `json:"schemas,omitempty"` @@ -217,3 +246,25 @@ func (s *SCIMService) DeleteSCIMUserFromOrg(ctx context.Context, org, scimUserID return s.client.Do(ctx, req, nil) } + +// ListSCIMProvisionedGroupsForEnterprise lists SCIM provisioned groups for an enterprise. +// +// GitHub API docs: https://docs.github.com/enterprise-cloud@latest/rest/enterprise-admin/scim#list-provisioned-scim-groups-for-an-enterprise +// +//meta:operation GET /scim/v2/enterprises/{enterprise}/Groups +func (s *SCIMService) ListSCIMProvisionedGroupsForEnterprise(ctx context.Context, enterprise string, opts *ListSCIMProvisionedIdentitiesOptions) (*SCIMProvisionedGroups, *Response, error) { + u := fmt.Sprintf("scim/v2/enterprises/%v/Groups", enterprise) + + req, err := s.client.NewRequest("GET", u, nil) + if err != nil { + return nil, nil, err + } + + groups := new(SCIMProvisionedGroups) + resp, err := s.client.Do(ctx, req, groups) + if err != nil { + return nil, resp, err + } + + return groups, resp, nil +} diff --git a/vendor/github.com/google/go-github/v68/github/search.go b/vendor/github.com/google/go-github/v70/github/search.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/search.go rename to vendor/github.com/google/go-github/v70/github/search.go diff --git a/vendor/github.com/google/go-github/v68/github/secret_scanning.go b/vendor/github.com/google/go-github/v70/github/secret_scanning.go similarity index 95% rename from vendor/github.com/google/go-github/v68/github/secret_scanning.go rename to vendor/github.com/google/go-github/v70/github/secret_scanning.go index fc0fe0cd8c..4eeeba3df7 100644 --- a/vendor/github.com/google/go-github/v68/github/secret_scanning.go +++ b/vendor/github.com/google/go-github/v70/github/secret_scanning.go @@ -68,6 +68,16 @@ type SecretScanningAlertListOptions struct { // Valid resolutions are false_positive, wont_fix, revoked, pattern_edited, pattern_deleted or used_in_tests. Resolution string `url:"resolution,omitempty"` + // A comma-separated list of validities that, when present, will return alerts that match the validities in this list. + // Valid options are active, inactive, and unknown. + Validity string `url:"validity,omitempty"` + + // The direction to sort the results by. Possible values are: asc, desc. Default: desc. + Direction string `url:"direction,omitempty"` + + // The property by which to sort the results. Possible values are: created, updated. Default: created. + Sort string `url:"sort,omitempty"` + ListCursorOptions // List options can vary on the Enterprise type. diff --git a/vendor/github.com/google/go-github/v68/github/security_advisories.go b/vendor/github.com/google/go-github/v70/github/security_advisories.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/security_advisories.go rename to vendor/github.com/google/go-github/v70/github/security_advisories.go diff --git a/vendor/github.com/google/go-github/v68/github/strings.go b/vendor/github.com/google/go-github/v70/github/strings.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/strings.go rename to vendor/github.com/google/go-github/v70/github/strings.go diff --git a/vendor/github.com/google/go-github/v68/github/teams.go b/vendor/github.com/google/go-github/v70/github/teams.go similarity index 99% rename from vendor/github.com/google/go-github/v68/github/teams.go rename to vendor/github.com/google/go-github/v70/github/teams.go index 579e5b828b..d334110f4a 100644 --- a/vendor/github.com/google/go-github/v68/github/teams.go +++ b/vendor/github.com/google/go-github/v70/github/teams.go @@ -37,6 +37,9 @@ type Team struct { // Default is "secret". Privacy *string `json:"privacy,omitempty"` + // NotificationSetting can be one of: "notifications_enabled", "notifications_disabled". + NotificationSetting *string `json:"notification_setting,omitempty"` + MembersCount *int `json:"members_count,omitempty"` ReposCount *int `json:"repos_count,omitempty"` Organization *Organization `json:"organization,omitempty"` diff --git a/vendor/github.com/google/go-github/v68/github/teams_discussion_comments.go b/vendor/github.com/google/go-github/v70/github/teams_discussion_comments.go similarity index 99% rename from vendor/github.com/google/go-github/v68/github/teams_discussion_comments.go rename to vendor/github.com/google/go-github/v70/github/teams_discussion_comments.go index eba6fdf46a..70bcbbc95b 100644 --- a/vendor/github.com/google/go-github/v68/github/teams_discussion_comments.go +++ b/vendor/github.com/google/go-github/v70/github/teams_discussion_comments.go @@ -167,8 +167,8 @@ func (s *TeamsService) CreateCommentByID(ctx context.Context, orgID, teamID int6 // GitHub API docs: https://docs.github.com/rest/teams/discussion-comments#create-a-discussion-comment // //meta:operation POST /orgs/{org}/teams/{team_slug}/discussions/{discussion_number}/comments -func (s *TeamsService) CreateCommentBySlug(ctx context.Context, org, slug string, discsusionNumber int, comment DiscussionComment) (*DiscussionComment, *Response, error) { - u := fmt.Sprintf("orgs/%v/teams/%v/discussions/%v/comments", org, slug, discsusionNumber) +func (s *TeamsService) CreateCommentBySlug(ctx context.Context, org, slug string, discussionNumber int, comment DiscussionComment) (*DiscussionComment, *Response, error) { + u := fmt.Sprintf("orgs/%v/teams/%v/discussions/%v/comments", org, slug, discussionNumber) req, err := s.client.NewRequest("POST", u, comment) if err != nil { return nil, nil, err diff --git a/vendor/github.com/google/go-github/v68/github/teams_discussions.go b/vendor/github.com/google/go-github/v70/github/teams_discussions.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/teams_discussions.go rename to vendor/github.com/google/go-github/v70/github/teams_discussions.go diff --git a/vendor/github.com/google/go-github/v68/github/teams_members.go b/vendor/github.com/google/go-github/v70/github/teams_members.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/teams_members.go rename to vendor/github.com/google/go-github/v70/github/teams_members.go diff --git a/vendor/github.com/google/go-github/v68/github/timestamp.go b/vendor/github.com/google/go-github/v70/github/timestamp.go similarity index 94% rename from vendor/github.com/google/go-github/v68/github/timestamp.go rename to vendor/github.com/google/go-github/v70/github/timestamp.go index dc1045cf74..71660193bb 100644 --- a/vendor/github.com/google/go-github/v68/github/timestamp.go +++ b/vendor/github.com/google/go-github/v70/github/timestamp.go @@ -10,7 +10,7 @@ import ( "time" ) -// Timestamp represents a time that can be unmarshalled from a JSON string +// Timestamp represents a time that can be unmarshaled from a JSON string // formatted as either an RFC3339 or Unix timestamp. This is necessary for some // fields since the GitHub API is inconsistent in how it represents times. All // exported methods of time.Time can be called on Timestamp. diff --git a/vendor/github.com/google/go-github/v68/github/users.go b/vendor/github.com/google/go-github/v70/github/users.go similarity index 99% rename from vendor/github.com/google/go-github/v68/github/users.go rename to vendor/github.com/google/go-github/v70/github/users.go index 60f1e06a69..28db59c5e1 100644 --- a/vendor/github.com/google/go-github/v68/github/users.go +++ b/vendor/github.com/google/go-github/v70/github/users.go @@ -77,7 +77,7 @@ type User struct { Assignment *string `json:"assignment,omitempty"` // InheritedFrom identifies the team that a user inherited their organization role // from. This is only populated when calling the ListUsersAssignedToOrgRole method. - InheritedFrom *Team `json:"inherited_from,omitempty"` + InheritedFrom []*Team `json:"inherited_from,omitempty"` } func (u User) String() string { diff --git a/vendor/github.com/google/go-github/v68/github/users_administration.go b/vendor/github.com/google/go-github/v70/github/users_administration.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/users_administration.go rename to vendor/github.com/google/go-github/v70/github/users_administration.go diff --git a/vendor/github.com/google/go-github/v68/github/users_attestations.go b/vendor/github.com/google/go-github/v70/github/users_attestations.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/users_attestations.go rename to vendor/github.com/google/go-github/v70/github/users_attestations.go diff --git a/vendor/github.com/google/go-github/v68/github/users_blocking.go b/vendor/github.com/google/go-github/v70/github/users_blocking.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/users_blocking.go rename to vendor/github.com/google/go-github/v70/github/users_blocking.go diff --git a/vendor/github.com/google/go-github/v68/github/users_emails.go b/vendor/github.com/google/go-github/v70/github/users_emails.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/users_emails.go rename to vendor/github.com/google/go-github/v70/github/users_emails.go diff --git a/vendor/github.com/google/go-github/v68/github/users_followers.go b/vendor/github.com/google/go-github/v70/github/users_followers.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/users_followers.go rename to vendor/github.com/google/go-github/v70/github/users_followers.go diff --git a/vendor/github.com/google/go-github/v68/github/users_gpg_keys.go b/vendor/github.com/google/go-github/v70/github/users_gpg_keys.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/users_gpg_keys.go rename to vendor/github.com/google/go-github/v70/github/users_gpg_keys.go diff --git a/vendor/github.com/google/go-github/v68/github/users_keys.go b/vendor/github.com/google/go-github/v70/github/users_keys.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/users_keys.go rename to vendor/github.com/google/go-github/v70/github/users_keys.go diff --git a/vendor/github.com/google/go-github/v68/github/users_packages.go b/vendor/github.com/google/go-github/v70/github/users_packages.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/users_packages.go rename to vendor/github.com/google/go-github/v70/github/users_packages.go diff --git a/vendor/github.com/google/go-github/v68/github/users_ssh_signing_keys.go b/vendor/github.com/google/go-github/v70/github/users_ssh_signing_keys.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/users_ssh_signing_keys.go rename to vendor/github.com/google/go-github/v70/github/users_ssh_signing_keys.go diff --git a/vendor/github.com/google/go-github/v68/github/with_appengine.go b/vendor/github.com/google/go-github/v70/github/with_appengine.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/with_appengine.go rename to vendor/github.com/google/go-github/v70/github/with_appengine.go diff --git a/vendor/github.com/google/go-github/v68/github/without_appengine.go b/vendor/github.com/google/go-github/v70/github/without_appengine.go similarity index 100% rename from vendor/github.com/google/go-github/v68/github/without_appengine.go rename to vendor/github.com/google/go-github/v70/github/without_appengine.go diff --git a/vendor/github.com/hashicorp/hcl/.gitignore b/vendor/github.com/hashicorp/hcl/.gitignore deleted file mode 100644 index 15586a2b54..0000000000 --- a/vendor/github.com/hashicorp/hcl/.gitignore +++ /dev/null @@ -1,9 +0,0 @@ -y.output - -# ignore intellij files -.idea -*.iml -*.ipr -*.iws - -*.test diff --git a/vendor/github.com/hashicorp/hcl/.travis.yml b/vendor/github.com/hashicorp/hcl/.travis.yml deleted file mode 100644 index cb63a32161..0000000000 --- a/vendor/github.com/hashicorp/hcl/.travis.yml +++ /dev/null @@ -1,13 +0,0 @@ -sudo: false - -language: go - -go: - - 1.x - - tip - -branches: - only: - - master - -script: make test diff --git a/vendor/github.com/hashicorp/hcl/LICENSE b/vendor/github.com/hashicorp/hcl/LICENSE deleted file mode 100644 index c33dcc7c92..0000000000 --- a/vendor/github.com/hashicorp/hcl/LICENSE +++ /dev/null @@ -1,354 +0,0 @@ -Mozilla Public License, version 2.0 - -1. Definitions - -1.1. “Contributor” - - means each individual or legal entity that creates, contributes to the - creation of, or owns Covered Software. - -1.2. “Contributor Version” - - means the combination of the Contributions of others (if any) used by a - Contributor and that particular Contributor’s Contribution. - -1.3. “Contribution” - - means Covered Software of a particular Contributor. - -1.4. “Covered Software” - - means Source Code Form to which the initial Contributor has attached the - notice in Exhibit A, the Executable Form of such Source Code Form, and - Modifications of such Source Code Form, in each case including portions - thereof. - -1.5. “Incompatible With Secondary Licenses” - means - - a. that the initial Contributor has attached the notice described in - Exhibit B to the Covered Software; or - - b. that the Covered Software was made available under the terms of version - 1.1 or earlier of the License, but not also under the terms of a - Secondary License. - -1.6. “Executable Form” - - means any form of the work other than Source Code Form. - -1.7. “Larger Work” - - means a work that combines Covered Software with other material, in a separate - file or files, that is not Covered Software. - -1.8. “License” - - means this document. - -1.9. “Licensable” - - means having the right to grant, to the maximum extent possible, whether at the - time of the initial grant or subsequently, any and all of the rights conveyed by - this License. - -1.10. “Modifications” - - means any of the following: - - a. any file in Source Code Form that results from an addition to, deletion - from, or modification of the contents of Covered Software; or - - b. any new file in Source Code Form that contains any Covered Software. - -1.11. “Patent Claims” of a Contributor - - means any patent claim(s), including without limitation, method, process, - and apparatus claims, in any patent Licensable by such Contributor that - would be infringed, but for the grant of the License, by the making, - using, selling, offering for sale, having made, import, or transfer of - either its Contributions or its Contributor Version. - -1.12. “Secondary License” - - means either the GNU General Public License, Version 2.0, the GNU Lesser - General Public License, Version 2.1, the GNU Affero General Public - License, Version 3.0, or any later versions of those licenses. - -1.13. “Source Code Form” - - means the form of the work preferred for making modifications. - -1.14. “You” (or “Your”) - - means an individual or a legal entity exercising rights under this - License. For legal entities, “You” includes any entity that controls, is - controlled by, or is under common control with You. For purposes of this - definition, “control” means (a) the power, direct or indirect, to cause - the direction or management of such entity, whether by contract or - otherwise, or (b) ownership of more than fifty percent (50%) of the - outstanding shares or beneficial ownership of such entity. - - -2. License Grants and Conditions - -2.1. Grants - - Each Contributor hereby grants You a world-wide, royalty-free, - non-exclusive license: - - a. under intellectual property rights (other than patent or trademark) - Licensable by such Contributor to use, reproduce, make available, - modify, display, perform, distribute, and otherwise exploit its - Contributions, either on an unmodified basis, with Modifications, or as - part of a Larger Work; and - - b. under Patent Claims of such Contributor to make, use, sell, offer for - sale, have made, import, and otherwise transfer either its Contributions - or its Contributor Version. - -2.2. Effective Date - - The licenses granted in Section 2.1 with respect to any Contribution become - effective for each Contribution on the date the Contributor first distributes - such Contribution. - -2.3. Limitations on Grant Scope - - The licenses granted in this Section 2 are the only rights granted under this - License. No additional rights or licenses will be implied from the distribution - or licensing of Covered Software under this License. Notwithstanding Section - 2.1(b) above, no patent license is granted by a Contributor: - - a. for any code that a Contributor has removed from Covered Software; or - - b. for infringements caused by: (i) Your and any other third party’s - modifications of Covered Software, or (ii) the combination of its - Contributions with other software (except as part of its Contributor - Version); or - - c. under Patent Claims infringed by Covered Software in the absence of its - Contributions. - - This License does not grant any rights in the trademarks, service marks, or - logos of any Contributor (except as may be necessary to comply with the - notice requirements in Section 3.4). - -2.4. Subsequent Licenses - - No Contributor makes additional grants as a result of Your choice to - distribute the Covered Software under a subsequent version of this License - (see Section 10.2) or under the terms of a Secondary License (if permitted - under the terms of Section 3.3). - -2.5. Representation - - Each Contributor represents that the Contributor believes its Contributions - are its original creation(s) or it has sufficient rights to grant the - rights to its Contributions conveyed by this License. - -2.6. Fair Use - - This License is not intended to limit any rights You have under applicable - copyright doctrines of fair use, fair dealing, or other equivalents. - -2.7. Conditions - - Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in - Section 2.1. - - -3. Responsibilities - -3.1. Distribution of Source Form - - All distribution of Covered Software in Source Code Form, including any - Modifications that You create or to which You contribute, must be under the - terms of this License. You must inform recipients that the Source Code Form - of the Covered Software is governed by the terms of this License, and how - they can obtain a copy of this License. You may not attempt to alter or - restrict the recipients’ rights in the Source Code Form. - -3.2. Distribution of Executable Form - - If You distribute Covered Software in Executable Form then: - - a. such Covered Software must also be made available in Source Code Form, - as described in Section 3.1, and You must inform recipients of the - Executable Form how they can obtain a copy of such Source Code Form by - reasonable means in a timely manner, at a charge no more than the cost - of distribution to the recipient; and - - b. You may distribute such Executable Form under the terms of this License, - or sublicense it under different terms, provided that the license for - the Executable Form does not attempt to limit or alter the recipients’ - rights in the Source Code Form under this License. - -3.3. Distribution of a Larger Work - - You may create and distribute a Larger Work under terms of Your choice, - provided that You also comply with the requirements of this License for the - Covered Software. If the Larger Work is a combination of Covered Software - with a work governed by one or more Secondary Licenses, and the Covered - Software is not Incompatible With Secondary Licenses, this License permits - You to additionally distribute such Covered Software under the terms of - such Secondary License(s), so that the recipient of the Larger Work may, at - their option, further distribute the Covered Software under the terms of - either this License or such Secondary License(s). - -3.4. Notices - - You may not remove or alter the substance of any license notices (including - copyright notices, patent notices, disclaimers of warranty, or limitations - of liability) contained within the Source Code Form of the Covered - Software, except that You may alter any license notices to the extent - required to remedy known factual inaccuracies. - -3.5. Application of Additional Terms - - You may choose to offer, and to charge a fee for, warranty, support, - indemnity or liability obligations to one or more recipients of Covered - Software. However, You may do so only on Your own behalf, and not on behalf - of any Contributor. You must make it absolutely clear that any such - warranty, support, indemnity, or liability obligation is offered by You - alone, and You hereby agree to indemnify every Contributor for any - liability incurred by such Contributor as a result of warranty, support, - indemnity or liability terms You offer. You may include additional - disclaimers of warranty and limitations of liability specific to any - jurisdiction. - -4. Inability to Comply Due to Statute or Regulation - - If it is impossible for You to comply with any of the terms of this License - with respect to some or all of the Covered Software due to statute, judicial - order, or regulation then You must: (a) comply with the terms of this License - to the maximum extent possible; and (b) describe the limitations and the code - they affect. Such description must be placed in a text file included with all - distributions of the Covered Software under this License. Except to the - extent prohibited by statute or regulation, such description must be - sufficiently detailed for a recipient of ordinary skill to be able to - understand it. - -5. Termination - -5.1. The rights granted under this License will terminate automatically if You - fail to comply with any of its terms. However, if You become compliant, - then the rights granted under this License from a particular Contributor - are reinstated (a) provisionally, unless and until such Contributor - explicitly and finally terminates Your grants, and (b) on an ongoing basis, - if such Contributor fails to notify You of the non-compliance by some - reasonable means prior to 60 days after You have come back into compliance. - Moreover, Your grants from a particular Contributor are reinstated on an - ongoing basis if such Contributor notifies You of the non-compliance by - some reasonable means, this is the first time You have received notice of - non-compliance with this License from such Contributor, and You become - compliant prior to 30 days after Your receipt of the notice. - -5.2. If You initiate litigation against any entity by asserting a patent - infringement claim (excluding declaratory judgment actions, counter-claims, - and cross-claims) alleging that a Contributor Version directly or - indirectly infringes any patent, then the rights granted to You by any and - all Contributors for the Covered Software under Section 2.1 of this License - shall terminate. - -5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user - license agreements (excluding distributors and resellers) which have been - validly granted by You or Your distributors under this License prior to - termination shall survive termination. - -6. Disclaimer of Warranty - - Covered Software is provided under this License on an “as is” basis, without - warranty of any kind, either expressed, implied, or statutory, including, - without limitation, warranties that the Covered Software is free of defects, - merchantable, fit for a particular purpose or non-infringing. The entire - risk as to the quality and performance of the Covered Software is with You. - Should any Covered Software prove defective in any respect, You (not any - Contributor) assume the cost of any necessary servicing, repair, or - correction. This disclaimer of warranty constitutes an essential part of this - License. No use of any Covered Software is authorized under this License - except under this disclaimer. - -7. Limitation of Liability - - Under no circumstances and under no legal theory, whether tort (including - negligence), contract, or otherwise, shall any Contributor, or anyone who - distributes Covered Software as permitted above, be liable to You for any - direct, indirect, special, incidental, or consequential damages of any - character including, without limitation, damages for lost profits, loss of - goodwill, work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses, even if such party shall have been - informed of the possibility of such damages. This limitation of liability - shall not apply to liability for death or personal injury resulting from such - party’s negligence to the extent applicable law prohibits such limitation. - Some jurisdictions do not allow the exclusion or limitation of incidental or - consequential damages, so this exclusion and limitation may not apply to You. - -8. Litigation - - Any litigation relating to this License may be brought only in the courts of - a jurisdiction where the defendant maintains its principal place of business - and such litigation shall be governed by laws of that jurisdiction, without - reference to its conflict-of-law provisions. Nothing in this Section shall - prevent a party’s ability to bring cross-claims or counter-claims. - -9. Miscellaneous - - This License represents the complete agreement concerning the subject matter - hereof. If any provision of this License is held to be unenforceable, such - provision shall be reformed only to the extent necessary to make it - enforceable. Any law or regulation which provides that the language of a - contract shall be construed against the drafter shall not be used to construe - this License against a Contributor. - - -10. Versions of the License - -10.1. New Versions - - Mozilla Foundation is the license steward. Except as provided in Section - 10.3, no one other than the license steward has the right to modify or - publish new versions of this License. Each version will be given a - distinguishing version number. - -10.2. Effect of New Versions - - You may distribute the Covered Software under the terms of the version of - the License under which You originally received the Covered Software, or - under the terms of any subsequent version published by the license - steward. - -10.3. Modified Versions - - If you create software not governed by this License, and you want to - create a new license for such software, you may create and use a modified - version of this License if you rename the license and remove any - references to the name of the license steward (except to note that such - modified license differs from this License). - -10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses - If You choose to distribute Source Code Form that is Incompatible With - Secondary Licenses under the terms of this version of the License, the - notice described in Exhibit B of this License must be attached. - -Exhibit A - Source Code Form License Notice - - This Source Code Form is subject to the - terms of the Mozilla Public License, v. - 2.0. If a copy of the MPL was not - distributed with this file, You can - obtain one at - http://mozilla.org/MPL/2.0/. - -If it is not possible or desirable to put the notice in a particular file, then -You may include the notice in a location (such as a LICENSE file in a relevant -directory) where a recipient would be likely to look for such a notice. - -You may add additional accurate notices of copyright ownership. - -Exhibit B - “Incompatible With Secondary Licenses” Notice - - This Source Code Form is “Incompatible - With Secondary Licenses”, as defined by - the Mozilla Public License, v. 2.0. - diff --git a/vendor/github.com/hashicorp/hcl/Makefile b/vendor/github.com/hashicorp/hcl/Makefile deleted file mode 100644 index 84fd743f5c..0000000000 --- a/vendor/github.com/hashicorp/hcl/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -TEST?=./... - -default: test - -fmt: generate - go fmt ./... - -test: generate - go get -t ./... - go test $(TEST) $(TESTARGS) - -generate: - go generate ./... - -updatedeps: - go get -u golang.org/x/tools/cmd/stringer - -.PHONY: default generate test updatedeps diff --git a/vendor/github.com/hashicorp/hcl/README.md b/vendor/github.com/hashicorp/hcl/README.md deleted file mode 100644 index c8223326dd..0000000000 --- a/vendor/github.com/hashicorp/hcl/README.md +++ /dev/null @@ -1,125 +0,0 @@ -# HCL - -[![GoDoc](https://godoc.org/github.com/hashicorp/hcl?status.png)](https://godoc.org/github.com/hashicorp/hcl) [![Build Status](https://travis-ci.org/hashicorp/hcl.svg?branch=master)](https://travis-ci.org/hashicorp/hcl) - -HCL (HashiCorp Configuration Language) is a configuration language built -by HashiCorp. The goal of HCL is to build a structured configuration language -that is both human and machine friendly for use with command-line tools, but -specifically targeted towards DevOps tools, servers, etc. - -HCL is also fully JSON compatible. That is, JSON can be used as completely -valid input to a system expecting HCL. This helps makes systems -interoperable with other systems. - -HCL is heavily inspired by -[libucl](https://github.com/vstakhov/libucl), -nginx configuration, and others similar. - -## Why? - -A common question when viewing HCL is to ask the question: why not -JSON, YAML, etc.? - -Prior to HCL, the tools we built at [HashiCorp](http://www.hashicorp.com) -used a variety of configuration languages from full programming languages -such as Ruby to complete data structure languages such as JSON. What we -learned is that some people wanted human-friendly configuration languages -and some people wanted machine-friendly languages. - -JSON fits a nice balance in this, but is fairly verbose and most -importantly doesn't support comments. With YAML, we found that beginners -had a really hard time determining what the actual structure was, and -ended up guessing more often than not whether to use a hyphen, colon, etc. -in order to represent some configuration key. - -Full programming languages such as Ruby enable complex behavior -a configuration language shouldn't usually allow, and also forces -people to learn some set of Ruby. - -Because of this, we decided to create our own configuration language -that is JSON-compatible. Our configuration language (HCL) is designed -to be written and modified by humans. The API for HCL allows JSON -as an input so that it is also machine-friendly (machines can generate -JSON instead of trying to generate HCL). - -Our goal with HCL is not to alienate other configuration languages. -It is instead to provide HCL as a specialized language for our tools, -and JSON as the interoperability layer. - -## Syntax - -For a complete grammar, please see the parser itself. A high-level overview -of the syntax and grammar is listed here. - - * Single line comments start with `#` or `//` - - * Multi-line comments are wrapped in `/*` and `*/`. Nested block comments - are not allowed. A multi-line comment (also known as a block comment) - terminates at the first `*/` found. - - * Values are assigned with the syntax `key = value` (whitespace doesn't - matter). The value can be any primitive: a string, number, boolean, - object, or list. - - * Strings are double-quoted and can contain any UTF-8 characters. - Example: `"Hello, World"` - - * Multi-line strings start with `<- - echo %Path% - - go version - - go env - - go get -t ./... - -build_script: -- cmd: go test -v ./... diff --git a/vendor/github.com/hashicorp/hcl/decoder.go b/vendor/github.com/hashicorp/hcl/decoder.go deleted file mode 100644 index d9a00f21d4..0000000000 --- a/vendor/github.com/hashicorp/hcl/decoder.go +++ /dev/null @@ -1,769 +0,0 @@ -package hcl - -import ( - "errors" - "fmt" - "reflect" - "sort" - "strconv" - "strings" - - "github.com/hashicorp/hcl/hcl/ast" - "github.com/hashicorp/hcl/hcl/parser" - "github.com/hashicorp/hcl/hcl/token" -) - -// This is the tag to use with structures to have settings for HCL -const tagName = "hcl" - -var ( - // nodeType holds a reference to the type of ast.Node - nodeType reflect.Type = findNodeType() -) - -// Unmarshal accepts a byte slice as input and writes the -// data to the value pointed to by v. -func Unmarshal(bs []byte, v interface{}) error { - root, err := parse(bs) - if err != nil { - return err - } - - return DecodeObject(v, root) -} - -// Decode reads the given input and decodes it into the structure -// given by `out`. -func Decode(out interface{}, in string) error { - obj, err := Parse(in) - if err != nil { - return err - } - - return DecodeObject(out, obj) -} - -// DecodeObject is a lower-level version of Decode. It decodes a -// raw Object into the given output. -func DecodeObject(out interface{}, n ast.Node) error { - val := reflect.ValueOf(out) - if val.Kind() != reflect.Ptr { - return errors.New("result must be a pointer") - } - - // If we have the file, we really decode the root node - if f, ok := n.(*ast.File); ok { - n = f.Node - } - - var d decoder - return d.decode("root", n, val.Elem()) -} - -type decoder struct { - stack []reflect.Kind -} - -func (d *decoder) decode(name string, node ast.Node, result reflect.Value) error { - k := result - - // If we have an interface with a valid value, we use that - // for the check. - if result.Kind() == reflect.Interface { - elem := result.Elem() - if elem.IsValid() { - k = elem - } - } - - // Push current onto stack unless it is an interface. - if k.Kind() != reflect.Interface { - d.stack = append(d.stack, k.Kind()) - - // Schedule a pop - defer func() { - d.stack = d.stack[:len(d.stack)-1] - }() - } - - switch k.Kind() { - case reflect.Bool: - return d.decodeBool(name, node, result) - case reflect.Float32, reflect.Float64: - return d.decodeFloat(name, node, result) - case reflect.Int, reflect.Int32, reflect.Int64: - return d.decodeInt(name, node, result) - case reflect.Interface: - // When we see an interface, we make our own thing - return d.decodeInterface(name, node, result) - case reflect.Map: - return d.decodeMap(name, node, result) - case reflect.Ptr: - return d.decodePtr(name, node, result) - case reflect.Slice: - return d.decodeSlice(name, node, result) - case reflect.String: - return d.decodeString(name, node, result) - case reflect.Struct: - return d.decodeStruct(name, node, result) - default: - return &parser.PosError{ - Pos: node.Pos(), - Err: fmt.Errorf("%s: unknown kind to decode into: %s", name, k.Kind()), - } - } -} - -func (d *decoder) decodeBool(name string, node ast.Node, result reflect.Value) error { - switch n := node.(type) { - case *ast.LiteralType: - if n.Token.Type == token.BOOL { - v, err := strconv.ParseBool(n.Token.Text) - if err != nil { - return err - } - - result.Set(reflect.ValueOf(v)) - return nil - } - } - - return &parser.PosError{ - Pos: node.Pos(), - Err: fmt.Errorf("%s: unknown type %T", name, node), - } -} - -func (d *decoder) decodeFloat(name string, node ast.Node, result reflect.Value) error { - switch n := node.(type) { - case *ast.LiteralType: - if n.Token.Type == token.FLOAT || n.Token.Type == token.NUMBER { - v, err := strconv.ParseFloat(n.Token.Text, 64) - if err != nil { - return err - } - - result.Set(reflect.ValueOf(v).Convert(result.Type())) - return nil - } - } - - return &parser.PosError{ - Pos: node.Pos(), - Err: fmt.Errorf("%s: unknown type %T", name, node), - } -} - -func (d *decoder) decodeInt(name string, node ast.Node, result reflect.Value) error { - switch n := node.(type) { - case *ast.LiteralType: - switch n.Token.Type { - case token.NUMBER: - v, err := strconv.ParseInt(n.Token.Text, 0, 0) - if err != nil { - return err - } - - if result.Kind() == reflect.Interface { - result.Set(reflect.ValueOf(int(v))) - } else { - result.SetInt(v) - } - return nil - case token.STRING: - v, err := strconv.ParseInt(n.Token.Value().(string), 0, 0) - if err != nil { - return err - } - - if result.Kind() == reflect.Interface { - result.Set(reflect.ValueOf(int(v))) - } else { - result.SetInt(v) - } - return nil - } - } - - return &parser.PosError{ - Pos: node.Pos(), - Err: fmt.Errorf("%s: unknown type %T", name, node), - } -} - -func (d *decoder) decodeInterface(name string, node ast.Node, result reflect.Value) error { - // When we see an ast.Node, we retain the value to enable deferred decoding. - // Very useful in situations where we want to preserve ast.Node information - // like Pos - if result.Type() == nodeType && result.CanSet() { - result.Set(reflect.ValueOf(node)) - return nil - } - - var set reflect.Value - redecode := true - - // For testing types, ObjectType should just be treated as a list. We - // set this to a temporary var because we want to pass in the real node. - testNode := node - if ot, ok := node.(*ast.ObjectType); ok { - testNode = ot.List - } - - switch n := testNode.(type) { - case *ast.ObjectList: - // If we're at the root or we're directly within a slice, then we - // decode objects into map[string]interface{}, otherwise we decode - // them into lists. - if len(d.stack) == 0 || d.stack[len(d.stack)-1] == reflect.Slice { - var temp map[string]interface{} - tempVal := reflect.ValueOf(temp) - result := reflect.MakeMap( - reflect.MapOf( - reflect.TypeOf(""), - tempVal.Type().Elem())) - - set = result - } else { - var temp []map[string]interface{} - tempVal := reflect.ValueOf(temp) - result := reflect.MakeSlice( - reflect.SliceOf(tempVal.Type().Elem()), 0, len(n.Items)) - set = result - } - case *ast.ObjectType: - // If we're at the root or we're directly within a slice, then we - // decode objects into map[string]interface{}, otherwise we decode - // them into lists. - if len(d.stack) == 0 || d.stack[len(d.stack)-1] == reflect.Slice { - var temp map[string]interface{} - tempVal := reflect.ValueOf(temp) - result := reflect.MakeMap( - reflect.MapOf( - reflect.TypeOf(""), - tempVal.Type().Elem())) - - set = result - } else { - var temp []map[string]interface{} - tempVal := reflect.ValueOf(temp) - result := reflect.MakeSlice( - reflect.SliceOf(tempVal.Type().Elem()), 0, 1) - set = result - } - case *ast.ListType: - var temp []interface{} - tempVal := reflect.ValueOf(temp) - result := reflect.MakeSlice( - reflect.SliceOf(tempVal.Type().Elem()), 0, 0) - set = result - case *ast.LiteralType: - switch n.Token.Type { - case token.BOOL: - var result bool - set = reflect.Indirect(reflect.New(reflect.TypeOf(result))) - case token.FLOAT: - var result float64 - set = reflect.Indirect(reflect.New(reflect.TypeOf(result))) - case token.NUMBER: - var result int - set = reflect.Indirect(reflect.New(reflect.TypeOf(result))) - case token.STRING, token.HEREDOC: - set = reflect.Indirect(reflect.New(reflect.TypeOf(""))) - default: - return &parser.PosError{ - Pos: node.Pos(), - Err: fmt.Errorf("%s: cannot decode into interface: %T", name, node), - } - } - default: - return fmt.Errorf( - "%s: cannot decode into interface: %T", - name, node) - } - - // Set the result to what its supposed to be, then reset - // result so we don't reflect into this method anymore. - result.Set(set) - - if redecode { - // Revisit the node so that we can use the newly instantiated - // thing and populate it. - if err := d.decode(name, node, result); err != nil { - return err - } - } - - return nil -} - -func (d *decoder) decodeMap(name string, node ast.Node, result reflect.Value) error { - if item, ok := node.(*ast.ObjectItem); ok { - node = &ast.ObjectList{Items: []*ast.ObjectItem{item}} - } - - if ot, ok := node.(*ast.ObjectType); ok { - node = ot.List - } - - n, ok := node.(*ast.ObjectList) - if !ok { - return &parser.PosError{ - Pos: node.Pos(), - Err: fmt.Errorf("%s: not an object type for map (%T)", name, node), - } - } - - // If we have an interface, then we can address the interface, - // but not the slice itself, so get the element but set the interface - set := result - if result.Kind() == reflect.Interface { - result = result.Elem() - } - - resultType := result.Type() - resultElemType := resultType.Elem() - resultKeyType := resultType.Key() - if resultKeyType.Kind() != reflect.String { - return &parser.PosError{ - Pos: node.Pos(), - Err: fmt.Errorf("%s: map must have string keys", name), - } - } - - // Make a map if it is nil - resultMap := result - if result.IsNil() { - resultMap = reflect.MakeMap( - reflect.MapOf(resultKeyType, resultElemType)) - } - - // Go through each element and decode it. - done := make(map[string]struct{}) - for _, item := range n.Items { - if item.Val == nil { - continue - } - - // github.com/hashicorp/terraform/issue/5740 - if len(item.Keys) == 0 { - return &parser.PosError{ - Pos: node.Pos(), - Err: fmt.Errorf("%s: map must have string keys", name), - } - } - - // Get the key we're dealing with, which is the first item - keyStr := item.Keys[0].Token.Value().(string) - - // If we've already processed this key, then ignore it - if _, ok := done[keyStr]; ok { - continue - } - - // Determine the value. If we have more than one key, then we - // get the objectlist of only these keys. - itemVal := item.Val - if len(item.Keys) > 1 { - itemVal = n.Filter(keyStr) - done[keyStr] = struct{}{} - } - - // Make the field name - fieldName := fmt.Sprintf("%s.%s", name, keyStr) - - // Get the key/value as reflection values - key := reflect.ValueOf(keyStr) - val := reflect.Indirect(reflect.New(resultElemType)) - - // If we have a pre-existing value in the map, use that - oldVal := resultMap.MapIndex(key) - if oldVal.IsValid() { - val.Set(oldVal) - } - - // Decode! - if err := d.decode(fieldName, itemVal, val); err != nil { - return err - } - - // Set the value on the map - resultMap.SetMapIndex(key, val) - } - - // Set the final map if we can - set.Set(resultMap) - return nil -} - -func (d *decoder) decodePtr(name string, node ast.Node, result reflect.Value) error { - // Create an element of the concrete (non pointer) type and decode - // into that. Then set the value of the pointer to this type. - resultType := result.Type() - resultElemType := resultType.Elem() - val := reflect.New(resultElemType) - if err := d.decode(name, node, reflect.Indirect(val)); err != nil { - return err - } - - result.Set(val) - return nil -} - -func (d *decoder) decodeSlice(name string, node ast.Node, result reflect.Value) error { - // If we have an interface, then we can address the interface, - // but not the slice itself, so get the element but set the interface - set := result - if result.Kind() == reflect.Interface { - result = result.Elem() - } - // Create the slice if it isn't nil - resultType := result.Type() - resultElemType := resultType.Elem() - if result.IsNil() { - resultSliceType := reflect.SliceOf(resultElemType) - result = reflect.MakeSlice( - resultSliceType, 0, 0) - } - - // Figure out the items we'll be copying into the slice - var items []ast.Node - switch n := node.(type) { - case *ast.ObjectList: - items = make([]ast.Node, len(n.Items)) - for i, item := range n.Items { - items[i] = item - } - case *ast.ObjectType: - items = []ast.Node{n} - case *ast.ListType: - items = n.List - default: - return &parser.PosError{ - Pos: node.Pos(), - Err: fmt.Errorf("unknown slice type: %T", node), - } - } - - for i, item := range items { - fieldName := fmt.Sprintf("%s[%d]", name, i) - - // Decode - val := reflect.Indirect(reflect.New(resultElemType)) - - // if item is an object that was decoded from ambiguous JSON and - // flattened, make sure it's expanded if it needs to decode into a - // defined structure. - item := expandObject(item, val) - - if err := d.decode(fieldName, item, val); err != nil { - return err - } - - // Append it onto the slice - result = reflect.Append(result, val) - } - - set.Set(result) - return nil -} - -// expandObject detects if an ambiguous JSON object was flattened to a List which -// should be decoded into a struct, and expands the ast to properly deocode. -func expandObject(node ast.Node, result reflect.Value) ast.Node { - item, ok := node.(*ast.ObjectItem) - if !ok { - return node - } - - elemType := result.Type() - - // our target type must be a struct - switch elemType.Kind() { - case reflect.Ptr: - switch elemType.Elem().Kind() { - case reflect.Struct: - //OK - default: - return node - } - case reflect.Struct: - //OK - default: - return node - } - - // A list value will have a key and field name. If it had more fields, - // it wouldn't have been flattened. - if len(item.Keys) != 2 { - return node - } - - keyToken := item.Keys[0].Token - item.Keys = item.Keys[1:] - - // we need to un-flatten the ast enough to decode - newNode := &ast.ObjectItem{ - Keys: []*ast.ObjectKey{ - { - Token: keyToken, - }, - }, - Val: &ast.ObjectType{ - List: &ast.ObjectList{ - Items: []*ast.ObjectItem{item}, - }, - }, - } - - return newNode -} - -func (d *decoder) decodeString(name string, node ast.Node, result reflect.Value) error { - switch n := node.(type) { - case *ast.LiteralType: - switch n.Token.Type { - case token.NUMBER: - result.Set(reflect.ValueOf(n.Token.Text).Convert(result.Type())) - return nil - case token.STRING, token.HEREDOC: - result.Set(reflect.ValueOf(n.Token.Value()).Convert(result.Type())) - return nil - } - } - - return &parser.PosError{ - Pos: node.Pos(), - Err: fmt.Errorf("%s: unknown type for string %T", name, node), - } -} - -func (d *decoder) decodeStruct(name string, node ast.Node, result reflect.Value) error { - var item *ast.ObjectItem - if it, ok := node.(*ast.ObjectItem); ok { - item = it - node = it.Val - } - - if ot, ok := node.(*ast.ObjectType); ok { - node = ot.List - } - - // Handle the special case where the object itself is a literal. Previously - // the yacc parser would always ensure top-level elements were arrays. The new - // parser does not make the same guarantees, thus we need to convert any - // top-level literal elements into a list. - if _, ok := node.(*ast.LiteralType); ok && item != nil { - node = &ast.ObjectList{Items: []*ast.ObjectItem{item}} - } - - list, ok := node.(*ast.ObjectList) - if !ok { - return &parser.PosError{ - Pos: node.Pos(), - Err: fmt.Errorf("%s: not an object type for struct (%T)", name, node), - } - } - - // This slice will keep track of all the structs we'll be decoding. - // There can be more than one struct if there are embedded structs - // that are squashed. - structs := make([]reflect.Value, 1, 5) - structs[0] = result - - // Compile the list of all the fields that we're going to be decoding - // from all the structs. - type field struct { - field reflect.StructField - val reflect.Value - } - fields := []field{} - for len(structs) > 0 { - structVal := structs[0] - structs = structs[1:] - - structType := structVal.Type() - for i := 0; i < structType.NumField(); i++ { - fieldType := structType.Field(i) - tagParts := strings.Split(fieldType.Tag.Get(tagName), ",") - - // Ignore fields with tag name "-" - if tagParts[0] == "-" { - continue - } - - if fieldType.Anonymous { - fieldKind := fieldType.Type.Kind() - if fieldKind != reflect.Struct { - return &parser.PosError{ - Pos: node.Pos(), - Err: fmt.Errorf("%s: unsupported type to struct: %s", - fieldType.Name, fieldKind), - } - } - - // We have an embedded field. We "squash" the fields down - // if specified in the tag. - squash := false - for _, tag := range tagParts[1:] { - if tag == "squash" { - squash = true - break - } - } - - if squash { - structs = append( - structs, result.FieldByName(fieldType.Name)) - continue - } - } - - // Normal struct field, store it away - fields = append(fields, field{fieldType, structVal.Field(i)}) - } - } - - usedKeys := make(map[string]struct{}) - decodedFields := make([]string, 0, len(fields)) - decodedFieldsVal := make([]reflect.Value, 0) - unusedKeysVal := make([]reflect.Value, 0) - - // fill unusedNodeKeys with keys from the AST - // a slice because we have to do equals case fold to match Filter - unusedNodeKeys := make(map[string][]token.Pos, 0) - for i, item := range list.Items { - for _, k := range item.Keys { - // isNestedJSON returns true for e.g. bar in - // { "foo": { "bar": {...} } } - // This isn't an unused node key, so we want to skip it - isNestedJSON := i > 0 && len(item.Keys) > 1 - if !isNestedJSON && (k.Token.JSON || k.Token.Type == token.IDENT) { - fn := k.Token.Value().(string) - sl := unusedNodeKeys[fn] - unusedNodeKeys[fn] = append(sl, k.Token.Pos) - } - } - } - - for _, f := range fields { - field, fieldValue := f.field, f.val - if !fieldValue.IsValid() { - // This should never happen - panic("field is not valid") - } - - // If we can't set the field, then it is unexported or something, - // and we just continue onwards. - if !fieldValue.CanSet() { - continue - } - - fieldName := field.Name - - tagValue := field.Tag.Get(tagName) - tagParts := strings.SplitN(tagValue, ",", 2) - if len(tagParts) >= 2 { - switch tagParts[1] { - case "decodedFields": - decodedFieldsVal = append(decodedFieldsVal, fieldValue) - continue - case "key": - if item == nil { - return &parser.PosError{ - Pos: node.Pos(), - Err: fmt.Errorf("%s: %s asked for 'key', impossible", - name, fieldName), - } - } - - fieldValue.SetString(item.Keys[0].Token.Value().(string)) - continue - case "unusedKeyPositions": - unusedKeysVal = append(unusedKeysVal, fieldValue) - continue - } - } - - if tagParts[0] != "" { - fieldName = tagParts[0] - } - - // Determine the element we'll use to decode. If it is a single - // match (only object with the field), then we decode it exactly. - // If it is a prefix match, then we decode the matches. - filter := list.Filter(fieldName) - - prefixMatches := filter.Children() - matches := filter.Elem() - if len(matches.Items) == 0 && len(prefixMatches.Items) == 0 { - continue - } - - // Track the used keys - usedKeys[fieldName] = struct{}{} - unusedNodeKeys = removeCaseFold(unusedNodeKeys, fieldName) - - // Create the field name and decode. We range over the elements - // because we actually want the value. - fieldName = fmt.Sprintf("%s.%s", name, fieldName) - if len(prefixMatches.Items) > 0 { - if err := d.decode(fieldName, prefixMatches, fieldValue); err != nil { - return err - } - } - for _, match := range matches.Items { - var decodeNode ast.Node = match.Val - if ot, ok := decodeNode.(*ast.ObjectType); ok { - decodeNode = &ast.ObjectList{Items: ot.List.Items} - } - - if err := d.decode(fieldName, decodeNode, fieldValue); err != nil { - return err - } - } - - decodedFields = append(decodedFields, field.Name) - } - - if len(decodedFieldsVal) > 0 { - // Sort it so that it is deterministic - sort.Strings(decodedFields) - - for _, v := range decodedFieldsVal { - v.Set(reflect.ValueOf(decodedFields)) - } - } - - if len(unusedNodeKeys) > 0 { - // like decodedFields, populated the unusedKeys field(s) - for _, v := range unusedKeysVal { - v.Set(reflect.ValueOf(unusedNodeKeys)) - } - } - - return nil -} - -// findNodeType returns the type of ast.Node -func findNodeType() reflect.Type { - var nodeContainer struct { - Node ast.Node - } - value := reflect.ValueOf(nodeContainer).FieldByName("Node") - return value.Type() -} - -func removeCaseFold(xs map[string][]token.Pos, y string) map[string][]token.Pos { - var toDel []string - - for i := range xs { - if strings.EqualFold(i, y) { - toDel = append(toDel, i) - } - } - for _, i := range toDel { - delete(xs, i) - } - return xs -} diff --git a/vendor/github.com/hashicorp/hcl/hcl.go b/vendor/github.com/hashicorp/hcl/hcl.go deleted file mode 100644 index 575a20b50b..0000000000 --- a/vendor/github.com/hashicorp/hcl/hcl.go +++ /dev/null @@ -1,11 +0,0 @@ -// Package hcl decodes HCL into usable Go structures. -// -// hcl input can come in either pure HCL format or JSON format. -// It can be parsed into an AST, and then decoded into a structure, -// or it can be decoded directly from a string into a structure. -// -// If you choose to parse HCL into a raw AST, the benefit is that you -// can write custom visitor implementations to implement custom -// semantic checks. By default, HCL does not perform any semantic -// checks. -package hcl diff --git a/vendor/github.com/hashicorp/hcl/hcl/ast/ast.go b/vendor/github.com/hashicorp/hcl/hcl/ast/ast.go deleted file mode 100644 index f5b6b93b94..0000000000 --- a/vendor/github.com/hashicorp/hcl/hcl/ast/ast.go +++ /dev/null @@ -1,226 +0,0 @@ -// Package ast declares the types used to represent syntax trees for HCL -// (HashiCorp Configuration Language) -package ast - -import ( - "fmt" - "strings" - - "github.com/hashicorp/hcl/hcl/token" -) - -// Node is an element in the abstract syntax tree. -type Node interface { - node() - Pos() token.Pos -} - -func (File) node() {} -func (ObjectList) node() {} -func (ObjectKey) node() {} -func (ObjectItem) node() {} -func (Comment) node() {} -func (CommentGroup) node() {} -func (ObjectType) node() {} -func (LiteralType) node() {} -func (ListType) node() {} - -var unknownPos token.Pos - -// File represents a single HCL file -type File struct { - Node Node // usually a *ObjectList - Comments []*CommentGroup // list of all comments in the source -} - -func (f *File) Pos() token.Pos { - return f.Node.Pos() -} - -// ObjectList represents a list of ObjectItems. An HCL file itself is an -// ObjectList. -type ObjectList struct { - Items []*ObjectItem -} - -func (o *ObjectList) Add(item *ObjectItem) { - o.Items = append(o.Items, item) -} - -// Filter filters out the objects with the given key list as a prefix. -// -// The returned list of objects contain ObjectItems where the keys have -// this prefix already stripped off. This might result in objects with -// zero-length key lists if they have no children. -// -// If no matches are found, an empty ObjectList (non-nil) is returned. -func (o *ObjectList) Filter(keys ...string) *ObjectList { - var result ObjectList - for _, item := range o.Items { - // If there aren't enough keys, then ignore this - if len(item.Keys) < len(keys) { - continue - } - - match := true - for i, key := range item.Keys[:len(keys)] { - key := key.Token.Value().(string) - if key != keys[i] && !strings.EqualFold(key, keys[i]) { - match = false - break - } - } - if !match { - continue - } - - // Strip off the prefix from the children - newItem := *item - newItem.Keys = newItem.Keys[len(keys):] - result.Add(&newItem) - } - - return &result -} - -// Children returns further nested objects (key length > 0) within this -// ObjectList. This should be used with Filter to get at child items. -func (o *ObjectList) Children() *ObjectList { - var result ObjectList - for _, item := range o.Items { - if len(item.Keys) > 0 { - result.Add(item) - } - } - - return &result -} - -// Elem returns items in the list that are direct element assignments -// (key length == 0). This should be used with Filter to get at elements. -func (o *ObjectList) Elem() *ObjectList { - var result ObjectList - for _, item := range o.Items { - if len(item.Keys) == 0 { - result.Add(item) - } - } - - return &result -} - -func (o *ObjectList) Pos() token.Pos { - // If an Object has no members, it won't have a first item - // to use as position - if len(o.Items) == 0 { - return unknownPos - } - // Return the uninitialized position - return o.Items[0].Pos() -} - -// ObjectItem represents a HCL Object Item. An item is represented with a key -// (or keys). It can be an assignment or an object (both normal and nested) -type ObjectItem struct { - // keys is only one length long if it's of type assignment. If it's a - // nested object it can be larger than one. In that case "assign" is - // invalid as there is no assignments for a nested object. - Keys []*ObjectKey - - // assign contains the position of "=", if any - Assign token.Pos - - // val is the item itself. It can be an object,list, number, bool or a - // string. If key length is larger than one, val can be only of type - // Object. - Val Node - - LeadComment *CommentGroup // associated lead comment - LineComment *CommentGroup // associated line comment -} - -func (o *ObjectItem) Pos() token.Pos { - // If a parsed object has no keys, there is no position - // for its first element. - if len(o.Keys) == 0 { - return unknownPos - } - - return o.Keys[0].Pos() -} - -// ObjectKeys are either an identifier or of type string. -type ObjectKey struct { - Token token.Token -} - -func (o *ObjectKey) Pos() token.Pos { - return o.Token.Pos -} - -// LiteralType represents a literal of basic type. Valid types are: -// token.NUMBER, token.FLOAT, token.BOOL and token.STRING -type LiteralType struct { - Token token.Token - - // comment types, only used when in a list - LeadComment *CommentGroup - LineComment *CommentGroup -} - -func (l *LiteralType) Pos() token.Pos { - return l.Token.Pos -} - -// ListStatement represents a HCL List type -type ListType struct { - Lbrack token.Pos // position of "[" - Rbrack token.Pos // position of "]" - List []Node // the elements in lexical order -} - -func (l *ListType) Pos() token.Pos { - return l.Lbrack -} - -func (l *ListType) Add(node Node) { - l.List = append(l.List, node) -} - -// ObjectType represents a HCL Object Type -type ObjectType struct { - Lbrace token.Pos // position of "{" - Rbrace token.Pos // position of "}" - List *ObjectList // the nodes in lexical order -} - -func (o *ObjectType) Pos() token.Pos { - return o.Lbrace -} - -// Comment node represents a single //, # style or /*- style commment -type Comment struct { - Start token.Pos // position of / or # - Text string -} - -func (c *Comment) Pos() token.Pos { - return c.Start -} - -// CommentGroup node represents a sequence of comments with no other tokens and -// no empty lines between. -type CommentGroup struct { - List []*Comment // len(List) > 0 -} - -func (c *CommentGroup) Pos() token.Pos { - return c.List[0].Pos() -} - -//------------------------------------------------------------------- -// GoStringer -//------------------------------------------------------------------- - -func (o *ObjectKey) GoString() string { return fmt.Sprintf("*%#v", *o) } -func (o *ObjectList) GoString() string { return fmt.Sprintf("*%#v", *o) } diff --git a/vendor/github.com/hashicorp/hcl/hcl/ast/walk.go b/vendor/github.com/hashicorp/hcl/hcl/ast/walk.go deleted file mode 100644 index ba07ad42b0..0000000000 --- a/vendor/github.com/hashicorp/hcl/hcl/ast/walk.go +++ /dev/null @@ -1,52 +0,0 @@ -package ast - -import "fmt" - -// WalkFunc describes a function to be called for each node during a Walk. The -// returned node can be used to rewrite the AST. Walking stops the returned -// bool is false. -type WalkFunc func(Node) (Node, bool) - -// Walk traverses an AST in depth-first order: It starts by calling fn(node); -// node must not be nil. If fn returns true, Walk invokes fn recursively for -// each of the non-nil children of node, followed by a call of fn(nil). The -// returned node of fn can be used to rewrite the passed node to fn. -func Walk(node Node, fn WalkFunc) Node { - rewritten, ok := fn(node) - if !ok { - return rewritten - } - - switch n := node.(type) { - case *File: - n.Node = Walk(n.Node, fn) - case *ObjectList: - for i, item := range n.Items { - n.Items[i] = Walk(item, fn).(*ObjectItem) - } - case *ObjectKey: - // nothing to do - case *ObjectItem: - for i, k := range n.Keys { - n.Keys[i] = Walk(k, fn).(*ObjectKey) - } - - if n.Val != nil { - n.Val = Walk(n.Val, fn) - } - case *LiteralType: - // nothing to do - case *ListType: - for i, l := range n.List { - n.List[i] = Walk(l, fn) - } - case *ObjectType: - n.List = Walk(n.List, fn).(*ObjectList) - default: - // should we panic here? - fmt.Printf("unknown type: %T\n", n) - } - - fn(nil) - return rewritten -} diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/error.go b/vendor/github.com/hashicorp/hcl/hcl/parser/error.go deleted file mode 100644 index 5c99381dfb..0000000000 --- a/vendor/github.com/hashicorp/hcl/hcl/parser/error.go +++ /dev/null @@ -1,17 +0,0 @@ -package parser - -import ( - "fmt" - - "github.com/hashicorp/hcl/hcl/token" -) - -// PosError is a parse error that contains a position. -type PosError struct { - Pos token.Pos - Err error -} - -func (e *PosError) Error() string { - return fmt.Sprintf("At %s: %s", e.Pos, e.Err) -} diff --git a/vendor/github.com/hashicorp/hcl/hcl/parser/parser.go b/vendor/github.com/hashicorp/hcl/hcl/parser/parser.go deleted file mode 100644 index 64c83bcfb5..0000000000 --- a/vendor/github.com/hashicorp/hcl/hcl/parser/parser.go +++ /dev/null @@ -1,532 +0,0 @@ -// Package parser implements a parser for HCL (HashiCorp Configuration -// Language) -package parser - -import ( - "bytes" - "errors" - "fmt" - "strings" - - "github.com/hashicorp/hcl/hcl/ast" - "github.com/hashicorp/hcl/hcl/scanner" - "github.com/hashicorp/hcl/hcl/token" -) - -type Parser struct { - sc *scanner.Scanner - - // Last read token - tok token.Token - commaPrev token.Token - - comments []*ast.CommentGroup - leadComment *ast.CommentGroup // last lead comment - lineComment *ast.CommentGroup // last line comment - - enableTrace bool - indent int - n int // buffer size (max = 1) -} - -func newParser(src []byte) *Parser { - return &Parser{ - sc: scanner.New(src), - } -} - -// Parse returns the fully parsed source and returns the abstract syntax tree. -func Parse(src []byte) (*ast.File, error) { - // normalize all line endings - // since the scanner and output only work with "\n" line endings, we may - // end up with dangling "\r" characters in the parsed data. - src = bytes.Replace(src, []byte("\r\n"), []byte("\n"), -1) - - p := newParser(src) - return p.Parse() -} - -var errEofToken = errors.New("EOF token found") - -// Parse returns the fully parsed source and returns the abstract syntax tree. -func (p *Parser) Parse() (*ast.File, error) { - f := &ast.File{} - var err, scerr error - p.sc.Error = func(pos token.Pos, msg string) { - scerr = &PosError{Pos: pos, Err: errors.New(msg)} - } - - f.Node, err = p.objectList(false) - if scerr != nil { - return nil, scerr - } - if err != nil { - return nil, err - } - - f.Comments = p.comments - return f, nil -} - -// objectList parses a list of items within an object (generally k/v pairs). -// The parameter" obj" tells this whether to we are within an object (braces: -// '{', '}') or just at the top level. If we're within an object, we end -// at an RBRACE. -func (p *Parser) objectList(obj bool) (*ast.ObjectList, error) { - defer un(trace(p, "ParseObjectList")) - node := &ast.ObjectList{} - - for { - if obj { - tok := p.scan() - p.unscan() - if tok.Type == token.RBRACE { - break - } - } - - n, err := p.objectItem() - if err == errEofToken { - break // we are finished - } - - // we don't return a nil node, because might want to use already - // collected items. - if err != nil { - return node, err - } - - node.Add(n) - - // object lists can be optionally comma-delimited e.g. when a list of maps - // is being expressed, so a comma is allowed here - it's simply consumed - tok := p.scan() - if tok.Type != token.COMMA { - p.unscan() - } - } - return node, nil -} - -func (p *Parser) consumeComment() (comment *ast.Comment, endline int) { - endline = p.tok.Pos.Line - - // count the endline if it's multiline comment, ie starting with /* - if len(p.tok.Text) > 1 && p.tok.Text[1] == '*' { - // don't use range here - no need to decode Unicode code points - for i := 0; i < len(p.tok.Text); i++ { - if p.tok.Text[i] == '\n' { - endline++ - } - } - } - - comment = &ast.Comment{Start: p.tok.Pos, Text: p.tok.Text} - p.tok = p.sc.Scan() - return -} - -func (p *Parser) consumeCommentGroup(n int) (comments *ast.CommentGroup, endline int) { - var list []*ast.Comment - endline = p.tok.Pos.Line - - for p.tok.Type == token.COMMENT && p.tok.Pos.Line <= endline+n { - var comment *ast.Comment - comment, endline = p.consumeComment() - list = append(list, comment) - } - - // add comment group to the comments list - comments = &ast.CommentGroup{List: list} - p.comments = append(p.comments, comments) - - return -} - -// objectItem parses a single object item -func (p *Parser) objectItem() (*ast.ObjectItem, error) { - defer un(trace(p, "ParseObjectItem")) - - keys, err := p.objectKey() - if len(keys) > 0 && err == errEofToken { - // We ignore eof token here since it is an error if we didn't - // receive a value (but we did receive a key) for the item. - err = nil - } - if len(keys) > 0 && err != nil && p.tok.Type == token.RBRACE { - // This is a strange boolean statement, but what it means is: - // We have keys with no value, and we're likely in an object - // (since RBrace ends an object). For this, we set err to nil so - // we continue and get the error below of having the wrong value - // type. - err = nil - - // Reset the token type so we don't think it completed fine. See - // objectType which uses p.tok.Type to check if we're done with - // the object. - p.tok.Type = token.EOF - } - if err != nil { - return nil, err - } - - o := &ast.ObjectItem{ - Keys: keys, - } - - if p.leadComment != nil { - o.LeadComment = p.leadComment - p.leadComment = nil - } - - switch p.tok.Type { - case token.ASSIGN: - o.Assign = p.tok.Pos - o.Val, err = p.object() - if err != nil { - return nil, err - } - case token.LBRACE: - o.Val, err = p.objectType() - if err != nil { - return nil, err - } - default: - keyStr := make([]string, 0, len(keys)) - for _, k := range keys { - keyStr = append(keyStr, k.Token.Text) - } - - return nil, &PosError{ - Pos: p.tok.Pos, - Err: fmt.Errorf( - "key '%s' expected start of object ('{') or assignment ('=')", - strings.Join(keyStr, " ")), - } - } - - // key=#comment - // val - if p.lineComment != nil { - o.LineComment, p.lineComment = p.lineComment, nil - } - - // do a look-ahead for line comment - p.scan() - if len(keys) > 0 && o.Val.Pos().Line == keys[0].Pos().Line && p.lineComment != nil { - o.LineComment = p.lineComment - p.lineComment = nil - } - p.unscan() - return o, nil -} - -// objectKey parses an object key and returns a ObjectKey AST -func (p *Parser) objectKey() ([]*ast.ObjectKey, error) { - keyCount := 0 - keys := make([]*ast.ObjectKey, 0) - - for { - tok := p.scan() - switch tok.Type { - case token.EOF: - // It is very important to also return the keys here as well as - // the error. This is because we need to be able to tell if we - // did parse keys prior to finding the EOF, or if we just found - // a bare EOF. - return keys, errEofToken - case token.ASSIGN: - // assignment or object only, but not nested objects. this is not - // allowed: `foo bar = {}` - if keyCount > 1 { - return nil, &PosError{ - Pos: p.tok.Pos, - Err: fmt.Errorf("nested object expected: LBRACE got: %s", p.tok.Type), - } - } - - if keyCount == 0 { - return nil, &PosError{ - Pos: p.tok.Pos, - Err: errors.New("no object keys found!"), - } - } - - return keys, nil - case token.LBRACE: - var err error - - // If we have no keys, then it is a syntax error. i.e. {{}} is not - // allowed. - if len(keys) == 0 { - err = &PosError{ - Pos: p.tok.Pos, - Err: fmt.Errorf("expected: IDENT | STRING got: %s", p.tok.Type), - } - } - - // object - return keys, err - case token.IDENT, token.STRING: - keyCount++ - keys = append(keys, &ast.ObjectKey{Token: p.tok}) - case token.ILLEGAL: - return keys, &PosError{ - Pos: p.tok.Pos, - Err: fmt.Errorf("illegal character"), - } - default: - return keys, &PosError{ - Pos: p.tok.Pos, - Err: fmt.Errorf("expected: IDENT | STRING | ASSIGN | LBRACE got: %s", p.tok.Type), - } - } - } -} - -// object parses any type of object, such as number, bool, string, object or -// list. -func (p *Parser) object() (ast.Node, error) { - defer un(trace(p, "ParseType")) - tok := p.scan() - - switch tok.Type { - case token.NUMBER, token.FLOAT, token.BOOL, token.STRING, token.HEREDOC: - return p.literalType() - case token.LBRACE: - return p.objectType() - case token.LBRACK: - return p.listType() - case token.COMMENT: - // implement comment - case token.EOF: - return nil, errEofToken - } - - return nil, &PosError{ - Pos: tok.Pos, - Err: fmt.Errorf("Unknown token: %+v", tok), - } -} - -// objectType parses an object type and returns a ObjectType AST -func (p *Parser) objectType() (*ast.ObjectType, error) { - defer un(trace(p, "ParseObjectType")) - - // we assume that the currently scanned token is a LBRACE - o := &ast.ObjectType{ - Lbrace: p.tok.Pos, - } - - l, err := p.objectList(true) - - // if we hit RBRACE, we are good to go (means we parsed all Items), if it's - // not a RBRACE, it's an syntax error and we just return it. - if err != nil && p.tok.Type != token.RBRACE { - return nil, err - } - - // No error, scan and expect the ending to be a brace - if tok := p.scan(); tok.Type != token.RBRACE { - return nil, &PosError{ - Pos: tok.Pos, - Err: fmt.Errorf("object expected closing RBRACE got: %s", tok.Type), - } - } - - o.List = l - o.Rbrace = p.tok.Pos // advanced via parseObjectList - return o, nil -} - -// listType parses a list type and returns a ListType AST -func (p *Parser) listType() (*ast.ListType, error) { - defer un(trace(p, "ParseListType")) - - // we assume that the currently scanned token is a LBRACK - l := &ast.ListType{ - Lbrack: p.tok.Pos, - } - - needComma := false - for { - tok := p.scan() - if needComma { - switch tok.Type { - case token.COMMA, token.RBRACK: - default: - return nil, &PosError{ - Pos: tok.Pos, - Err: fmt.Errorf( - "error parsing list, expected comma or list end, got: %s", - tok.Type), - } - } - } - switch tok.Type { - case token.BOOL, token.NUMBER, token.FLOAT, token.STRING, token.HEREDOC: - node, err := p.literalType() - if err != nil { - return nil, err - } - - // If there is a lead comment, apply it - if p.leadComment != nil { - node.LeadComment = p.leadComment - p.leadComment = nil - } - - l.Add(node) - needComma = true - case token.COMMA: - // get next list item or we are at the end - // do a look-ahead for line comment - p.scan() - if p.lineComment != nil && len(l.List) > 0 { - lit, ok := l.List[len(l.List)-1].(*ast.LiteralType) - if ok { - lit.LineComment = p.lineComment - l.List[len(l.List)-1] = lit - p.lineComment = nil - } - } - p.unscan() - - needComma = false - continue - case token.LBRACE: - // Looks like a nested object, so parse it out - node, err := p.objectType() - if err != nil { - return nil, &PosError{ - Pos: tok.Pos, - Err: fmt.Errorf( - "error while trying to parse object within list: %s", err), - } - } - l.Add(node) - needComma = true - case token.LBRACK: - node, err := p.listType() - if err != nil { - return nil, &PosError{ - Pos: tok.Pos, - Err: fmt.Errorf( - "error while trying to parse list within list: %s", err), - } - } - l.Add(node) - case token.RBRACK: - // finished - l.Rbrack = p.tok.Pos - return l, nil - default: - return nil, &PosError{ - Pos: tok.Pos, - Err: fmt.Errorf("unexpected token while parsing list: %s", tok.Type), - } - } - } -} - -// literalType parses a literal type and returns a LiteralType AST -func (p *Parser) literalType() (*ast.LiteralType, error) { - defer un(trace(p, "ParseLiteral")) - - return &ast.LiteralType{ - Token: p.tok, - }, nil -} - -// scan returns the next token from the underlying scanner. If a token has -// been unscanned then read that instead. In the process, it collects any -// comment groups encountered, and remembers the last lead and line comments. -func (p *Parser) scan() token.Token { - // If we have a token on the buffer, then return it. - if p.n != 0 { - p.n = 0 - return p.tok - } - - // Otherwise read the next token from the scanner and Save it to the buffer - // in case we unscan later. - prev := p.tok - p.tok = p.sc.Scan() - - if p.tok.Type == token.COMMENT { - var comment *ast.CommentGroup - var endline int - - // fmt.Printf("p.tok.Pos.Line = %+v prev: %d endline %d \n", - // p.tok.Pos.Line, prev.Pos.Line, endline) - if p.tok.Pos.Line == prev.Pos.Line { - // The comment is on same line as the previous token; it - // cannot be a lead comment but may be a line comment. - comment, endline = p.consumeCommentGroup(0) - if p.tok.Pos.Line != endline { - // The next token is on a different line, thus - // the last comment group is a line comment. - p.lineComment = comment - } - } - - // consume successor comments, if any - endline = -1 - for p.tok.Type == token.COMMENT { - comment, endline = p.consumeCommentGroup(1) - } - - if endline+1 == p.tok.Pos.Line && p.tok.Type != token.RBRACE { - switch p.tok.Type { - case token.RBRACE, token.RBRACK: - // Do not count for these cases - default: - // The next token is following on the line immediately after the - // comment group, thus the last comment group is a lead comment. - p.leadComment = comment - } - } - - } - - return p.tok -} - -// unscan pushes the previously read token back onto the buffer. -func (p *Parser) unscan() { - p.n = 1 -} - -// ---------------------------------------------------------------------------- -// Parsing support - -func (p *Parser) printTrace(a ...interface{}) { - if !p.enableTrace { - return - } - - const dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " - const n = len(dots) - fmt.Printf("%5d:%3d: ", p.tok.Pos.Line, p.tok.Pos.Column) - - i := 2 * p.indent - for i > n { - fmt.Print(dots) - i -= n - } - // i <= n - fmt.Print(dots[0:i]) - fmt.Println(a...) -} - -func trace(p *Parser, msg string) *Parser { - p.printTrace(msg, "(") - p.indent++ - return p -} - -// Usage pattern: defer un(trace(p, "...")) -func un(p *Parser) { - p.indent-- - p.printTrace(")") -} diff --git a/vendor/github.com/hashicorp/hcl/hcl/printer/nodes.go b/vendor/github.com/hashicorp/hcl/hcl/printer/nodes.go deleted file mode 100644 index 7c038d12a2..0000000000 --- a/vendor/github.com/hashicorp/hcl/hcl/printer/nodes.go +++ /dev/null @@ -1,789 +0,0 @@ -package printer - -import ( - "bytes" - "fmt" - "sort" - - "github.com/hashicorp/hcl/hcl/ast" - "github.com/hashicorp/hcl/hcl/token" -) - -const ( - blank = byte(' ') - newline = byte('\n') - tab = byte('\t') - infinity = 1 << 30 // offset or line -) - -var ( - unindent = []byte("\uE123") // in the private use space -) - -type printer struct { - cfg Config - prev token.Pos - - comments []*ast.CommentGroup // may be nil, contains all comments - standaloneComments []*ast.CommentGroup // contains all standalone comments (not assigned to any node) - - enableTrace bool - indentTrace int -} - -type ByPosition []*ast.CommentGroup - -func (b ByPosition) Len() int { return len(b) } -func (b ByPosition) Swap(i, j int) { b[i], b[j] = b[j], b[i] } -func (b ByPosition) Less(i, j int) bool { return b[i].Pos().Before(b[j].Pos()) } - -// collectComments comments all standalone comments which are not lead or line -// comment -func (p *printer) collectComments(node ast.Node) { - // first collect all comments. This is already stored in - // ast.File.(comments) - ast.Walk(node, func(nn ast.Node) (ast.Node, bool) { - switch t := nn.(type) { - case *ast.File: - p.comments = t.Comments - return nn, false - } - return nn, true - }) - - standaloneComments := make(map[token.Pos]*ast.CommentGroup, 0) - for _, c := range p.comments { - standaloneComments[c.Pos()] = c - } - - // next remove all lead and line comments from the overall comment map. - // This will give us comments which are standalone, comments which are not - // assigned to any kind of node. - ast.Walk(node, func(nn ast.Node) (ast.Node, bool) { - switch t := nn.(type) { - case *ast.LiteralType: - if t.LeadComment != nil { - for _, comment := range t.LeadComment.List { - if _, ok := standaloneComments[comment.Pos()]; ok { - delete(standaloneComments, comment.Pos()) - } - } - } - - if t.LineComment != nil { - for _, comment := range t.LineComment.List { - if _, ok := standaloneComments[comment.Pos()]; ok { - delete(standaloneComments, comment.Pos()) - } - } - } - case *ast.ObjectItem: - if t.LeadComment != nil { - for _, comment := range t.LeadComment.List { - if _, ok := standaloneComments[comment.Pos()]; ok { - delete(standaloneComments, comment.Pos()) - } - } - } - - if t.LineComment != nil { - for _, comment := range t.LineComment.List { - if _, ok := standaloneComments[comment.Pos()]; ok { - delete(standaloneComments, comment.Pos()) - } - } - } - } - - return nn, true - }) - - for _, c := range standaloneComments { - p.standaloneComments = append(p.standaloneComments, c) - } - - sort.Sort(ByPosition(p.standaloneComments)) -} - -// output prints creates b printable HCL output and returns it. -func (p *printer) output(n interface{}) []byte { - var buf bytes.Buffer - - switch t := n.(type) { - case *ast.File: - // File doesn't trace so we add the tracing here - defer un(trace(p, "File")) - return p.output(t.Node) - case *ast.ObjectList: - defer un(trace(p, "ObjectList")) - - var index int - for { - // Determine the location of the next actual non-comment - // item. If we're at the end, the next item is at "infinity" - var nextItem token.Pos - if index != len(t.Items) { - nextItem = t.Items[index].Pos() - } else { - nextItem = token.Pos{Offset: infinity, Line: infinity} - } - - // Go through the standalone comments in the file and print out - // the comments that we should be for this object item. - for _, c := range p.standaloneComments { - // Go through all the comments in the group. The group - // should be printed together, not separated by double newlines. - printed := false - newlinePrinted := false - for _, comment := range c.List { - // We only care about comments after the previous item - // we've printed so that comments are printed in the - // correct locations (between two objects for example). - // And before the next item. - if comment.Pos().After(p.prev) && comment.Pos().Before(nextItem) { - // if we hit the end add newlines so we can print the comment - // we don't do this if prev is invalid which means the - // beginning of the file since the first comment should - // be at the first line. - if !newlinePrinted && p.prev.IsValid() && index == len(t.Items) { - buf.Write([]byte{newline, newline}) - newlinePrinted = true - } - - // Write the actual comment. - buf.WriteString(comment.Text) - buf.WriteByte(newline) - - // Set printed to true to note that we printed something - printed = true - } - } - - // If we're not at the last item, write a new line so - // that there is a newline separating this comment from - // the next object. - if printed && index != len(t.Items) { - buf.WriteByte(newline) - } - } - - if index == len(t.Items) { - break - } - - buf.Write(p.output(t.Items[index])) - if index != len(t.Items)-1 { - // Always write a newline to separate us from the next item - buf.WriteByte(newline) - - // Need to determine if we're going to separate the next item - // with a blank line. The logic here is simple, though there - // are a few conditions: - // - // 1. The next object is more than one line away anyways, - // so we need an empty line. - // - // 2. The next object is not a "single line" object, so - // we need an empty line. - // - // 3. This current object is not a single line object, - // so we need an empty line. - current := t.Items[index] - next := t.Items[index+1] - if next.Pos().Line != t.Items[index].Pos().Line+1 || - !p.isSingleLineObject(next) || - !p.isSingleLineObject(current) { - buf.WriteByte(newline) - } - } - index++ - } - case *ast.ObjectKey: - buf.WriteString(t.Token.Text) - case *ast.ObjectItem: - p.prev = t.Pos() - buf.Write(p.objectItem(t)) - case *ast.LiteralType: - buf.Write(p.literalType(t)) - case *ast.ListType: - buf.Write(p.list(t)) - case *ast.ObjectType: - buf.Write(p.objectType(t)) - default: - fmt.Printf(" unknown type: %T\n", n) - } - - return buf.Bytes() -} - -func (p *printer) literalType(lit *ast.LiteralType) []byte { - result := []byte(lit.Token.Text) - switch lit.Token.Type { - case token.HEREDOC: - // Clear the trailing newline from heredocs - if result[len(result)-1] == '\n' { - result = result[:len(result)-1] - } - - // Poison lines 2+ so that we don't indent them - result = p.heredocIndent(result) - case token.STRING: - // If this is a multiline string, poison lines 2+ so we don't - // indent them. - if bytes.IndexRune(result, '\n') >= 0 { - result = p.heredocIndent(result) - } - } - - return result -} - -// objectItem returns the printable HCL form of an object item. An object type -// starts with one/multiple keys and has a value. The value might be of any -// type. -func (p *printer) objectItem(o *ast.ObjectItem) []byte { - defer un(trace(p, fmt.Sprintf("ObjectItem: %s", o.Keys[0].Token.Text))) - var buf bytes.Buffer - - if o.LeadComment != nil { - for _, comment := range o.LeadComment.List { - buf.WriteString(comment.Text) - buf.WriteByte(newline) - } - } - - // If key and val are on different lines, treat line comments like lead comments. - if o.LineComment != nil && o.Val.Pos().Line != o.Keys[0].Pos().Line { - for _, comment := range o.LineComment.List { - buf.WriteString(comment.Text) - buf.WriteByte(newline) - } - } - - for i, k := range o.Keys { - buf.WriteString(k.Token.Text) - buf.WriteByte(blank) - - // reach end of key - if o.Assign.IsValid() && i == len(o.Keys)-1 && len(o.Keys) == 1 { - buf.WriteString("=") - buf.WriteByte(blank) - } - } - - buf.Write(p.output(o.Val)) - - if o.LineComment != nil && o.Val.Pos().Line == o.Keys[0].Pos().Line { - buf.WriteByte(blank) - for _, comment := range o.LineComment.List { - buf.WriteString(comment.Text) - } - } - - return buf.Bytes() -} - -// objectType returns the printable HCL form of an object type. An object type -// begins with a brace and ends with a brace. -func (p *printer) objectType(o *ast.ObjectType) []byte { - defer un(trace(p, "ObjectType")) - var buf bytes.Buffer - buf.WriteString("{") - - var index int - var nextItem token.Pos - var commented, newlinePrinted bool - for { - // Determine the location of the next actual non-comment - // item. If we're at the end, the next item is the closing brace - if index != len(o.List.Items) { - nextItem = o.List.Items[index].Pos() - } else { - nextItem = o.Rbrace - } - - // Go through the standalone comments in the file and print out - // the comments that we should be for this object item. - for _, c := range p.standaloneComments { - printed := false - var lastCommentPos token.Pos - for _, comment := range c.List { - // We only care about comments after the previous item - // we've printed so that comments are printed in the - // correct locations (between two objects for example). - // And before the next item. - if comment.Pos().After(p.prev) && comment.Pos().Before(nextItem) { - // If there are standalone comments and the initial newline has not - // been printed yet, do it now. - if !newlinePrinted { - newlinePrinted = true - buf.WriteByte(newline) - } - - // add newline if it's between other printed nodes - if index > 0 { - commented = true - buf.WriteByte(newline) - } - - // Store this position - lastCommentPos = comment.Pos() - - // output the comment itself - buf.Write(p.indent(p.heredocIndent([]byte(comment.Text)))) - - // Set printed to true to note that we printed something - printed = true - - /* - if index != len(o.List.Items) { - buf.WriteByte(newline) // do not print on the end - } - */ - } - } - - // Stuff to do if we had comments - if printed { - // Always write a newline - buf.WriteByte(newline) - - // If there is another item in the object and our comment - // didn't hug it directly, then make sure there is a blank - // line separating them. - if nextItem != o.Rbrace && nextItem.Line != lastCommentPos.Line+1 { - buf.WriteByte(newline) - } - } - } - - if index == len(o.List.Items) { - p.prev = o.Rbrace - break - } - - // At this point we are sure that it's not a totally empty block: print - // the initial newline if it hasn't been printed yet by the previous - // block about standalone comments. - if !newlinePrinted { - buf.WriteByte(newline) - newlinePrinted = true - } - - // check if we have adjacent one liner items. If yes we'll going to align - // the comments. - var aligned []*ast.ObjectItem - for _, item := range o.List.Items[index:] { - // we don't group one line lists - if len(o.List.Items) == 1 { - break - } - - // one means a oneliner with out any lead comment - // two means a oneliner with lead comment - // anything else might be something else - cur := lines(string(p.objectItem(item))) - if cur > 2 { - break - } - - curPos := item.Pos() - - nextPos := token.Pos{} - if index != len(o.List.Items)-1 { - nextPos = o.List.Items[index+1].Pos() - } - - prevPos := token.Pos{} - if index != 0 { - prevPos = o.List.Items[index-1].Pos() - } - - // fmt.Println("DEBUG ----------------") - // fmt.Printf("prev = %+v prevPos: %s\n", prev, prevPos) - // fmt.Printf("cur = %+v curPos: %s\n", cur, curPos) - // fmt.Printf("next = %+v nextPos: %s\n", next, nextPos) - - if curPos.Line+1 == nextPos.Line { - aligned = append(aligned, item) - index++ - continue - } - - if curPos.Line-1 == prevPos.Line { - aligned = append(aligned, item) - index++ - - // finish if we have a new line or comment next. This happens - // if the next item is not adjacent - if curPos.Line+1 != nextPos.Line { - break - } - continue - } - - break - } - - // put newlines if the items are between other non aligned items. - // newlines are also added if there is a standalone comment already, so - // check it too - if !commented && index != len(aligned) { - buf.WriteByte(newline) - } - - if len(aligned) >= 1 { - p.prev = aligned[len(aligned)-1].Pos() - - items := p.alignedItems(aligned) - buf.Write(p.indent(items)) - } else { - p.prev = o.List.Items[index].Pos() - - buf.Write(p.indent(p.objectItem(o.List.Items[index]))) - index++ - } - - buf.WriteByte(newline) - } - - buf.WriteString("}") - return buf.Bytes() -} - -func (p *printer) alignedItems(items []*ast.ObjectItem) []byte { - var buf bytes.Buffer - - // find the longest key and value length, needed for alignment - var longestKeyLen int // longest key length - var longestValLen int // longest value length - for _, item := range items { - key := len(item.Keys[0].Token.Text) - val := len(p.output(item.Val)) - - if key > longestKeyLen { - longestKeyLen = key - } - - if val > longestValLen { - longestValLen = val - } - } - - for i, item := range items { - if item.LeadComment != nil { - for _, comment := range item.LeadComment.List { - buf.WriteString(comment.Text) - buf.WriteByte(newline) - } - } - - for i, k := range item.Keys { - keyLen := len(k.Token.Text) - buf.WriteString(k.Token.Text) - for i := 0; i < longestKeyLen-keyLen+1; i++ { - buf.WriteByte(blank) - } - - // reach end of key - if i == len(item.Keys)-1 && len(item.Keys) == 1 { - buf.WriteString("=") - buf.WriteByte(blank) - } - } - - val := p.output(item.Val) - valLen := len(val) - buf.Write(val) - - if item.Val.Pos().Line == item.Keys[0].Pos().Line && item.LineComment != nil { - for i := 0; i < longestValLen-valLen+1; i++ { - buf.WriteByte(blank) - } - - for _, comment := range item.LineComment.List { - buf.WriteString(comment.Text) - } - } - - // do not print for the last item - if i != len(items)-1 { - buf.WriteByte(newline) - } - } - - return buf.Bytes() -} - -// list returns the printable HCL form of an list type. -func (p *printer) list(l *ast.ListType) []byte { - if p.isSingleLineList(l) { - return p.singleLineList(l) - } - - var buf bytes.Buffer - buf.WriteString("[") - buf.WriteByte(newline) - - var longestLine int - for _, item := range l.List { - // for now we assume that the list only contains literal types - if lit, ok := item.(*ast.LiteralType); ok { - lineLen := len(lit.Token.Text) - if lineLen > longestLine { - longestLine = lineLen - } - } - } - - haveEmptyLine := false - for i, item := range l.List { - // If we have a lead comment, then we want to write that first - leadComment := false - if lit, ok := item.(*ast.LiteralType); ok && lit.LeadComment != nil { - leadComment = true - - // Ensure an empty line before every element with a - // lead comment (except the first item in a list). - if !haveEmptyLine && i != 0 { - buf.WriteByte(newline) - } - - for _, comment := range lit.LeadComment.List { - buf.Write(p.indent([]byte(comment.Text))) - buf.WriteByte(newline) - } - } - - // also indent each line - val := p.output(item) - curLen := len(val) - buf.Write(p.indent(val)) - - // if this item is a heredoc, then we output the comma on - // the next line. This is the only case this happens. - comma := []byte{','} - if lit, ok := item.(*ast.LiteralType); ok && lit.Token.Type == token.HEREDOC { - buf.WriteByte(newline) - comma = p.indent(comma) - } - - buf.Write(comma) - - if lit, ok := item.(*ast.LiteralType); ok && lit.LineComment != nil { - // if the next item doesn't have any comments, do not align - buf.WriteByte(blank) // align one space - for i := 0; i < longestLine-curLen; i++ { - buf.WriteByte(blank) - } - - for _, comment := range lit.LineComment.List { - buf.WriteString(comment.Text) - } - } - - buf.WriteByte(newline) - - // Ensure an empty line after every element with a - // lead comment (except the first item in a list). - haveEmptyLine = leadComment && i != len(l.List)-1 - if haveEmptyLine { - buf.WriteByte(newline) - } - } - - buf.WriteString("]") - return buf.Bytes() -} - -// isSingleLineList returns true if: -// * they were previously formatted entirely on one line -// * they consist entirely of literals -// * there are either no heredoc strings or the list has exactly one element -// * there are no line comments -func (printer) isSingleLineList(l *ast.ListType) bool { - for _, item := range l.List { - if item.Pos().Line != l.Lbrack.Line { - return false - } - - lit, ok := item.(*ast.LiteralType) - if !ok { - return false - } - - if lit.Token.Type == token.HEREDOC && len(l.List) != 1 { - return false - } - - if lit.LineComment != nil { - return false - } - } - - return true -} - -// singleLineList prints a simple single line list. -// For a definition of "simple", see isSingleLineList above. -func (p *printer) singleLineList(l *ast.ListType) []byte { - buf := &bytes.Buffer{} - - buf.WriteString("[") - for i, item := range l.List { - if i != 0 { - buf.WriteString(", ") - } - - // Output the item itself - buf.Write(p.output(item)) - - // The heredoc marker needs to be at the end of line. - if lit, ok := item.(*ast.LiteralType); ok && lit.Token.Type == token.HEREDOC { - buf.WriteByte(newline) - } - } - - buf.WriteString("]") - return buf.Bytes() -} - -// indent indents the lines of the given buffer for each non-empty line -func (p *printer) indent(buf []byte) []byte { - var prefix []byte - if p.cfg.SpacesWidth != 0 { - for i := 0; i < p.cfg.SpacesWidth; i++ { - prefix = append(prefix, blank) - } - } else { - prefix = []byte{tab} - } - - var res []byte - bol := true - for _, c := range buf { - if bol && c != '\n' { - res = append(res, prefix...) - } - - res = append(res, c) - bol = c == '\n' - } - return res -} - -// unindent removes all the indentation from the tombstoned lines -func (p *printer) unindent(buf []byte) []byte { - var res []byte - for i := 0; i < len(buf); i++ { - skip := len(buf)-i <= len(unindent) - if !skip { - skip = !bytes.Equal(unindent, buf[i:i+len(unindent)]) - } - if skip { - res = append(res, buf[i]) - continue - } - - // We have a marker. we have to backtrace here and clean out - // any whitespace ahead of our tombstone up to a \n - for j := len(res) - 1; j >= 0; j-- { - if res[j] == '\n' { - break - } - - res = res[:j] - } - - // Skip the entire unindent marker - i += len(unindent) - 1 - } - - return res -} - -// heredocIndent marks all the 2nd and further lines as unindentable -func (p *printer) heredocIndent(buf []byte) []byte { - var res []byte - bol := false - for _, c := range buf { - if bol && c != '\n' { - res = append(res, unindent...) - } - res = append(res, c) - bol = c == '\n' - } - return res -} - -// isSingleLineObject tells whether the given object item is a single -// line object such as "obj {}". -// -// A single line object: -// -// * has no lead comments (hence multi-line) -// * has no assignment -// * has no values in the stanza (within {}) -// -func (p *printer) isSingleLineObject(val *ast.ObjectItem) bool { - // If there is a lead comment, can't be one line - if val.LeadComment != nil { - return false - } - - // If there is assignment, we always break by line - if val.Assign.IsValid() { - return false - } - - // If it isn't an object type, then its not a single line object - ot, ok := val.Val.(*ast.ObjectType) - if !ok { - return false - } - - // If the object has no items, it is single line! - return len(ot.List.Items) == 0 -} - -func lines(txt string) int { - endline := 1 - for i := 0; i < len(txt); i++ { - if txt[i] == '\n' { - endline++ - } - } - return endline -} - -// ---------------------------------------------------------------------------- -// Tracing support - -func (p *printer) printTrace(a ...interface{}) { - if !p.enableTrace { - return - } - - const dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " - const n = len(dots) - i := 2 * p.indentTrace - for i > n { - fmt.Print(dots) - i -= n - } - // i <= n - fmt.Print(dots[0:i]) - fmt.Println(a...) -} - -func trace(p *printer, msg string) *printer { - p.printTrace(msg, "(") - p.indentTrace++ - return p -} - -// Usage pattern: defer un(trace(p, "...")) -func un(p *printer) { - p.indentTrace-- - p.printTrace(")") -} diff --git a/vendor/github.com/hashicorp/hcl/hcl/printer/printer.go b/vendor/github.com/hashicorp/hcl/hcl/printer/printer.go deleted file mode 100644 index 6617ab8e7a..0000000000 --- a/vendor/github.com/hashicorp/hcl/hcl/printer/printer.go +++ /dev/null @@ -1,66 +0,0 @@ -// Package printer implements printing of AST nodes to HCL format. -package printer - -import ( - "bytes" - "io" - "text/tabwriter" - - "github.com/hashicorp/hcl/hcl/ast" - "github.com/hashicorp/hcl/hcl/parser" -) - -var DefaultConfig = Config{ - SpacesWidth: 2, -} - -// A Config node controls the output of Fprint. -type Config struct { - SpacesWidth int // if set, it will use spaces instead of tabs for alignment -} - -func (c *Config) Fprint(output io.Writer, node ast.Node) error { - p := &printer{ - cfg: *c, - comments: make([]*ast.CommentGroup, 0), - standaloneComments: make([]*ast.CommentGroup, 0), - // enableTrace: true, - } - - p.collectComments(node) - - if _, err := output.Write(p.unindent(p.output(node))); err != nil { - return err - } - - // flush tabwriter, if any - var err error - if tw, _ := output.(*tabwriter.Writer); tw != nil { - err = tw.Flush() - } - - return err -} - -// Fprint "pretty-prints" an HCL node to output -// It calls Config.Fprint with default settings. -func Fprint(output io.Writer, node ast.Node) error { - return DefaultConfig.Fprint(output, node) -} - -// Format formats src HCL and returns the result. -func Format(src []byte) ([]byte, error) { - node, err := parser.Parse(src) - if err != nil { - return nil, err - } - - var buf bytes.Buffer - if err := DefaultConfig.Fprint(&buf, node); err != nil { - return nil, err - } - - // Add trailing newline to result - buf.WriteString("\n") - return buf.Bytes(), nil -} diff --git a/vendor/github.com/hashicorp/hcl/hcl/scanner/scanner.go b/vendor/github.com/hashicorp/hcl/hcl/scanner/scanner.go deleted file mode 100644 index 624a18fe3a..0000000000 --- a/vendor/github.com/hashicorp/hcl/hcl/scanner/scanner.go +++ /dev/null @@ -1,652 +0,0 @@ -// Package scanner implements a scanner for HCL (HashiCorp Configuration -// Language) source text. -package scanner - -import ( - "bytes" - "fmt" - "os" - "regexp" - "unicode" - "unicode/utf8" - - "github.com/hashicorp/hcl/hcl/token" -) - -// eof represents a marker rune for the end of the reader. -const eof = rune(0) - -// Scanner defines a lexical scanner -type Scanner struct { - buf *bytes.Buffer // Source buffer for advancing and scanning - src []byte // Source buffer for immutable access - - // Source Position - srcPos token.Pos // current position - prevPos token.Pos // previous position, used for peek() method - - lastCharLen int // length of last character in bytes - lastLineLen int // length of last line in characters (for correct column reporting) - - tokStart int // token text start position - tokEnd int // token text end position - - // Error is called for each error encountered. If no Error - // function is set, the error is reported to os.Stderr. - Error func(pos token.Pos, msg string) - - // ErrorCount is incremented by one for each error encountered. - ErrorCount int - - // tokPos is the start position of most recently scanned token; set by - // Scan. The Filename field is always left untouched by the Scanner. If - // an error is reported (via Error) and Position is invalid, the scanner is - // not inside a token. - tokPos token.Pos -} - -// New creates and initializes a new instance of Scanner using src as -// its source content. -func New(src []byte) *Scanner { - // even though we accept a src, we read from a io.Reader compatible type - // (*bytes.Buffer). So in the future we might easily change it to streaming - // read. - b := bytes.NewBuffer(src) - s := &Scanner{ - buf: b, - src: src, - } - - // srcPosition always starts with 1 - s.srcPos.Line = 1 - return s -} - -// next reads the next rune from the bufferred reader. Returns the rune(0) if -// an error occurs (or io.EOF is returned). -func (s *Scanner) next() rune { - ch, size, err := s.buf.ReadRune() - if err != nil { - // advance for error reporting - s.srcPos.Column++ - s.srcPos.Offset += size - s.lastCharLen = size - return eof - } - - // remember last position - s.prevPos = s.srcPos - - s.srcPos.Column++ - s.lastCharLen = size - s.srcPos.Offset += size - - if ch == utf8.RuneError && size == 1 { - s.err("illegal UTF-8 encoding") - return ch - } - - if ch == '\n' { - s.srcPos.Line++ - s.lastLineLen = s.srcPos.Column - s.srcPos.Column = 0 - } - - if ch == '\x00' { - s.err("unexpected null character (0x00)") - return eof - } - - if ch == '\uE123' { - s.err("unicode code point U+E123 reserved for internal use") - return utf8.RuneError - } - - // debug - // fmt.Printf("ch: %q, offset:column: %d:%d\n", ch, s.srcPos.Offset, s.srcPos.Column) - return ch -} - -// unread unreads the previous read Rune and updates the source position -func (s *Scanner) unread() { - if err := s.buf.UnreadRune(); err != nil { - panic(err) // this is user fault, we should catch it - } - s.srcPos = s.prevPos // put back last position -} - -// peek returns the next rune without advancing the reader. -func (s *Scanner) peek() rune { - peek, _, err := s.buf.ReadRune() - if err != nil { - return eof - } - - s.buf.UnreadRune() - return peek -} - -// Scan scans the next token and returns the token. -func (s *Scanner) Scan() token.Token { - ch := s.next() - - // skip white space - for isWhitespace(ch) { - ch = s.next() - } - - var tok token.Type - - // token text markings - s.tokStart = s.srcPos.Offset - s.lastCharLen - - // token position, initial next() is moving the offset by one(size of rune - // actually), though we are interested with the starting point - s.tokPos.Offset = s.srcPos.Offset - s.lastCharLen - if s.srcPos.Column > 0 { - // common case: last character was not a '\n' - s.tokPos.Line = s.srcPos.Line - s.tokPos.Column = s.srcPos.Column - } else { - // last character was a '\n' - // (we cannot be at the beginning of the source - // since we have called next() at least once) - s.tokPos.Line = s.srcPos.Line - 1 - s.tokPos.Column = s.lastLineLen - } - - switch { - case isLetter(ch): - tok = token.IDENT - lit := s.scanIdentifier() - if lit == "true" || lit == "false" { - tok = token.BOOL - } - case isDecimal(ch): - tok = s.scanNumber(ch) - default: - switch ch { - case eof: - tok = token.EOF - case '"': - tok = token.STRING - s.scanString() - case '#', '/': - tok = token.COMMENT - s.scanComment(ch) - case '.': - tok = token.PERIOD - ch = s.peek() - if isDecimal(ch) { - tok = token.FLOAT - ch = s.scanMantissa(ch) - ch = s.scanExponent(ch) - } - case '<': - tok = token.HEREDOC - s.scanHeredoc() - case '[': - tok = token.LBRACK - case ']': - tok = token.RBRACK - case '{': - tok = token.LBRACE - case '}': - tok = token.RBRACE - case ',': - tok = token.COMMA - case '=': - tok = token.ASSIGN - case '+': - tok = token.ADD - case '-': - if isDecimal(s.peek()) { - ch := s.next() - tok = s.scanNumber(ch) - } else { - tok = token.SUB - } - default: - s.err("illegal char") - } - } - - // finish token ending - s.tokEnd = s.srcPos.Offset - - // create token literal - var tokenText string - if s.tokStart >= 0 { - tokenText = string(s.src[s.tokStart:s.tokEnd]) - } - s.tokStart = s.tokEnd // ensure idempotency of tokenText() call - - return token.Token{ - Type: tok, - Pos: s.tokPos, - Text: tokenText, - } -} - -func (s *Scanner) scanComment(ch rune) { - // single line comments - if ch == '#' || (ch == '/' && s.peek() != '*') { - if ch == '/' && s.peek() != '/' { - s.err("expected '/' for comment") - return - } - - ch = s.next() - for ch != '\n' && ch >= 0 && ch != eof { - ch = s.next() - } - if ch != eof && ch >= 0 { - s.unread() - } - return - } - - // be sure we get the character after /* This allows us to find comment's - // that are not erminated - if ch == '/' { - s.next() - ch = s.next() // read character after "/*" - } - - // look for /* - style comments - for { - if ch < 0 || ch == eof { - s.err("comment not terminated") - break - } - - ch0 := ch - ch = s.next() - if ch0 == '*' && ch == '/' { - break - } - } -} - -// scanNumber scans a HCL number definition starting with the given rune -func (s *Scanner) scanNumber(ch rune) token.Type { - if ch == '0' { - // check for hexadecimal, octal or float - ch = s.next() - if ch == 'x' || ch == 'X' { - // hexadecimal - ch = s.next() - found := false - for isHexadecimal(ch) { - ch = s.next() - found = true - } - - if !found { - s.err("illegal hexadecimal number") - } - - if ch != eof { - s.unread() - } - - return token.NUMBER - } - - // now it's either something like: 0421(octal) or 0.1231(float) - illegalOctal := false - for isDecimal(ch) { - ch = s.next() - if ch == '8' || ch == '9' { - // this is just a possibility. For example 0159 is illegal, but - // 0159.23 is valid. So we mark a possible illegal octal. If - // the next character is not a period, we'll print the error. - illegalOctal = true - } - } - - if ch == 'e' || ch == 'E' { - ch = s.scanExponent(ch) - return token.FLOAT - } - - if ch == '.' { - ch = s.scanFraction(ch) - - if ch == 'e' || ch == 'E' { - ch = s.next() - ch = s.scanExponent(ch) - } - return token.FLOAT - } - - if illegalOctal { - s.err("illegal octal number") - } - - if ch != eof { - s.unread() - } - return token.NUMBER - } - - s.scanMantissa(ch) - ch = s.next() // seek forward - if ch == 'e' || ch == 'E' { - ch = s.scanExponent(ch) - return token.FLOAT - } - - if ch == '.' { - ch = s.scanFraction(ch) - if ch == 'e' || ch == 'E' { - ch = s.next() - ch = s.scanExponent(ch) - } - return token.FLOAT - } - - if ch != eof { - s.unread() - } - return token.NUMBER -} - -// scanMantissa scans the mantissa beginning from the rune. It returns the next -// non decimal rune. It's used to determine wheter it's a fraction or exponent. -func (s *Scanner) scanMantissa(ch rune) rune { - scanned := false - for isDecimal(ch) { - ch = s.next() - scanned = true - } - - if scanned && ch != eof { - s.unread() - } - return ch -} - -// scanFraction scans the fraction after the '.' rune -func (s *Scanner) scanFraction(ch rune) rune { - if ch == '.' { - ch = s.peek() // we peek just to see if we can move forward - ch = s.scanMantissa(ch) - } - return ch -} - -// scanExponent scans the remaining parts of an exponent after the 'e' or 'E' -// rune. -func (s *Scanner) scanExponent(ch rune) rune { - if ch == 'e' || ch == 'E' { - ch = s.next() - if ch == '-' || ch == '+' { - ch = s.next() - } - ch = s.scanMantissa(ch) - } - return ch -} - -// scanHeredoc scans a heredoc string -func (s *Scanner) scanHeredoc() { - // Scan the second '<' in example: '<= len(identBytes) && identRegexp.Match(s.src[lineStart:s.srcPos.Offset-s.lastCharLen]) { - break - } - - // Not an anchor match, record the start of a new line - lineStart = s.srcPos.Offset - } - - if ch == eof { - s.err("heredoc not terminated") - return - } - } - - return -} - -// scanString scans a quoted string -func (s *Scanner) scanString() { - braces := 0 - for { - // '"' opening already consumed - // read character after quote - ch := s.next() - - if (ch == '\n' && braces == 0) || ch < 0 || ch == eof { - s.err("literal not terminated") - return - } - - if ch == '"' && braces == 0 { - break - } - - // If we're going into a ${} then we can ignore quotes for awhile - if braces == 0 && ch == '$' && s.peek() == '{' { - braces++ - s.next() - } else if braces > 0 && ch == '{' { - braces++ - } - if braces > 0 && ch == '}' { - braces-- - } - - if ch == '\\' { - s.scanEscape() - } - } - - return -} - -// scanEscape scans an escape sequence -func (s *Scanner) scanEscape() rune { - // http://en.cppreference.com/w/cpp/language/escape - ch := s.next() // read character after '/' - switch ch { - case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '"': - // nothing to do - case '0', '1', '2', '3', '4', '5', '6', '7': - // octal notation - ch = s.scanDigits(ch, 8, 3) - case 'x': - // hexademical notation - ch = s.scanDigits(s.next(), 16, 2) - case 'u': - // universal character name - ch = s.scanDigits(s.next(), 16, 4) - case 'U': - // universal character name - ch = s.scanDigits(s.next(), 16, 8) - default: - s.err("illegal char escape") - } - return ch -} - -// scanDigits scans a rune with the given base for n times. For example an -// octal notation \184 would yield in scanDigits(ch, 8, 3) -func (s *Scanner) scanDigits(ch rune, base, n int) rune { - start := n - for n > 0 && digitVal(ch) < base { - ch = s.next() - if ch == eof { - // If we see an EOF, we halt any more scanning of digits - // immediately. - break - } - - n-- - } - if n > 0 { - s.err("illegal char escape") - } - - if n != start && ch != eof { - // we scanned all digits, put the last non digit char back, - // only if we read anything at all - s.unread() - } - - return ch -} - -// scanIdentifier scans an identifier and returns the literal string -func (s *Scanner) scanIdentifier() string { - offs := s.srcPos.Offset - s.lastCharLen - ch := s.next() - for isLetter(ch) || isDigit(ch) || ch == '-' || ch == '.' { - ch = s.next() - } - - if ch != eof { - s.unread() // we got identifier, put back latest char - } - - return string(s.src[offs:s.srcPos.Offset]) -} - -// recentPosition returns the position of the character immediately after the -// character or token returned by the last call to Scan. -func (s *Scanner) recentPosition() (pos token.Pos) { - pos.Offset = s.srcPos.Offset - s.lastCharLen - switch { - case s.srcPos.Column > 0: - // common case: last character was not a '\n' - pos.Line = s.srcPos.Line - pos.Column = s.srcPos.Column - case s.lastLineLen > 0: - // last character was a '\n' - // (we cannot be at the beginning of the source - // since we have called next() at least once) - pos.Line = s.srcPos.Line - 1 - pos.Column = s.lastLineLen - default: - // at the beginning of the source - pos.Line = 1 - pos.Column = 1 - } - return -} - -// err prints the error of any scanning to s.Error function. If the function is -// not defined, by default it prints them to os.Stderr -func (s *Scanner) err(msg string) { - s.ErrorCount++ - pos := s.recentPosition() - - if s.Error != nil { - s.Error(pos, msg) - return - } - - fmt.Fprintf(os.Stderr, "%s: %s\n", pos, msg) -} - -// isHexadecimal returns true if the given rune is a letter -func isLetter(ch rune) bool { - return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch) -} - -// isDigit returns true if the given rune is a decimal digit -func isDigit(ch rune) bool { - return '0' <= ch && ch <= '9' || ch >= 0x80 && unicode.IsDigit(ch) -} - -// isDecimal returns true if the given rune is a decimal number -func isDecimal(ch rune) bool { - return '0' <= ch && ch <= '9' -} - -// isHexadecimal returns true if the given rune is an hexadecimal number -func isHexadecimal(ch rune) bool { - return '0' <= ch && ch <= '9' || 'a' <= ch && ch <= 'f' || 'A' <= ch && ch <= 'F' -} - -// isWhitespace returns true if the rune is a space, tab, newline or carriage return -func isWhitespace(ch rune) bool { - return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' -} - -// digitVal returns the integer value of a given octal,decimal or hexadecimal rune -func digitVal(ch rune) int { - switch { - case '0' <= ch && ch <= '9': - return int(ch - '0') - case 'a' <= ch && ch <= 'f': - return int(ch - 'a' + 10) - case 'A' <= ch && ch <= 'F': - return int(ch - 'A' + 10) - } - return 16 // larger than any legal digit val -} diff --git a/vendor/github.com/hashicorp/hcl/hcl/strconv/quote.go b/vendor/github.com/hashicorp/hcl/hcl/strconv/quote.go deleted file mode 100644 index 5f981eaa2f..0000000000 --- a/vendor/github.com/hashicorp/hcl/hcl/strconv/quote.go +++ /dev/null @@ -1,241 +0,0 @@ -package strconv - -import ( - "errors" - "unicode/utf8" -) - -// ErrSyntax indicates that a value does not have the right syntax for the target type. -var ErrSyntax = errors.New("invalid syntax") - -// Unquote interprets s as a single-quoted, double-quoted, -// or backquoted Go string literal, returning the string value -// that s quotes. (If s is single-quoted, it would be a Go -// character literal; Unquote returns the corresponding -// one-character string.) -func Unquote(s string) (t string, err error) { - n := len(s) - if n < 2 { - return "", ErrSyntax - } - quote := s[0] - if quote != s[n-1] { - return "", ErrSyntax - } - s = s[1 : n-1] - - if quote != '"' { - return "", ErrSyntax - } - if !contains(s, '$') && !contains(s, '{') && contains(s, '\n') { - return "", ErrSyntax - } - - // Is it trivial? Avoid allocation. - if !contains(s, '\\') && !contains(s, quote) && !contains(s, '$') { - switch quote { - case '"': - return s, nil - case '\'': - r, size := utf8.DecodeRuneInString(s) - if size == len(s) && (r != utf8.RuneError || size != 1) { - return s, nil - } - } - } - - var runeTmp [utf8.UTFMax]byte - buf := make([]byte, 0, 3*len(s)/2) // Try to avoid more allocations. - for len(s) > 0 { - // If we're starting a '${}' then let it through un-unquoted. - // Specifically: we don't unquote any characters within the `${}` - // section. - if s[0] == '$' && len(s) > 1 && s[1] == '{' { - buf = append(buf, '$', '{') - s = s[2:] - - // Continue reading until we find the closing brace, copying as-is - braces := 1 - for len(s) > 0 && braces > 0 { - r, size := utf8.DecodeRuneInString(s) - if r == utf8.RuneError { - return "", ErrSyntax - } - - s = s[size:] - - n := utf8.EncodeRune(runeTmp[:], r) - buf = append(buf, runeTmp[:n]...) - - switch r { - case '{': - braces++ - case '}': - braces-- - } - } - if braces != 0 { - return "", ErrSyntax - } - if len(s) == 0 { - // If there's no string left, we're done! - break - } else { - // If there's more left, we need to pop back up to the top of the loop - // in case there's another interpolation in this string. - continue - } - } - - if s[0] == '\n' { - return "", ErrSyntax - } - - c, multibyte, ss, err := unquoteChar(s, quote) - if err != nil { - return "", err - } - s = ss - if c < utf8.RuneSelf || !multibyte { - buf = append(buf, byte(c)) - } else { - n := utf8.EncodeRune(runeTmp[:], c) - buf = append(buf, runeTmp[:n]...) - } - if quote == '\'' && len(s) != 0 { - // single-quoted must be single character - return "", ErrSyntax - } - } - return string(buf), nil -} - -// contains reports whether the string contains the byte c. -func contains(s string, c byte) bool { - for i := 0; i < len(s); i++ { - if s[i] == c { - return true - } - } - return false -} - -func unhex(b byte) (v rune, ok bool) { - c := rune(b) - switch { - case '0' <= c && c <= '9': - return c - '0', true - case 'a' <= c && c <= 'f': - return c - 'a' + 10, true - case 'A' <= c && c <= 'F': - return c - 'A' + 10, true - } - return -} - -func unquoteChar(s string, quote byte) (value rune, multibyte bool, tail string, err error) { - // easy cases - switch c := s[0]; { - case c == quote && (quote == '\'' || quote == '"'): - err = ErrSyntax - return - case c >= utf8.RuneSelf: - r, size := utf8.DecodeRuneInString(s) - return r, true, s[size:], nil - case c != '\\': - return rune(s[0]), false, s[1:], nil - } - - // hard case: c is backslash - if len(s) <= 1 { - err = ErrSyntax - return - } - c := s[1] - s = s[2:] - - switch c { - case 'a': - value = '\a' - case 'b': - value = '\b' - case 'f': - value = '\f' - case 'n': - value = '\n' - case 'r': - value = '\r' - case 't': - value = '\t' - case 'v': - value = '\v' - case 'x', 'u', 'U': - n := 0 - switch c { - case 'x': - n = 2 - case 'u': - n = 4 - case 'U': - n = 8 - } - var v rune - if len(s) < n { - err = ErrSyntax - return - } - for j := 0; j < n; j++ { - x, ok := unhex(s[j]) - if !ok { - err = ErrSyntax - return - } - v = v<<4 | x - } - s = s[n:] - if c == 'x' { - // single-byte string, possibly not UTF-8 - value = v - break - } - if v > utf8.MaxRune { - err = ErrSyntax - return - } - value = v - multibyte = true - case '0', '1', '2', '3', '4', '5', '6', '7': - v := rune(c) - '0' - if len(s) < 2 { - err = ErrSyntax - return - } - for j := 0; j < 2; j++ { // one digit already; two more - x := rune(s[j]) - '0' - if x < 0 || x > 7 { - err = ErrSyntax - return - } - v = (v << 3) | x - } - s = s[2:] - if v > 255 { - err = ErrSyntax - return - } - value = v - case '\\': - value = '\\' - case '\'', '"': - if c != quote { - err = ErrSyntax - return - } - value = rune(c) - default: - err = ErrSyntax - return - } - tail = s - return -} diff --git a/vendor/github.com/hashicorp/hcl/hcl/token/position.go b/vendor/github.com/hashicorp/hcl/hcl/token/position.go deleted file mode 100644 index 59c1bb72d4..0000000000 --- a/vendor/github.com/hashicorp/hcl/hcl/token/position.go +++ /dev/null @@ -1,46 +0,0 @@ -package token - -import "fmt" - -// Pos describes an arbitrary source position -// including the file, line, and column location. -// A Position is valid if the line number is > 0. -type Pos struct { - Filename string // filename, if any - Offset int // offset, starting at 0 - Line int // line number, starting at 1 - Column int // column number, starting at 1 (character count) -} - -// IsValid returns true if the position is valid. -func (p *Pos) IsValid() bool { return p.Line > 0 } - -// String returns a string in one of several forms: -// -// file:line:column valid position with file name -// line:column valid position without file name -// file invalid position with file name -// - invalid position without file name -func (p Pos) String() string { - s := p.Filename - if p.IsValid() { - if s != "" { - s += ":" - } - s += fmt.Sprintf("%d:%d", p.Line, p.Column) - } - if s == "" { - s = "-" - } - return s -} - -// Before reports whether the position p is before u. -func (p Pos) Before(u Pos) bool { - return u.Offset > p.Offset || u.Line > p.Line -} - -// After reports whether the position p is after u. -func (p Pos) After(u Pos) bool { - return u.Offset < p.Offset || u.Line < p.Line -} diff --git a/vendor/github.com/hashicorp/hcl/hcl/token/token.go b/vendor/github.com/hashicorp/hcl/hcl/token/token.go deleted file mode 100644 index e37c0664ec..0000000000 --- a/vendor/github.com/hashicorp/hcl/hcl/token/token.go +++ /dev/null @@ -1,219 +0,0 @@ -// Package token defines constants representing the lexical tokens for HCL -// (HashiCorp Configuration Language) -package token - -import ( - "fmt" - "strconv" - "strings" - - hclstrconv "github.com/hashicorp/hcl/hcl/strconv" -) - -// Token defines a single HCL token which can be obtained via the Scanner -type Token struct { - Type Type - Pos Pos - Text string - JSON bool -} - -// Type is the set of lexical tokens of the HCL (HashiCorp Configuration Language) -type Type int - -const ( - // Special tokens - ILLEGAL Type = iota - EOF - COMMENT - - identifier_beg - IDENT // literals - literal_beg - NUMBER // 12345 - FLOAT // 123.45 - BOOL // true,false - STRING // "abc" - HEREDOC // < 0 { - // Pop the current item - n := len(frontier) - item := frontier[n-1] - frontier = frontier[:n-1] - - switch v := item.Val.(type) { - case *ast.ObjectType: - items, frontier = flattenObjectType(v, item, items, frontier) - case *ast.ListType: - items, frontier = flattenListType(v, item, items, frontier) - default: - items = append(items, item) - } - } - - // Reverse the list since the frontier model runs things backwards - for i := len(items)/2 - 1; i >= 0; i-- { - opp := len(items) - 1 - i - items[i], items[opp] = items[opp], items[i] - } - - // Done! Set the original items - list.Items = items - return n, true - }) -} - -func flattenListType( - ot *ast.ListType, - item *ast.ObjectItem, - items []*ast.ObjectItem, - frontier []*ast.ObjectItem) ([]*ast.ObjectItem, []*ast.ObjectItem) { - // If the list is empty, keep the original list - if len(ot.List) == 0 { - items = append(items, item) - return items, frontier - } - - // All the elements of this object must also be objects! - for _, subitem := range ot.List { - if _, ok := subitem.(*ast.ObjectType); !ok { - items = append(items, item) - return items, frontier - } - } - - // Great! We have a match go through all the items and flatten - for _, elem := range ot.List { - // Add it to the frontier so that we can recurse - frontier = append(frontier, &ast.ObjectItem{ - Keys: item.Keys, - Assign: item.Assign, - Val: elem, - LeadComment: item.LeadComment, - LineComment: item.LineComment, - }) - } - - return items, frontier -} - -func flattenObjectType( - ot *ast.ObjectType, - item *ast.ObjectItem, - items []*ast.ObjectItem, - frontier []*ast.ObjectItem) ([]*ast.ObjectItem, []*ast.ObjectItem) { - // If the list has no items we do not have to flatten anything - if ot.List.Items == nil { - items = append(items, item) - return items, frontier - } - - // All the elements of this object must also be objects! - for _, subitem := range ot.List.Items { - if _, ok := subitem.Val.(*ast.ObjectType); !ok { - items = append(items, item) - return items, frontier - } - } - - // Great! We have a match go through all the items and flatten - for _, subitem := range ot.List.Items { - // Copy the new key - keys := make([]*ast.ObjectKey, len(item.Keys)+len(subitem.Keys)) - copy(keys, item.Keys) - copy(keys[len(item.Keys):], subitem.Keys) - - // Add it to the frontier so that we can recurse - frontier = append(frontier, &ast.ObjectItem{ - Keys: keys, - Assign: item.Assign, - Val: subitem.Val, - LeadComment: item.LeadComment, - LineComment: item.LineComment, - }) - } - - return items, frontier -} diff --git a/vendor/github.com/hashicorp/hcl/json/parser/parser.go b/vendor/github.com/hashicorp/hcl/json/parser/parser.go deleted file mode 100644 index 125a5f0729..0000000000 --- a/vendor/github.com/hashicorp/hcl/json/parser/parser.go +++ /dev/null @@ -1,313 +0,0 @@ -package parser - -import ( - "errors" - "fmt" - - "github.com/hashicorp/hcl/hcl/ast" - hcltoken "github.com/hashicorp/hcl/hcl/token" - "github.com/hashicorp/hcl/json/scanner" - "github.com/hashicorp/hcl/json/token" -) - -type Parser struct { - sc *scanner.Scanner - - // Last read token - tok token.Token - commaPrev token.Token - - enableTrace bool - indent int - n int // buffer size (max = 1) -} - -func newParser(src []byte) *Parser { - return &Parser{ - sc: scanner.New(src), - } -} - -// Parse returns the fully parsed source and returns the abstract syntax tree. -func Parse(src []byte) (*ast.File, error) { - p := newParser(src) - return p.Parse() -} - -var errEofToken = errors.New("EOF token found") - -// Parse returns the fully parsed source and returns the abstract syntax tree. -func (p *Parser) Parse() (*ast.File, error) { - f := &ast.File{} - var err, scerr error - p.sc.Error = func(pos token.Pos, msg string) { - scerr = fmt.Errorf("%s: %s", pos, msg) - } - - // The root must be an object in JSON - object, err := p.object() - if scerr != nil { - return nil, scerr - } - if err != nil { - return nil, err - } - - // We make our final node an object list so it is more HCL compatible - f.Node = object.List - - // Flatten it, which finds patterns and turns them into more HCL-like - // AST trees. - flattenObjects(f.Node) - - return f, nil -} - -func (p *Parser) objectList() (*ast.ObjectList, error) { - defer un(trace(p, "ParseObjectList")) - node := &ast.ObjectList{} - - for { - n, err := p.objectItem() - if err == errEofToken { - break // we are finished - } - - // we don't return a nil node, because might want to use already - // collected items. - if err != nil { - return node, err - } - - node.Add(n) - - // Check for a followup comma. If it isn't a comma, then we're done - if tok := p.scan(); tok.Type != token.COMMA { - break - } - } - - return node, nil -} - -// objectItem parses a single object item -func (p *Parser) objectItem() (*ast.ObjectItem, error) { - defer un(trace(p, "ParseObjectItem")) - - keys, err := p.objectKey() - if err != nil { - return nil, err - } - - o := &ast.ObjectItem{ - Keys: keys, - } - - switch p.tok.Type { - case token.COLON: - pos := p.tok.Pos - o.Assign = hcltoken.Pos{ - Filename: pos.Filename, - Offset: pos.Offset, - Line: pos.Line, - Column: pos.Column, - } - - o.Val, err = p.objectValue() - if err != nil { - return nil, err - } - } - - return o, nil -} - -// objectKey parses an object key and returns a ObjectKey AST -func (p *Parser) objectKey() ([]*ast.ObjectKey, error) { - keyCount := 0 - keys := make([]*ast.ObjectKey, 0) - - for { - tok := p.scan() - switch tok.Type { - case token.EOF: - return nil, errEofToken - case token.STRING: - keyCount++ - keys = append(keys, &ast.ObjectKey{ - Token: p.tok.HCLToken(), - }) - case token.COLON: - // If we have a zero keycount it means that we never got - // an object key, i.e. `{ :`. This is a syntax error. - if keyCount == 0 { - return nil, fmt.Errorf("expected: STRING got: %s", p.tok.Type) - } - - // Done - return keys, nil - case token.ILLEGAL: - return nil, errors.New("illegal") - default: - return nil, fmt.Errorf("expected: STRING got: %s", p.tok.Type) - } - } -} - -// object parses any type of object, such as number, bool, string, object or -// list. -func (p *Parser) objectValue() (ast.Node, error) { - defer un(trace(p, "ParseObjectValue")) - tok := p.scan() - - switch tok.Type { - case token.NUMBER, token.FLOAT, token.BOOL, token.NULL, token.STRING: - return p.literalType() - case token.LBRACE: - return p.objectType() - case token.LBRACK: - return p.listType() - case token.EOF: - return nil, errEofToken - } - - return nil, fmt.Errorf("Expected object value, got unknown token: %+v", tok) -} - -// object parses any type of object, such as number, bool, string, object or -// list. -func (p *Parser) object() (*ast.ObjectType, error) { - defer un(trace(p, "ParseType")) - tok := p.scan() - - switch tok.Type { - case token.LBRACE: - return p.objectType() - case token.EOF: - return nil, errEofToken - } - - return nil, fmt.Errorf("Expected object, got unknown token: %+v", tok) -} - -// objectType parses an object type and returns a ObjectType AST -func (p *Parser) objectType() (*ast.ObjectType, error) { - defer un(trace(p, "ParseObjectType")) - - // we assume that the currently scanned token is a LBRACE - o := &ast.ObjectType{} - - l, err := p.objectList() - - // if we hit RBRACE, we are good to go (means we parsed all Items), if it's - // not a RBRACE, it's an syntax error and we just return it. - if err != nil && p.tok.Type != token.RBRACE { - return nil, err - } - - o.List = l - return o, nil -} - -// listType parses a list type and returns a ListType AST -func (p *Parser) listType() (*ast.ListType, error) { - defer un(trace(p, "ParseListType")) - - // we assume that the currently scanned token is a LBRACK - l := &ast.ListType{} - - for { - tok := p.scan() - switch tok.Type { - case token.NUMBER, token.FLOAT, token.STRING: - node, err := p.literalType() - if err != nil { - return nil, err - } - - l.Add(node) - case token.COMMA: - continue - case token.LBRACE: - node, err := p.objectType() - if err != nil { - return nil, err - } - - l.Add(node) - case token.BOOL: - // TODO(arslan) should we support? not supported by HCL yet - case token.LBRACK: - // TODO(arslan) should we support nested lists? Even though it's - // written in README of HCL, it's not a part of the grammar - // (not defined in parse.y) - case token.RBRACK: - // finished - return l, nil - default: - return nil, fmt.Errorf("unexpected token while parsing list: %s", tok.Type) - } - - } -} - -// literalType parses a literal type and returns a LiteralType AST -func (p *Parser) literalType() (*ast.LiteralType, error) { - defer un(trace(p, "ParseLiteral")) - - return &ast.LiteralType{ - Token: p.tok.HCLToken(), - }, nil -} - -// scan returns the next token from the underlying scanner. If a token has -// been unscanned then read that instead. -func (p *Parser) scan() token.Token { - // If we have a token on the buffer, then return it. - if p.n != 0 { - p.n = 0 - return p.tok - } - - p.tok = p.sc.Scan() - return p.tok -} - -// unscan pushes the previously read token back onto the buffer. -func (p *Parser) unscan() { - p.n = 1 -} - -// ---------------------------------------------------------------------------- -// Parsing support - -func (p *Parser) printTrace(a ...interface{}) { - if !p.enableTrace { - return - } - - const dots = ". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . " - const n = len(dots) - fmt.Printf("%5d:%3d: ", p.tok.Pos.Line, p.tok.Pos.Column) - - i := 2 * p.indent - for i > n { - fmt.Print(dots) - i -= n - } - // i <= n - fmt.Print(dots[0:i]) - fmt.Println(a...) -} - -func trace(p *Parser, msg string) *Parser { - p.printTrace(msg, "(") - p.indent++ - return p -} - -// Usage pattern: defer un(trace(p, "...")) -func un(p *Parser) { - p.indent-- - p.printTrace(")") -} diff --git a/vendor/github.com/hashicorp/hcl/json/scanner/scanner.go b/vendor/github.com/hashicorp/hcl/json/scanner/scanner.go deleted file mode 100644 index fe3f0f0950..0000000000 --- a/vendor/github.com/hashicorp/hcl/json/scanner/scanner.go +++ /dev/null @@ -1,451 +0,0 @@ -package scanner - -import ( - "bytes" - "fmt" - "os" - "unicode" - "unicode/utf8" - - "github.com/hashicorp/hcl/json/token" -) - -// eof represents a marker rune for the end of the reader. -const eof = rune(0) - -// Scanner defines a lexical scanner -type Scanner struct { - buf *bytes.Buffer // Source buffer for advancing and scanning - src []byte // Source buffer for immutable access - - // Source Position - srcPos token.Pos // current position - prevPos token.Pos // previous position, used for peek() method - - lastCharLen int // length of last character in bytes - lastLineLen int // length of last line in characters (for correct column reporting) - - tokStart int // token text start position - tokEnd int // token text end position - - // Error is called for each error encountered. If no Error - // function is set, the error is reported to os.Stderr. - Error func(pos token.Pos, msg string) - - // ErrorCount is incremented by one for each error encountered. - ErrorCount int - - // tokPos is the start position of most recently scanned token; set by - // Scan. The Filename field is always left untouched by the Scanner. If - // an error is reported (via Error) and Position is invalid, the scanner is - // not inside a token. - tokPos token.Pos -} - -// New creates and initializes a new instance of Scanner using src as -// its source content. -func New(src []byte) *Scanner { - // even though we accept a src, we read from a io.Reader compatible type - // (*bytes.Buffer). So in the future we might easily change it to streaming - // read. - b := bytes.NewBuffer(src) - s := &Scanner{ - buf: b, - src: src, - } - - // srcPosition always starts with 1 - s.srcPos.Line = 1 - return s -} - -// next reads the next rune from the bufferred reader. Returns the rune(0) if -// an error occurs (or io.EOF is returned). -func (s *Scanner) next() rune { - ch, size, err := s.buf.ReadRune() - if err != nil { - // advance for error reporting - s.srcPos.Column++ - s.srcPos.Offset += size - s.lastCharLen = size - return eof - } - - if ch == utf8.RuneError && size == 1 { - s.srcPos.Column++ - s.srcPos.Offset += size - s.lastCharLen = size - s.err("illegal UTF-8 encoding") - return ch - } - - // remember last position - s.prevPos = s.srcPos - - s.srcPos.Column++ - s.lastCharLen = size - s.srcPos.Offset += size - - if ch == '\n' { - s.srcPos.Line++ - s.lastLineLen = s.srcPos.Column - s.srcPos.Column = 0 - } - - // debug - // fmt.Printf("ch: %q, offset:column: %d:%d\n", ch, s.srcPos.Offset, s.srcPos.Column) - return ch -} - -// unread unreads the previous read Rune and updates the source position -func (s *Scanner) unread() { - if err := s.buf.UnreadRune(); err != nil { - panic(err) // this is user fault, we should catch it - } - s.srcPos = s.prevPos // put back last position -} - -// peek returns the next rune without advancing the reader. -func (s *Scanner) peek() rune { - peek, _, err := s.buf.ReadRune() - if err != nil { - return eof - } - - s.buf.UnreadRune() - return peek -} - -// Scan scans the next token and returns the token. -func (s *Scanner) Scan() token.Token { - ch := s.next() - - // skip white space - for isWhitespace(ch) { - ch = s.next() - } - - var tok token.Type - - // token text markings - s.tokStart = s.srcPos.Offset - s.lastCharLen - - // token position, initial next() is moving the offset by one(size of rune - // actually), though we are interested with the starting point - s.tokPos.Offset = s.srcPos.Offset - s.lastCharLen - if s.srcPos.Column > 0 { - // common case: last character was not a '\n' - s.tokPos.Line = s.srcPos.Line - s.tokPos.Column = s.srcPos.Column - } else { - // last character was a '\n' - // (we cannot be at the beginning of the source - // since we have called next() at least once) - s.tokPos.Line = s.srcPos.Line - 1 - s.tokPos.Column = s.lastLineLen - } - - switch { - case isLetter(ch): - lit := s.scanIdentifier() - if lit == "true" || lit == "false" { - tok = token.BOOL - } else if lit == "null" { - tok = token.NULL - } else { - s.err("illegal char") - } - case isDecimal(ch): - tok = s.scanNumber(ch) - default: - switch ch { - case eof: - tok = token.EOF - case '"': - tok = token.STRING - s.scanString() - case '.': - tok = token.PERIOD - ch = s.peek() - if isDecimal(ch) { - tok = token.FLOAT - ch = s.scanMantissa(ch) - ch = s.scanExponent(ch) - } - case '[': - tok = token.LBRACK - case ']': - tok = token.RBRACK - case '{': - tok = token.LBRACE - case '}': - tok = token.RBRACE - case ',': - tok = token.COMMA - case ':': - tok = token.COLON - case '-': - if isDecimal(s.peek()) { - ch := s.next() - tok = s.scanNumber(ch) - } else { - s.err("illegal char") - } - default: - s.err("illegal char: " + string(ch)) - } - } - - // finish token ending - s.tokEnd = s.srcPos.Offset - - // create token literal - var tokenText string - if s.tokStart >= 0 { - tokenText = string(s.src[s.tokStart:s.tokEnd]) - } - s.tokStart = s.tokEnd // ensure idempotency of tokenText() call - - return token.Token{ - Type: tok, - Pos: s.tokPos, - Text: tokenText, - } -} - -// scanNumber scans a HCL number definition starting with the given rune -func (s *Scanner) scanNumber(ch rune) token.Type { - zero := ch == '0' - pos := s.srcPos - - s.scanMantissa(ch) - ch = s.next() // seek forward - if ch == 'e' || ch == 'E' { - ch = s.scanExponent(ch) - return token.FLOAT - } - - if ch == '.' { - ch = s.scanFraction(ch) - if ch == 'e' || ch == 'E' { - ch = s.next() - ch = s.scanExponent(ch) - } - return token.FLOAT - } - - if ch != eof { - s.unread() - } - - // If we have a larger number and this is zero, error - if zero && pos != s.srcPos { - s.err("numbers cannot start with 0") - } - - return token.NUMBER -} - -// scanMantissa scans the mantissa beginning from the rune. It returns the next -// non decimal rune. It's used to determine wheter it's a fraction or exponent. -func (s *Scanner) scanMantissa(ch rune) rune { - scanned := false - for isDecimal(ch) { - ch = s.next() - scanned = true - } - - if scanned && ch != eof { - s.unread() - } - return ch -} - -// scanFraction scans the fraction after the '.' rune -func (s *Scanner) scanFraction(ch rune) rune { - if ch == '.' { - ch = s.peek() // we peek just to see if we can move forward - ch = s.scanMantissa(ch) - } - return ch -} - -// scanExponent scans the remaining parts of an exponent after the 'e' or 'E' -// rune. -func (s *Scanner) scanExponent(ch rune) rune { - if ch == 'e' || ch == 'E' { - ch = s.next() - if ch == '-' || ch == '+' { - ch = s.next() - } - ch = s.scanMantissa(ch) - } - return ch -} - -// scanString scans a quoted string -func (s *Scanner) scanString() { - braces := 0 - for { - // '"' opening already consumed - // read character after quote - ch := s.next() - - if ch == '\n' || ch < 0 || ch == eof { - s.err("literal not terminated") - return - } - - if ch == '"' { - break - } - - // If we're going into a ${} then we can ignore quotes for awhile - if braces == 0 && ch == '$' && s.peek() == '{' { - braces++ - s.next() - } else if braces > 0 && ch == '{' { - braces++ - } - if braces > 0 && ch == '}' { - braces-- - } - - if ch == '\\' { - s.scanEscape() - } - } - - return -} - -// scanEscape scans an escape sequence -func (s *Scanner) scanEscape() rune { - // http://en.cppreference.com/w/cpp/language/escape - ch := s.next() // read character after '/' - switch ch { - case 'a', 'b', 'f', 'n', 'r', 't', 'v', '\\', '"': - // nothing to do - case '0', '1', '2', '3', '4', '5', '6', '7': - // octal notation - ch = s.scanDigits(ch, 8, 3) - case 'x': - // hexademical notation - ch = s.scanDigits(s.next(), 16, 2) - case 'u': - // universal character name - ch = s.scanDigits(s.next(), 16, 4) - case 'U': - // universal character name - ch = s.scanDigits(s.next(), 16, 8) - default: - s.err("illegal char escape") - } - return ch -} - -// scanDigits scans a rune with the given base for n times. For example an -// octal notation \184 would yield in scanDigits(ch, 8, 3) -func (s *Scanner) scanDigits(ch rune, base, n int) rune { - for n > 0 && digitVal(ch) < base { - ch = s.next() - n-- - } - if n > 0 { - s.err("illegal char escape") - } - - // we scanned all digits, put the last non digit char back - s.unread() - return ch -} - -// scanIdentifier scans an identifier and returns the literal string -func (s *Scanner) scanIdentifier() string { - offs := s.srcPos.Offset - s.lastCharLen - ch := s.next() - for isLetter(ch) || isDigit(ch) || ch == '-' { - ch = s.next() - } - - if ch != eof { - s.unread() // we got identifier, put back latest char - } - - return string(s.src[offs:s.srcPos.Offset]) -} - -// recentPosition returns the position of the character immediately after the -// character or token returned by the last call to Scan. -func (s *Scanner) recentPosition() (pos token.Pos) { - pos.Offset = s.srcPos.Offset - s.lastCharLen - switch { - case s.srcPos.Column > 0: - // common case: last character was not a '\n' - pos.Line = s.srcPos.Line - pos.Column = s.srcPos.Column - case s.lastLineLen > 0: - // last character was a '\n' - // (we cannot be at the beginning of the source - // since we have called next() at least once) - pos.Line = s.srcPos.Line - 1 - pos.Column = s.lastLineLen - default: - // at the beginning of the source - pos.Line = 1 - pos.Column = 1 - } - return -} - -// err prints the error of any scanning to s.Error function. If the function is -// not defined, by default it prints them to os.Stderr -func (s *Scanner) err(msg string) { - s.ErrorCount++ - pos := s.recentPosition() - - if s.Error != nil { - s.Error(pos, msg) - return - } - - fmt.Fprintf(os.Stderr, "%s: %s\n", pos, msg) -} - -// isHexadecimal returns true if the given rune is a letter -func isLetter(ch rune) bool { - return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' || ch >= 0x80 && unicode.IsLetter(ch) -} - -// isHexadecimal returns true if the given rune is a decimal digit -func isDigit(ch rune) bool { - return '0' <= ch && ch <= '9' || ch >= 0x80 && unicode.IsDigit(ch) -} - -// isHexadecimal returns true if the given rune is a decimal number -func isDecimal(ch rune) bool { - return '0' <= ch && ch <= '9' -} - -// isHexadecimal returns true if the given rune is an hexadecimal number -func isHexadecimal(ch rune) bool { - return '0' <= ch && ch <= '9' || 'a' <= ch && ch <= 'f' || 'A' <= ch && ch <= 'F' -} - -// isWhitespace returns true if the rune is a space, tab, newline or carriage return -func isWhitespace(ch rune) bool { - return ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' -} - -// digitVal returns the integer value of a given octal,decimal or hexadecimal rune -func digitVal(ch rune) int { - switch { - case '0' <= ch && ch <= '9': - return int(ch - '0') - case 'a' <= ch && ch <= 'f': - return int(ch - 'a' + 10) - case 'A' <= ch && ch <= 'F': - return int(ch - 'A' + 10) - } - return 16 // larger than any legal digit val -} diff --git a/vendor/github.com/hashicorp/hcl/json/token/position.go b/vendor/github.com/hashicorp/hcl/json/token/position.go deleted file mode 100644 index 59c1bb72d4..0000000000 --- a/vendor/github.com/hashicorp/hcl/json/token/position.go +++ /dev/null @@ -1,46 +0,0 @@ -package token - -import "fmt" - -// Pos describes an arbitrary source position -// including the file, line, and column location. -// A Position is valid if the line number is > 0. -type Pos struct { - Filename string // filename, if any - Offset int // offset, starting at 0 - Line int // line number, starting at 1 - Column int // column number, starting at 1 (character count) -} - -// IsValid returns true if the position is valid. -func (p *Pos) IsValid() bool { return p.Line > 0 } - -// String returns a string in one of several forms: -// -// file:line:column valid position with file name -// line:column valid position without file name -// file invalid position with file name -// - invalid position without file name -func (p Pos) String() string { - s := p.Filename - if p.IsValid() { - if s != "" { - s += ":" - } - s += fmt.Sprintf("%d:%d", p.Line, p.Column) - } - if s == "" { - s = "-" - } - return s -} - -// Before reports whether the position p is before u. -func (p Pos) Before(u Pos) bool { - return u.Offset > p.Offset || u.Line > p.Line -} - -// After reports whether the position p is after u. -func (p Pos) After(u Pos) bool { - return u.Offset < p.Offset || u.Line < p.Line -} diff --git a/vendor/github.com/hashicorp/hcl/json/token/token.go b/vendor/github.com/hashicorp/hcl/json/token/token.go deleted file mode 100644 index 95a0c3eee6..0000000000 --- a/vendor/github.com/hashicorp/hcl/json/token/token.go +++ /dev/null @@ -1,118 +0,0 @@ -package token - -import ( - "fmt" - "strconv" - - hcltoken "github.com/hashicorp/hcl/hcl/token" -) - -// Token defines a single HCL token which can be obtained via the Scanner -type Token struct { - Type Type - Pos Pos - Text string -} - -// Type is the set of lexical tokens of the HCL (HashiCorp Configuration Language) -type Type int - -const ( - // Special tokens - ILLEGAL Type = iota - EOF - - identifier_beg - literal_beg - NUMBER // 12345 - FLOAT // 123.45 - BOOL // true,false - STRING // "abc" - NULL // null - literal_end - identifier_end - - operator_beg - LBRACK // [ - LBRACE // { - COMMA // , - PERIOD // . - COLON // : - - RBRACK // ] - RBRACE // } - - operator_end -) - -var tokens = [...]string{ - ILLEGAL: "ILLEGAL", - - EOF: "EOF", - - NUMBER: "NUMBER", - FLOAT: "FLOAT", - BOOL: "BOOL", - STRING: "STRING", - NULL: "NULL", - - LBRACK: "LBRACK", - LBRACE: "LBRACE", - COMMA: "COMMA", - PERIOD: "PERIOD", - COLON: "COLON", - - RBRACK: "RBRACK", - RBRACE: "RBRACE", -} - -// String returns the string corresponding to the token tok. -func (t Type) String() string { - s := "" - if 0 <= t && t < Type(len(tokens)) { - s = tokens[t] - } - if s == "" { - s = "token(" + strconv.Itoa(int(t)) + ")" - } - return s -} - -// IsIdentifier returns true for tokens corresponding to identifiers and basic -// type literals; it returns false otherwise. -func (t Type) IsIdentifier() bool { return identifier_beg < t && t < identifier_end } - -// IsLiteral returns true for tokens corresponding to basic type literals; it -// returns false otherwise. -func (t Type) IsLiteral() bool { return literal_beg < t && t < literal_end } - -// IsOperator returns true for tokens corresponding to operators and -// delimiters; it returns false otherwise. -func (t Type) IsOperator() bool { return operator_beg < t && t < operator_end } - -// String returns the token's literal text. Note that this is only -// applicable for certain token types, such as token.IDENT, -// token.STRING, etc.. -func (t Token) String() string { - return fmt.Sprintf("%s %s %s", t.Pos.String(), t.Type.String(), t.Text) -} - -// HCLToken converts this token to an HCL token. -// -// The token type must be a literal type or this will panic. -func (t Token) HCLToken() hcltoken.Token { - switch t.Type { - case BOOL: - return hcltoken.Token{Type: hcltoken.BOOL, Text: t.Text} - case FLOAT: - return hcltoken.Token{Type: hcltoken.FLOAT, Text: t.Text} - case NULL: - return hcltoken.Token{Type: hcltoken.STRING, Text: ""} - case NUMBER: - return hcltoken.Token{Type: hcltoken.NUMBER, Text: t.Text} - case STRING: - return hcltoken.Token{Type: hcltoken.STRING, Text: t.Text, JSON: true} - default: - panic(fmt.Sprintf("unimplemented HCLToken for type: %s", t.Type)) - } -} diff --git a/vendor/github.com/hashicorp/hcl/lex.go b/vendor/github.com/hashicorp/hcl/lex.go deleted file mode 100644 index d9993c2928..0000000000 --- a/vendor/github.com/hashicorp/hcl/lex.go +++ /dev/null @@ -1,38 +0,0 @@ -package hcl - -import ( - "unicode" - "unicode/utf8" -) - -type lexModeValue byte - -const ( - lexModeUnknown lexModeValue = iota - lexModeHcl - lexModeJson -) - -// lexMode returns whether we're going to be parsing in JSON -// mode or HCL mode. -func lexMode(v []byte) lexModeValue { - var ( - r rune - w int - offset int - ) - - for { - r, w = utf8.DecodeRune(v[offset:]) - offset += w - if unicode.IsSpace(r) { - continue - } - if r == '{' { - return lexModeJson - } - break - } - - return lexModeHcl -} diff --git a/vendor/github.com/hashicorp/hcl/parse.go b/vendor/github.com/hashicorp/hcl/parse.go deleted file mode 100644 index 1fca53c4ce..0000000000 --- a/vendor/github.com/hashicorp/hcl/parse.go +++ /dev/null @@ -1,39 +0,0 @@ -package hcl - -import ( - "fmt" - - "github.com/hashicorp/hcl/hcl/ast" - hclParser "github.com/hashicorp/hcl/hcl/parser" - jsonParser "github.com/hashicorp/hcl/json/parser" -) - -// ParseBytes accepts as input byte slice and returns ast tree. -// -// Input can be either JSON or HCL -func ParseBytes(in []byte) (*ast.File, error) { - return parse(in) -} - -// ParseString accepts input as a string and returns ast tree. -func ParseString(input string) (*ast.File, error) { - return parse([]byte(input)) -} - -func parse(in []byte) (*ast.File, error) { - switch lexMode(in) { - case lexModeHcl: - return hclParser.Parse(in) - case lexModeJson: - return jsonParser.Parse(in) - } - - return nil, fmt.Errorf("unknown config format") -} - -// Parse parses the given input and returns the root object. -// -// The input format can be either HCL or JSON. -func Parse(input string) (*ast.File, error) { - return parse([]byte(input)) -} diff --git a/vendor/github.com/klauspost/compress/.goreleaser.yml b/vendor/github.com/klauspost/compress/.goreleaser.yml index a22953805c..4528059ca6 100644 --- a/vendor/github.com/klauspost/compress/.goreleaser.yml +++ b/vendor/github.com/klauspost/compress/.goreleaser.yml @@ -1,5 +1,5 @@ -# This is an example goreleaser.yaml file with some sane defaults. -# Make sure to check the documentation at http://goreleaser.com +version: 2 + before: hooks: - ./gen.sh @@ -99,7 +99,7 @@ archives: checksum: name_template: 'checksums.txt' snapshot: - name_template: "{{ .Tag }}-next" + version_template: "{{ .Tag }}-next" changelog: sort: asc filters: diff --git a/vendor/github.com/klauspost/compress/README.md b/vendor/github.com/klauspost/compress/README.md index 05c7359e48..de264c85a5 100644 --- a/vendor/github.com/klauspost/compress/README.md +++ b/vendor/github.com/klauspost/compress/README.md @@ -16,6 +16,27 @@ This package provides various compression algorithms. # changelog +* Sep 23rd, 2024 - [1.17.10](https://github.com/klauspost/compress/releases/tag/v1.17.10) + * gzhttp: Add TransportAlwaysDecompress option. https://github.com/klauspost/compress/pull/978 + * gzhttp: Add supported decompress request body by @mirecl in https://github.com/klauspost/compress/pull/1002 + * s2: Add EncodeBuffer buffer recycling callback https://github.com/klauspost/compress/pull/982 + * zstd: Improve memory usage on small streaming encodes https://github.com/klauspost/compress/pull/1007 + * flate: read data written with partial flush by @vajexal in https://github.com/klauspost/compress/pull/996 + +* Jun 12th, 2024 - [1.17.9](https://github.com/klauspost/compress/releases/tag/v1.17.9) + * s2: Reduce ReadFrom temporary allocations https://github.com/klauspost/compress/pull/949 + * flate, zstd: Shave some bytes off amd64 matchLen by @greatroar in https://github.com/klauspost/compress/pull/963 + * Upgrade zip/zlib to 1.22.4 upstream https://github.com/klauspost/compress/pull/970 https://github.com/klauspost/compress/pull/971 + * zstd: BuildDict fails with RLE table https://github.com/klauspost/compress/pull/951 + +* Apr 9th, 2024 - [1.17.8](https://github.com/klauspost/compress/releases/tag/v1.17.8) + * zstd: Reject blocks where reserved values are not 0 https://github.com/klauspost/compress/pull/885 + * zstd: Add RLE detection+encoding https://github.com/klauspost/compress/pull/938 + +* Feb 21st, 2024 - [1.17.7](https://github.com/klauspost/compress/releases/tag/v1.17.7) + * s2: Add AsyncFlush method: Complete the block without flushing by @Jille in https://github.com/klauspost/compress/pull/927 + * s2: Fix literal+repeat exceeds dst crash https://github.com/klauspost/compress/pull/930 + * Feb 5th, 2024 - [1.17.6](https://github.com/klauspost/compress/releases/tag/v1.17.6) * zstd: Fix incorrect repeat coding in best mode https://github.com/klauspost/compress/pull/923 * s2: Fix DecodeConcurrent deadlock on errors https://github.com/klauspost/compress/pull/925 @@ -81,7 +102,7 @@ https://github.com/klauspost/compress/pull/919 https://github.com/klauspost/comp * zstd: Various minor improvements by @greatroar in https://github.com/klauspost/compress/pull/788 https://github.com/klauspost/compress/pull/794 https://github.com/klauspost/compress/pull/795 * s2: Fix huge block overflow https://github.com/klauspost/compress/pull/779 * s2: Allow CustomEncoder fallback https://github.com/klauspost/compress/pull/780 - * gzhttp: Suppport ResponseWriter Unwrap() in gzhttp handler by @jgimenez in https://github.com/klauspost/compress/pull/799 + * gzhttp: Support ResponseWriter Unwrap() in gzhttp handler by @jgimenez in https://github.com/klauspost/compress/pull/799 * Mar 13, 2023 - [v1.16.1](https://github.com/klauspost/compress/releases/tag/v1.16.1) * zstd: Speed up + improve best encoder by @greatroar in https://github.com/klauspost/compress/pull/776 @@ -136,7 +157,7 @@ https://github.com/klauspost/compress/pull/919 https://github.com/klauspost/comp * zstd: Add [WithDecodeAllCapLimit](https://pkg.go.dev/github.com/klauspost/compress@v1.15.10/zstd#WithDecodeAllCapLimit) https://github.com/klauspost/compress/pull/649 * Add Go 1.19 - deprecate Go 1.16 https://github.com/klauspost/compress/pull/651 * flate: Improve level 5+6 compression https://github.com/klauspost/compress/pull/656 - * zstd: Improve "better" compresssion https://github.com/klauspost/compress/pull/657 + * zstd: Improve "better" compression https://github.com/klauspost/compress/pull/657 * s2: Improve "best" compression https://github.com/klauspost/compress/pull/658 * s2: Improve "better" compression. https://github.com/klauspost/compress/pull/635 * s2: Slightly faster non-assembly decompression https://github.com/klauspost/compress/pull/646 @@ -339,7 +360,7 @@ While the release has been extensively tested, it is recommended to testing when * s2: Fix binaries. * Feb 25, 2021 (v1.11.8) - * s2: Fixed occational out-of-bounds write on amd64. Upgrade recommended. + * s2: Fixed occasional out-of-bounds write on amd64. Upgrade recommended. * s2: Add AMD64 assembly for better mode. 25-50% faster. [#315](https://github.com/klauspost/compress/pull/315) * s2: Less upfront decoder allocation. [#322](https://github.com/klauspost/compress/pull/322) * zstd: Faster "compression" of incompressible data. [#314](https://github.com/klauspost/compress/pull/314) @@ -518,7 +539,7 @@ While the release has been extensively tested, it is recommended to testing when * Feb 19, 2016: Faster bit writer, level -2 is 15% faster, level 1 is 4% faster. * Feb 19, 2016: Handle small payloads faster in level 1-3. * Feb 19, 2016: Added faster level 2 + 3 compression modes. -* Feb 19, 2016: [Rebalanced compression levels](https://blog.klauspost.com/rebalancing-deflate-compression-levels/), so there is a more even progresssion in terms of compression. New default level is 5. +* Feb 19, 2016: [Rebalanced compression levels](https://blog.klauspost.com/rebalancing-deflate-compression-levels/), so there is a more even progression in terms of compression. New default level is 5. * Feb 14, 2016: Snappy: Merge upstream changes. * Feb 14, 2016: Snappy: Fix aggressive skipping. * Feb 14, 2016: Snappy: Update benchmark. diff --git a/vendor/github.com/klauspost/compress/fse/decompress.go b/vendor/github.com/klauspost/compress/fse/decompress.go index cc05d0f7ea..0c7dd4ffef 100644 --- a/vendor/github.com/klauspost/compress/fse/decompress.go +++ b/vendor/github.com/klauspost/compress/fse/decompress.go @@ -15,7 +15,7 @@ const ( // It is possible, but by no way guaranteed that corrupt data will // return an error. // It is up to the caller to verify integrity of the returned data. -// Use a predefined Scrach to set maximum acceptable output size. +// Use a predefined Scratch to set maximum acceptable output size. func Decompress(b []byte, s *Scratch) ([]byte, error) { s, err := s.prepare(b) if err != nil { diff --git a/vendor/github.com/klauspost/compress/huff0/decompress.go b/vendor/github.com/klauspost/compress/huff0/decompress.go index 54bd08b25c..0f56b02d74 100644 --- a/vendor/github.com/klauspost/compress/huff0/decompress.go +++ b/vendor/github.com/klauspost/compress/huff0/decompress.go @@ -1136,7 +1136,7 @@ func (s *Scratch) matches(ct cTable, w io.Writer) { errs++ } if errs > 0 { - fmt.Fprintf(w, "%d errros in base, stopping\n", errs) + fmt.Fprintf(w, "%d errors in base, stopping\n", errs) continue } // Ensure that all combinations are covered. @@ -1152,7 +1152,7 @@ func (s *Scratch) matches(ct cTable, w io.Writer) { errs++ } if errs > 20 { - fmt.Fprintf(w, "%d errros, stopping\n", errs) + fmt.Fprintf(w, "%d errors, stopping\n", errs) break } } diff --git a/vendor/github.com/klauspost/compress/zstd/blockdec.go b/vendor/github.com/klauspost/compress/zstd/blockdec.go index 03744fbc76..9c28840c3b 100644 --- a/vendor/github.com/klauspost/compress/zstd/blockdec.go +++ b/vendor/github.com/klauspost/compress/zstd/blockdec.go @@ -598,7 +598,9 @@ func (b *blockDec) prepareSequences(in []byte, hist *history) (err error) { printf("RLE set to 0x%x, code: %v", symb, v) } case compModeFSE: - println("Reading table for", tableIndex(i)) + if debugDecoder { + println("Reading table for", tableIndex(i)) + } if seq.fse == nil || seq.fse.preDefined { seq.fse = fseDecoderPool.Get().(*fseDecoder) } diff --git a/vendor/github.com/klauspost/compress/zstd/enc_better.go b/vendor/github.com/klauspost/compress/zstd/enc_better.go index a4f5bf91fc..84a79fde76 100644 --- a/vendor/github.com/klauspost/compress/zstd/enc_better.go +++ b/vendor/github.com/klauspost/compress/zstd/enc_better.go @@ -179,9 +179,9 @@ encodeLoop: if repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>(repOff*8)) { // Consider history as well. var seq seq - lenght := 4 + e.matchlen(s+4+repOff, repIndex+4, src) + length := 4 + e.matchlen(s+4+repOff, repIndex+4, src) - seq.matchLen = uint32(lenght - zstdMinMatch) + seq.matchLen = uint32(length - zstdMinMatch) // We might be able to match backwards. // Extend as long as we can. @@ -210,12 +210,12 @@ encodeLoop: // Index match start+1 (long) -> s - 1 index0 := s + repOff - s += lenght + repOff + s += length + repOff nextEmit = s if s >= sLimit { if debugEncoder { - println("repeat ended", s, lenght) + println("repeat ended", s, length) } break encodeLoop @@ -241,9 +241,9 @@ encodeLoop: if false && repIndex >= 0 && load6432(src, repIndex) == load6432(src, s+repOff) { // Consider history as well. var seq seq - lenght := 8 + e.matchlen(s+8+repOff2, repIndex+8, src) + length := 8 + e.matchlen(s+8+repOff2, repIndex+8, src) - seq.matchLen = uint32(lenght - zstdMinMatch) + seq.matchLen = uint32(length - zstdMinMatch) // We might be able to match backwards. // Extend as long as we can. @@ -270,11 +270,11 @@ encodeLoop: } blk.sequences = append(blk.sequences, seq) - s += lenght + repOff2 + s += length + repOff2 nextEmit = s if s >= sLimit { if debugEncoder { - println("repeat ended", s, lenght) + println("repeat ended", s, length) } break encodeLoop @@ -708,9 +708,9 @@ encodeLoop: if repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>(repOff*8)) { // Consider history as well. var seq seq - lenght := 4 + e.matchlen(s+4+repOff, repIndex+4, src) + length := 4 + e.matchlen(s+4+repOff, repIndex+4, src) - seq.matchLen = uint32(lenght - zstdMinMatch) + seq.matchLen = uint32(length - zstdMinMatch) // We might be able to match backwards. // Extend as long as we can. @@ -738,12 +738,12 @@ encodeLoop: blk.sequences = append(blk.sequences, seq) // Index match start+1 (long) -> s - 1 - s += lenght + repOff + s += length + repOff nextEmit = s if s >= sLimit { if debugEncoder { - println("repeat ended", s, lenght) + println("repeat ended", s, length) } break encodeLoop @@ -772,9 +772,9 @@ encodeLoop: if false && repIndex >= 0 && load6432(src, repIndex) == load6432(src, s+repOff) { // Consider history as well. var seq seq - lenght := 8 + e.matchlen(s+8+repOff2, repIndex+8, src) + length := 8 + e.matchlen(s+8+repOff2, repIndex+8, src) - seq.matchLen = uint32(lenght - zstdMinMatch) + seq.matchLen = uint32(length - zstdMinMatch) // We might be able to match backwards. // Extend as long as we can. @@ -801,11 +801,11 @@ encodeLoop: } blk.sequences = append(blk.sequences, seq) - s += lenght + repOff2 + s += length + repOff2 nextEmit = s if s >= sLimit { if debugEncoder { - println("repeat ended", s, lenght) + println("repeat ended", s, length) } break encodeLoop diff --git a/vendor/github.com/klauspost/compress/zstd/enc_dfast.go b/vendor/github.com/klauspost/compress/zstd/enc_dfast.go index a154c18f74..d36be7bd8c 100644 --- a/vendor/github.com/klauspost/compress/zstd/enc_dfast.go +++ b/vendor/github.com/klauspost/compress/zstd/enc_dfast.go @@ -138,9 +138,9 @@ encodeLoop: if repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>(repOff*8)) { // Consider history as well. var seq seq - lenght := 4 + e.matchlen(s+4+repOff, repIndex+4, src) + length := 4 + e.matchlen(s+4+repOff, repIndex+4, src) - seq.matchLen = uint32(lenght - zstdMinMatch) + seq.matchLen = uint32(length - zstdMinMatch) // We might be able to match backwards. // Extend as long as we can. @@ -166,11 +166,11 @@ encodeLoop: println("repeat sequence", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) - s += lenght + repOff + s += length + repOff nextEmit = s if s >= sLimit { if debugEncoder { - println("repeat ended", s, lenght) + println("repeat ended", s, length) } break encodeLoop @@ -798,9 +798,9 @@ encodeLoop: if repIndex >= 0 && load3232(src, repIndex) == uint32(cv>>(repOff*8)) { // Consider history as well. var seq seq - lenght := 4 + e.matchlen(s+4+repOff, repIndex+4, src) + length := 4 + e.matchlen(s+4+repOff, repIndex+4, src) - seq.matchLen = uint32(lenght - zstdMinMatch) + seq.matchLen = uint32(length - zstdMinMatch) // We might be able to match backwards. // Extend as long as we can. @@ -826,11 +826,11 @@ encodeLoop: println("repeat sequence", seq, "next s:", s) } blk.sequences = append(blk.sequences, seq) - s += lenght + repOff + s += length + repOff nextEmit = s if s >= sLimit { if debugEncoder { - println("repeat ended", s, lenght) + println("repeat ended", s, length) } break encodeLoop diff --git a/vendor/github.com/klauspost/compress/zstd/encoder.go b/vendor/github.com/klauspost/compress/zstd/encoder.go index 72af7ef0fe..8f8223cd3a 100644 --- a/vendor/github.com/klauspost/compress/zstd/encoder.go +++ b/vendor/github.com/klauspost/compress/zstd/encoder.go @@ -6,6 +6,7 @@ package zstd import ( "crypto/rand" + "errors" "fmt" "io" "math" @@ -149,6 +150,9 @@ func (e *Encoder) ResetContentSize(w io.Writer, size int64) { // and write CRC if requested. func (e *Encoder) Write(p []byte) (n int, err error) { s := &e.state + if s.eofWritten { + return 0, ErrEncoderClosed + } for len(p) > 0 { if len(p)+len(s.filling) < e.o.blockSize { if e.o.crc { @@ -202,7 +206,7 @@ func (e *Encoder) nextBlock(final bool) error { return nil } if final && len(s.filling) > 0 { - s.current = e.EncodeAll(s.filling, s.current[:0]) + s.current = e.encodeAll(s.encoder, s.filling, s.current[:0]) var n2 int n2, s.err = s.w.Write(s.current) if s.err != nil { @@ -288,6 +292,9 @@ func (e *Encoder) nextBlock(final bool) error { s.filling, s.current, s.previous = s.previous[:0], s.filling, s.current s.nInput += int64(len(s.current)) s.wg.Add(1) + if final { + s.eofWritten = true + } go func(src []byte) { if debugEncoder { println("Adding block,", len(src), "bytes, final:", final) @@ -303,9 +310,6 @@ func (e *Encoder) nextBlock(final bool) error { blk := enc.Block() enc.Encode(blk, src) blk.last = final - if final { - s.eofWritten = true - } // Wait for pending writes. s.wWg.Wait() if s.writeErr != nil { @@ -401,12 +405,20 @@ func (e *Encoder) Flush() error { if len(s.filling) > 0 { err := e.nextBlock(false) if err != nil { + // Ignore Flush after Close. + if errors.Is(s.err, ErrEncoderClosed) { + return nil + } return err } } s.wg.Wait() s.wWg.Wait() if s.err != nil { + // Ignore Flush after Close. + if errors.Is(s.err, ErrEncoderClosed) { + return nil + } return s.err } return s.writeErr @@ -422,6 +434,9 @@ func (e *Encoder) Close() error { } err := e.nextBlock(true) if err != nil { + if errors.Is(s.err, ErrEncoderClosed) { + return nil + } return err } if s.frameContentSize > 0 { @@ -459,6 +474,11 @@ func (e *Encoder) Close() error { } _, s.err = s.w.Write(frame) } + if s.err == nil { + s.err = ErrEncoderClosed + return nil + } + return s.err } @@ -469,6 +489,15 @@ func (e *Encoder) Close() error { // Data compressed with EncodeAll can be decoded with the Decoder, // using either a stream or DecodeAll. func (e *Encoder) EncodeAll(src, dst []byte) []byte { + e.init.Do(e.initialize) + enc := <-e.encoders + defer func() { + e.encoders <- enc + }() + return e.encodeAll(enc, src, dst) +} + +func (e *Encoder) encodeAll(enc encoder, src, dst []byte) []byte { if len(src) == 0 { if e.o.fullZero { // Add frame header. @@ -491,13 +520,7 @@ func (e *Encoder) EncodeAll(src, dst []byte) []byte { } return dst } - e.init.Do(e.initialize) - enc := <-e.encoders - defer func() { - // Release encoder reference to last block. - // If a non-single block is needed the encoder will reset again. - e.encoders <- enc - }() + // Use single segments when above minimum window and below window size. single := len(src) <= e.o.windowSize && len(src) > MinWindowSize if e.o.single != nil { diff --git a/vendor/github.com/klauspost/compress/zstd/framedec.go b/vendor/github.com/klauspost/compress/zstd/framedec.go index 53e160f7e5..e47af66e7c 100644 --- a/vendor/github.com/klauspost/compress/zstd/framedec.go +++ b/vendor/github.com/klauspost/compress/zstd/framedec.go @@ -146,7 +146,9 @@ func (d *frameDec) reset(br byteBuffer) error { } return err } - printf("raw: %x, mantissa: %d, exponent: %d\n", wd, wd&7, wd>>3) + if debugDecoder { + printf("raw: %x, mantissa: %d, exponent: %d\n", wd, wd&7, wd>>3) + } windowLog := 10 + (wd >> 3) windowBase := uint64(1) << windowLog windowAdd := (windowBase / 8) * uint64(wd&0x7) diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.go b/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.go index 8adabd8287..c59f17e07a 100644 --- a/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.go +++ b/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.go @@ -146,7 +146,7 @@ func (s *sequenceDecs) decodeSyncSimple(hist []byte) (bool, error) { return true, fmt.Errorf("output bigger than max block size (%d)", maxBlockSize) default: - return true, fmt.Errorf("sequenceDecs_decode returned erronous code %d", errCode) + return true, fmt.Errorf("sequenceDecs_decode returned erroneous code %d", errCode) } s.seqSize += ctx.litRemain @@ -292,7 +292,7 @@ func (s *sequenceDecs) decode(seqs []seqVals) error { return io.ErrUnexpectedEOF } - return fmt.Errorf("sequenceDecs_decode_amd64 returned erronous code %d", errCode) + return fmt.Errorf("sequenceDecs_decode_amd64 returned erroneous code %d", errCode) } if ctx.litRemain < 0 { diff --git a/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.s b/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.s index 5b06174b89..f5591fa1e8 100644 --- a/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.s +++ b/vendor/github.com/klauspost/compress/zstd/seqdec_amd64.s @@ -1814,7 +1814,7 @@ TEXT ·sequenceDecs_decodeSync_amd64(SB), $64-32 MOVQ 40(SP), AX ADDQ AX, 48(SP) - // Calculate poiter to s.out[cap(s.out)] (a past-end pointer) + // Calculate pointer to s.out[cap(s.out)] (a past-end pointer) ADDQ R10, 32(SP) // outBase += outPosition @@ -2376,7 +2376,7 @@ TEXT ·sequenceDecs_decodeSync_bmi2(SB), $64-32 MOVQ 40(SP), CX ADDQ CX, 48(SP) - // Calculate poiter to s.out[cap(s.out)] (a past-end pointer) + // Calculate pointer to s.out[cap(s.out)] (a past-end pointer) ADDQ R9, 32(SP) // outBase += outPosition @@ -2896,7 +2896,7 @@ TEXT ·sequenceDecs_decodeSync_safe_amd64(SB), $64-32 MOVQ 40(SP), AX ADDQ AX, 48(SP) - // Calculate poiter to s.out[cap(s.out)] (a past-end pointer) + // Calculate pointer to s.out[cap(s.out)] (a past-end pointer) ADDQ R10, 32(SP) // outBase += outPosition @@ -3560,7 +3560,7 @@ TEXT ·sequenceDecs_decodeSync_safe_bmi2(SB), $64-32 MOVQ 40(SP), CX ADDQ CX, 48(SP) - // Calculate poiter to s.out[cap(s.out)] (a past-end pointer) + // Calculate pointer to s.out[cap(s.out)] (a past-end pointer) ADDQ R9, 32(SP) // outBase += outPosition diff --git a/vendor/github.com/klauspost/compress/zstd/zstd.go b/vendor/github.com/klauspost/compress/zstd/zstd.go index 4be7cc7367..066bef2a4f 100644 --- a/vendor/github.com/klauspost/compress/zstd/zstd.go +++ b/vendor/github.com/klauspost/compress/zstd/zstd.go @@ -88,6 +88,10 @@ var ( // Close has been called. ErrDecoderClosed = errors.New("decoder used after Close") + // ErrEncoderClosed will be returned if the Encoder was used after + // Close has been called. + ErrEncoderClosed = errors.New("encoder used after Close") + // ErrDecoderNilInput is returned when a nil Reader was provided // and an operation other than Reset/DecodeAll/Close was attempted. ErrDecoderNilInput = errors.New("nil input provided as reader") diff --git a/vendor/github.com/magiconair/properties/.gitignore b/vendor/github.com/magiconair/properties/.gitignore deleted file mode 100644 index e7081ff522..0000000000 --- a/vendor/github.com/magiconair/properties/.gitignore +++ /dev/null @@ -1,6 +0,0 @@ -*.sublime-project -*.sublime-workspace -*.un~ -*.swp -.idea/ -*.iml diff --git a/vendor/github.com/magiconair/properties/CHANGELOG.md b/vendor/github.com/magiconair/properties/CHANGELOG.md deleted file mode 100644 index 842e8e24fb..0000000000 --- a/vendor/github.com/magiconair/properties/CHANGELOG.md +++ /dev/null @@ -1,205 +0,0 @@ -## Changelog - -### [1.8.7](https://github.com/magiconair/properties/tree/v1.8.7) - 08 Dec 2022 - - * [PR #65](https://github.com/magiconair/properties/pull/65): Speedup Merge - - Thanks to [@AdityaVallabh](https://github.com/AdityaVallabh) for the patch. - - * [PR #66](https://github.com/magiconair/properties/pull/66): use github actions - -### [1.8.6](https://github.com/magiconair/properties/tree/v1.8.6) - 23 Feb 2022 - - * [PR #57](https://github.com/magiconair/properties/pull/57):Fix "unreachable code" lint error - - Thanks to [@ellie](https://github.com/ellie) for the patch. - - * [PR #63](https://github.com/magiconair/properties/pull/63): Make TestMustGetParsedDuration backwards compatible - - This patch ensures that the `TestMustGetParsedDuration` still works with `go1.3` to make the - author happy until it affects real users. - - Thanks to [@maage](https://github.com/maage) for the patch. - -### [1.8.5](https://github.com/magiconair/properties/tree/v1.8.5) - 24 Mar 2021 - - * [PR #55](https://github.com/magiconair/properties/pull/55): Fix: Encoding Bug in Comments - - When reading comments \ are loaded correctly, but when writing they are then - replaced by \\. This leads to wrong comments when writing and reading multiple times. - - Thanks to [@doxsch](https://github.com/doxsch) for the patch. - -### [1.8.4](https://github.com/magiconair/properties/tree/v1.8.4) - 23 Sep 2020 - - * [PR #50](https://github.com/magiconair/properties/pull/50): enhance error message for circular references - - Thanks to [@sriv](https://github.com/sriv) for the patch. - -### [1.8.3](https://github.com/magiconair/properties/tree/v1.8.3) - 14 Sep 2020 - - * [PR #49](https://github.com/magiconair/properties/pull/49): Include the key in error message causing the circular reference - - The change is include the key in the error message which is causing the circular - reference when parsing/loading the properties files. - - Thanks to [@haroon-sheikh](https://github.com/haroon-sheikh) for the patch. - -### [1.8.2](https://github.com/magiconair/properties/tree/v1.8.2) - 25 Aug 2020 - - * [PR #36](https://github.com/magiconair/properties/pull/36): Escape backslash on write - - This patch ensures that backslashes are escaped on write. Existing applications which - rely on the old behavior may need to be updated. - - Thanks to [@apesternikov](https://github.com/apesternikov) for the patch. - - * [PR #42](https://github.com/magiconair/properties/pull/42): Made Content-Type check whitespace agnostic in LoadURL() - - Thanks to [@aliras1](https://github.com/aliras1) for the patch. - - * [PR #41](https://github.com/magiconair/properties/pull/41): Make key/value separator configurable on Write() - - Thanks to [@mkjor](https://github.com/mkjor) for the patch. - - * [PR #40](https://github.com/magiconair/properties/pull/40): Add method to return a sorted list of keys - - Thanks to [@mkjor](https://github.com/mkjor) for the patch. - -### [1.8.1](https://github.com/magiconair/properties/tree/v1.8.1) - 10 May 2019 - - * [PR #35](https://github.com/magiconair/properties/pull/35): Close body always after request - - This patch ensures that in `LoadURL` the response body is always closed. - - Thanks to [@liubog2008](https://github.com/liubog2008) for the patch. - -### [1.8](https://github.com/magiconair/properties/tree/v1.8) - 15 May 2018 - - * [PR #26](https://github.com/magiconair/properties/pull/26): Disable expansion during loading - - This adds the option to disable property expansion during loading. - - Thanks to [@kmala](https://github.com/kmala) for the patch. - -### [1.7.6](https://github.com/magiconair/properties/tree/v1.7.6) - 14 Feb 2018 - - * [PR #29](https://github.com/magiconair/properties/pull/29): Reworked expansion logic to handle more complex cases. - - See PR for an example. - - Thanks to [@yobert](https://github.com/yobert) for the fix. - -### [1.7.5](https://github.com/magiconair/properties/tree/v1.7.5) - 13 Feb 2018 - - * [PR #28](https://github.com/magiconair/properties/pull/28): Support duplicate expansions in the same value - - Values which expand the same key multiple times (e.g. `key=${a} ${a}`) will no longer fail - with a `circular reference error`. - - Thanks to [@yobert](https://github.com/yobert) for the fix. - -### [1.7.4](https://github.com/magiconair/properties/tree/v1.7.4) - 31 Oct 2017 - - * [Issue #23](https://github.com/magiconair/properties/issues/23): Ignore blank lines with whitespaces - - * [PR #24](https://github.com/magiconair/properties/pull/24): Update keys when DisableExpansion is enabled - - Thanks to [@mgurov](https://github.com/mgurov) for the fix. - -### [1.7.3](https://github.com/magiconair/properties/tree/v1.7.3) - 10 Jul 2017 - - * [Issue #17](https://github.com/magiconair/properties/issues/17): Add [SetValue()](http://godoc.org/github.com/magiconair/properties#Properties.SetValue) method to set values generically - * [Issue #22](https://github.com/magiconair/properties/issues/22): Add [LoadMap()](http://godoc.org/github.com/magiconair/properties#LoadMap) function to load properties from a string map - -### [1.7.2](https://github.com/magiconair/properties/tree/v1.7.2) - 20 Mar 2017 - - * [Issue #15](https://github.com/magiconair/properties/issues/15): Drop gocheck dependency - * [PR #21](https://github.com/magiconair/properties/pull/21): Add [Map()](http://godoc.org/github.com/magiconair/properties#Properties.Map) and [FilterFunc()](http://godoc.org/github.com/magiconair/properties#Properties.FilterFunc) - -### [1.7.1](https://github.com/magiconair/properties/tree/v1.7.1) - 13 Jan 2017 - - * [Issue #14](https://github.com/magiconair/properties/issues/14): Decouple TestLoadExpandedFile from `$USER` - * [PR #12](https://github.com/magiconair/properties/pull/12): Load from files and URLs - * [PR #16](https://github.com/magiconair/properties/pull/16): Keep gofmt happy - * [PR #18](https://github.com/magiconair/properties/pull/18): Fix Delete() function - -### [1.7.0](https://github.com/magiconair/properties/tree/v1.7.0) - 20 Mar 2016 - - * [Issue #10](https://github.com/magiconair/properties/issues/10): Add [LoadURL,LoadURLs,MustLoadURL,MustLoadURLs](http://godoc.org/github.com/magiconair/properties#LoadURL) method to load properties from a URL. - * [Issue #11](https://github.com/magiconair/properties/issues/11): Add [LoadString,MustLoadString](http://godoc.org/github.com/magiconair/properties#LoadString) method to load properties from an UTF8 string. - * [PR #8](https://github.com/magiconair/properties/pull/8): Add [MustFlag](http://godoc.org/github.com/magiconair/properties#Properties.MustFlag) method to provide overrides via command line flags. (@pascaldekloe) - -### [1.6.0](https://github.com/magiconair/properties/tree/v1.6.0) - 11 Dec 2015 - - * Add [Decode](http://godoc.org/github.com/magiconair/properties#Properties.Decode) method to populate struct from properties via tags. - -### [1.5.6](https://github.com/magiconair/properties/tree/v1.5.6) - 18 Oct 2015 - - * Vendored in gopkg.in/check.v1 - -### [1.5.5](https://github.com/magiconair/properties/tree/v1.5.5) - 31 Jul 2015 - - * [PR #6](https://github.com/magiconair/properties/pull/6): Add [Delete](http://godoc.org/github.com/magiconair/properties#Properties.Delete) method to remove keys including comments. (@gerbenjacobs) - -### [1.5.4](https://github.com/magiconair/properties/tree/v1.5.4) - 23 Jun 2015 - - * [Issue #5](https://github.com/magiconair/properties/issues/5): Allow disabling of property expansion [DisableExpansion](http://godoc.org/github.com/magiconair/properties#Properties.DisableExpansion). When property expansion is disabled Properties become a simple key/value store and don't check for circular references. - -### [1.5.3](https://github.com/magiconair/properties/tree/v1.5.3) - 02 Jun 2015 - - * [Issue #4](https://github.com/magiconair/properties/issues/4): Maintain key order in [Filter()](http://godoc.org/github.com/magiconair/properties#Properties.Filter), [FilterPrefix()](http://godoc.org/github.com/magiconair/properties#Properties.FilterPrefix) and [FilterRegexp()](http://godoc.org/github.com/magiconair/properties#Properties.FilterRegexp) - -### [1.5.2](https://github.com/magiconair/properties/tree/v1.5.2) - 10 Apr 2015 - - * [Issue #3](https://github.com/magiconair/properties/issues/3): Don't print comments in [WriteComment()](http://godoc.org/github.com/magiconair/properties#Properties.WriteComment) if they are all empty - * Add clickable links to README - -### [1.5.1](https://github.com/magiconair/properties/tree/v1.5.1) - 08 Dec 2014 - - * Added [GetParsedDuration()](http://godoc.org/github.com/magiconair/properties#Properties.GetParsedDuration) and [MustGetParsedDuration()](http://godoc.org/github.com/magiconair/properties#Properties.MustGetParsedDuration) for values specified compatible with - [time.ParseDuration()](http://golang.org/pkg/time/#ParseDuration). - -### [1.5.0](https://github.com/magiconair/properties/tree/v1.5.0) - 18 Nov 2014 - - * Added support for single and multi-line comments (reading, writing and updating) - * The order of keys is now preserved - * Calling [Set()](http://godoc.org/github.com/magiconair/properties#Properties.Set) with an empty key now silently ignores the call and does not create a new entry - * Added a [MustSet()](http://godoc.org/github.com/magiconair/properties#Properties.MustSet) method - * Migrated test library from launchpad.net/gocheck to [gopkg.in/check.v1](http://gopkg.in/check.v1) - -### [1.4.2](https://github.com/magiconair/properties/tree/v1.4.2) - 15 Nov 2014 - - * [Issue #2](https://github.com/magiconair/properties/issues/2): Fixed goroutine leak in parser which created two lexers but cleaned up only one - -### [1.4.1](https://github.com/magiconair/properties/tree/v1.4.1) - 13 Nov 2014 - - * [Issue #1](https://github.com/magiconair/properties/issues/1): Fixed bug in Keys() method which returned an empty string - -### [1.4.0](https://github.com/magiconair/properties/tree/v1.4.0) - 23 Sep 2014 - - * Added [Keys()](http://godoc.org/github.com/magiconair/properties#Properties.Keys) to get the keys - * Added [Filter()](http://godoc.org/github.com/magiconair/properties#Properties.Filter), [FilterRegexp()](http://godoc.org/github.com/magiconair/properties#Properties.FilterRegexp) and [FilterPrefix()](http://godoc.org/github.com/magiconair/properties#Properties.FilterPrefix) to get a subset of the properties - -### [1.3.0](https://github.com/magiconair/properties/tree/v1.3.0) - 18 Mar 2014 - -* Added support for time.Duration -* Made MustXXX() failure beha[ior configurable (log.Fatal, panic](https://github.com/magiconair/properties/tree/vior configurable (log.Fatal, panic) - custom) -* Changed default of MustXXX() failure from panic to log.Fatal - -### [1.2.0](https://github.com/magiconair/properties/tree/v1.2.0) - 05 Mar 2014 - -* Added MustGet... functions -* Added support for int and uint with range checks on 32 bit platforms - -### [1.1.0](https://github.com/magiconair/properties/tree/v1.1.0) - 20 Jan 2014 - -* Renamed from goproperties to properties -* Added support for expansion of environment vars in - filenames and value expressions -* Fixed bug where value expressions were not at the - start of the string - -### [1.0.0](https://github.com/magiconair/properties/tree/v1.0.0) - 7 Jan 2014 - -* Initial release diff --git a/vendor/github.com/magiconair/properties/LICENSE.md b/vendor/github.com/magiconair/properties/LICENSE.md deleted file mode 100644 index 79c87e3e6f..0000000000 --- a/vendor/github.com/magiconair/properties/LICENSE.md +++ /dev/null @@ -1,24 +0,0 @@ -Copyright (c) 2013-2020, Frank Schroeder - -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - - * Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND -ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 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/magiconair/properties/README.md b/vendor/github.com/magiconair/properties/README.md deleted file mode 100644 index e2edda025b..0000000000 --- a/vendor/github.com/magiconair/properties/README.md +++ /dev/null @@ -1,128 +0,0 @@ -[![](https://img.shields.io/github/tag/magiconair/properties.svg?style=flat-square&label=release)](https://github.com/magiconair/properties/releases) -[![Travis CI Status](https://img.shields.io/travis/magiconair/properties.svg?branch=master&style=flat-square&label=travis)](https://travis-ci.org/magiconair/properties) -[![License](https://img.shields.io/badge/License-BSD%202--Clause-orange.svg?style=flat-square)](https://raw.githubusercontent.com/magiconair/properties/master/LICENSE) -[![GoDoc](http://img.shields.io/badge/godoc-reference-5272B4.svg?style=flat-square)](http://godoc.org/github.com/magiconair/properties) - -# Overview - -#### Please run `git pull --tags` to update the tags. See [below](#updated-git-tags) why. - -properties is a Go library for reading and writing properties files. - -It supports reading from multiple files or URLs and Spring style recursive -property expansion of expressions like `${key}` to their corresponding value. -Value expressions can refer to other keys like in `${key}` or to environment -variables like in `${USER}`. Filenames can also contain environment variables -like in `/home/${USER}/myapp.properties`. - -Properties can be decoded into structs, maps, arrays and values through -struct tags. - -Comments and the order of keys are preserved. Comments can be modified -and can be written to the output. - -The properties library supports both ISO-8859-1 and UTF-8 encoded data. - -Starting from version 1.3.0 the behavior of the MustXXX() functions is -configurable by providing a custom `ErrorHandler` function. The default has -changed from `panic` to `log.Fatal` but this is configurable and custom -error handling functions can be provided. See the package documentation for -details. - -Read the full documentation on [![GoDoc](http://img.shields.io/badge/godoc-reference-5272B4.svg?style=flat-square)](http://godoc.org/github.com/magiconair/properties) - -## Getting Started - -```go -import ( - "flag" - "github.com/magiconair/properties" -) - -func main() { - // init from a file - p := properties.MustLoadFile("${HOME}/config.properties", properties.UTF8) - - // or multiple files - p = properties.MustLoadFiles([]string{ - "${HOME}/config.properties", - "${HOME}/config-${USER}.properties", - }, properties.UTF8, true) - - // or from a map - p = properties.LoadMap(map[string]string{"key": "value", "abc": "def"}) - - // or from a string - p = properties.MustLoadString("key=value\nabc=def") - - // or from a URL - p = properties.MustLoadURL("http://host/path") - - // or from multiple URLs - p = properties.MustLoadURL([]string{ - "http://host/config", - "http://host/config-${USER}", - }, true) - - // or from flags - p.MustFlag(flag.CommandLine) - - // get values through getters - host := p.MustGetString("host") - port := p.GetInt("port", 8080) - - // or through Decode - type Config struct { - Host string `properties:"host"` - Port int `properties:"port,default=9000"` - Accept []string `properties:"accept,default=image/png;image;gif"` - Timeout time.Duration `properties:"timeout,default=5s"` - } - var cfg Config - if err := p.Decode(&cfg); err != nil { - log.Fatal(err) - } -} - -``` - -## Installation and Upgrade - -``` -$ go get -u github.com/magiconair/properties -``` - -## License - -2 clause BSD license. See [LICENSE](https://github.com/magiconair/properties/blob/master/LICENSE) file for details. - -## ToDo - -* Dump contents with passwords and secrets obscured - -## Updated Git tags - -#### 13 Feb 2018 - -I realized that all of the git tags I had pushed before v1.7.5 were lightweight tags -and I've only recently learned that this doesn't play well with `git describe` 😞 - -I have replaced all lightweight tags with signed tags using this script which should -retain the commit date, name and email address. Please run `git pull --tags` to update them. - -Worst case you have to reclone the repo. - -```shell -#!/bin/bash -tag=$1 -echo "Updating $tag" -date=$(git show ${tag}^0 --format=%aD | head -1) -email=$(git show ${tag}^0 --format=%aE | head -1) -name=$(git show ${tag}^0 --format=%aN | head -1) -GIT_COMMITTER_DATE="$date" GIT_COMMITTER_NAME="$name" GIT_COMMITTER_EMAIL="$email" git tag -s -f ${tag} ${tag}^0 -m ${tag} -``` - -I apologize for the inconvenience. - -Frank - diff --git a/vendor/github.com/magiconair/properties/decode.go b/vendor/github.com/magiconair/properties/decode.go deleted file mode 100644 index 8e6aa441d9..0000000000 --- a/vendor/github.com/magiconair/properties/decode.go +++ /dev/null @@ -1,289 +0,0 @@ -// Copyright 2013-2022 Frank Schroeder. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package properties - -import ( - "fmt" - "reflect" - "strconv" - "strings" - "time" -) - -// Decode assigns property values to exported fields of a struct. -// -// Decode traverses v recursively and returns an error if a value cannot be -// converted to the field type or a required value is missing for a field. -// -// The following type dependent decodings are used: -// -// String, boolean, numeric fields have the value of the property key assigned. -// The property key name is the name of the field. A different key and a default -// value can be set in the field's tag. Fields without default value are -// required. If the value cannot be converted to the field type an error is -// returned. -// -// time.Duration fields have the result of time.ParseDuration() assigned. -// -// time.Time fields have the vaule of time.Parse() assigned. The default layout -// is time.RFC3339 but can be set in the field's tag. -// -// Arrays and slices of string, boolean, numeric, time.Duration and time.Time -// fields have the value interpreted as a comma separated list of values. The -// individual values are trimmed of whitespace and empty values are ignored. A -// default value can be provided as a semicolon separated list in the field's -// tag. -// -// Struct fields are decoded recursively using the field name plus "." as -// prefix. The prefix (without dot) can be overridden in the field's tag. -// Default values are not supported in the field's tag. Specify them on the -// fields of the inner struct instead. -// -// Map fields must have a key of type string and are decoded recursively by -// using the field's name plus ".' as prefix and the next element of the key -// name as map key. The prefix (without dot) can be overridden in the field's -// tag. Default values are not supported. -// -// Examples: -// -// // Field is ignored. -// Field int `properties:"-"` -// -// // Field is assigned value of 'Field'. -// Field int -// -// // Field is assigned value of 'myName'. -// Field int `properties:"myName"` -// -// // Field is assigned value of key 'myName' and has a default -// // value 15 if the key does not exist. -// Field int `properties:"myName,default=15"` -// -// // Field is assigned value of key 'Field' and has a default -// // value 15 if the key does not exist. -// Field int `properties:",default=15"` -// -// // Field is assigned value of key 'date' and the date -// // is in format 2006-01-02 -// Field time.Time `properties:"date,layout=2006-01-02"` -// -// // Field is assigned the non-empty and whitespace trimmed -// // values of key 'Field' split by commas. -// Field []string -// -// // Field is assigned the non-empty and whitespace trimmed -// // values of key 'Field' split by commas and has a default -// // value ["a", "b", "c"] if the key does not exist. -// Field []string `properties:",default=a;b;c"` -// -// // Field is decoded recursively with "Field." as key prefix. -// Field SomeStruct -// -// // Field is decoded recursively with "myName." as key prefix. -// Field SomeStruct `properties:"myName"` -// -// // Field is decoded recursively with "Field." as key prefix -// // and the next dotted element of the key as map key. -// Field map[string]string -// -// // Field is decoded recursively with "myName." as key prefix -// // and the next dotted element of the key as map key. -// Field map[string]string `properties:"myName"` -func (p *Properties) Decode(x interface{}) error { - t, v := reflect.TypeOf(x), reflect.ValueOf(x) - if t.Kind() != reflect.Ptr || v.Elem().Type().Kind() != reflect.Struct { - return fmt.Errorf("not a pointer to struct: %s", t) - } - if err := dec(p, "", nil, nil, v); err != nil { - return err - } - return nil -} - -func dec(p *Properties, key string, def *string, opts map[string]string, v reflect.Value) error { - t := v.Type() - - // value returns the property value for key or the default if provided. - value := func() (string, error) { - if val, ok := p.Get(key); ok { - return val, nil - } - if def != nil { - return *def, nil - } - return "", fmt.Errorf("missing required key %s", key) - } - - // conv converts a string to a value of the given type. - conv := func(s string, t reflect.Type) (val reflect.Value, err error) { - var v interface{} - - switch { - case isDuration(t): - v, err = time.ParseDuration(s) - - case isTime(t): - layout := opts["layout"] - if layout == "" { - layout = time.RFC3339 - } - v, err = time.Parse(layout, s) - - case isBool(t): - v, err = boolVal(s), nil - - case isString(t): - v, err = s, nil - - case isFloat(t): - v, err = strconv.ParseFloat(s, 64) - - case isInt(t): - v, err = strconv.ParseInt(s, 10, 64) - - case isUint(t): - v, err = strconv.ParseUint(s, 10, 64) - - default: - return reflect.Zero(t), fmt.Errorf("unsupported type %s", t) - } - if err != nil { - return reflect.Zero(t), err - } - return reflect.ValueOf(v).Convert(t), nil - } - - // keydef returns the property key and the default value based on the - // name of the struct field and the options in the tag. - keydef := func(f reflect.StructField) (string, *string, map[string]string) { - _key, _opts := parseTag(f.Tag.Get("properties")) - - var _def *string - if d, ok := _opts["default"]; ok { - _def = &d - } - if _key != "" { - return _key, _def, _opts - } - return f.Name, _def, _opts - } - - switch { - case isDuration(t) || isTime(t) || isBool(t) || isString(t) || isFloat(t) || isInt(t) || isUint(t): - s, err := value() - if err != nil { - return err - } - val, err := conv(s, t) - if err != nil { - return err - } - v.Set(val) - - case isPtr(t): - return dec(p, key, def, opts, v.Elem()) - - case isStruct(t): - for i := 0; i < v.NumField(); i++ { - fv := v.Field(i) - fk, def, opts := keydef(t.Field(i)) - if !fv.CanSet() { - return fmt.Errorf("cannot set %s", t.Field(i).Name) - } - if fk == "-" { - continue - } - if key != "" { - fk = key + "." + fk - } - if err := dec(p, fk, def, opts, fv); err != nil { - return err - } - } - return nil - - case isArray(t): - val, err := value() - if err != nil { - return err - } - vals := split(val, ";") - a := reflect.MakeSlice(t, 0, len(vals)) - for _, s := range vals { - val, err := conv(s, t.Elem()) - if err != nil { - return err - } - a = reflect.Append(a, val) - } - v.Set(a) - - case isMap(t): - valT := t.Elem() - m := reflect.MakeMap(t) - for postfix := range p.FilterStripPrefix(key + ".").m { - pp := strings.SplitN(postfix, ".", 2) - mk, mv := pp[0], reflect.New(valT) - if err := dec(p, key+"."+mk, nil, nil, mv); err != nil { - return err - } - m.SetMapIndex(reflect.ValueOf(mk), mv.Elem()) - } - v.Set(m) - - default: - return fmt.Errorf("unsupported type %s", t) - } - return nil -} - -// split splits a string on sep, trims whitespace of elements -// and omits empty elements -func split(s string, sep string) []string { - var a []string - for _, v := range strings.Split(s, sep) { - if v = strings.TrimSpace(v); v != "" { - a = append(a, v) - } - } - return a -} - -// parseTag parses a "key,k=v,k=v,..." -func parseTag(tag string) (key string, opts map[string]string) { - opts = map[string]string{} - for i, s := range strings.Split(tag, ",") { - if i == 0 { - key = s - continue - } - - pp := strings.SplitN(s, "=", 2) - if len(pp) == 1 { - opts[pp[0]] = "" - } else { - opts[pp[0]] = pp[1] - } - } - return key, opts -} - -func isArray(t reflect.Type) bool { return t.Kind() == reflect.Array || t.Kind() == reflect.Slice } -func isBool(t reflect.Type) bool { return t.Kind() == reflect.Bool } -func isDuration(t reflect.Type) bool { return t == reflect.TypeOf(time.Second) } -func isMap(t reflect.Type) bool { return t.Kind() == reflect.Map } -func isPtr(t reflect.Type) bool { return t.Kind() == reflect.Ptr } -func isString(t reflect.Type) bool { return t.Kind() == reflect.String } -func isStruct(t reflect.Type) bool { return t.Kind() == reflect.Struct } -func isTime(t reflect.Type) bool { return t == reflect.TypeOf(time.Time{}) } -func isFloat(t reflect.Type) bool { - return t.Kind() == reflect.Float32 || t.Kind() == reflect.Float64 -} -func isInt(t reflect.Type) bool { - return t.Kind() == reflect.Int || t.Kind() == reflect.Int8 || t.Kind() == reflect.Int16 || t.Kind() == reflect.Int32 || t.Kind() == reflect.Int64 -} -func isUint(t reflect.Type) bool { - return t.Kind() == reflect.Uint || t.Kind() == reflect.Uint8 || t.Kind() == reflect.Uint16 || t.Kind() == reflect.Uint32 || t.Kind() == reflect.Uint64 -} diff --git a/vendor/github.com/magiconair/properties/doc.go b/vendor/github.com/magiconair/properties/doc.go deleted file mode 100644 index 7c79793159..0000000000 --- a/vendor/github.com/magiconair/properties/doc.go +++ /dev/null @@ -1,155 +0,0 @@ -// Copyright 2013-2022 Frank Schroeder. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package properties provides functions for reading and writing -// ISO-8859-1 and UTF-8 encoded .properties files and has -// support for recursive property expansion. -// -// Java properties files are ISO-8859-1 encoded and use Unicode -// literals for characters outside the ISO character set. Unicode -// literals can be used in UTF-8 encoded properties files but -// aren't necessary. -// -// To load a single properties file use MustLoadFile(): -// -// p := properties.MustLoadFile(filename, properties.UTF8) -// -// To load multiple properties files use MustLoadFiles() -// which loads the files in the given order and merges the -// result. Missing properties files can be ignored if the -// 'ignoreMissing' flag is set to true. -// -// Filenames can contain environment variables which are expanded -// before loading. -// -// f1 := "/etc/myapp/myapp.conf" -// f2 := "/home/${USER}/myapp.conf" -// p := MustLoadFiles([]string{f1, f2}, properties.UTF8, true) -// -// All of the different key/value delimiters ' ', ':' and '=' are -// supported as well as the comment characters '!' and '#' and -// multi-line values. -// -// ! this is a comment -// # and so is this -// -// # the following expressions are equal -// key value -// key=value -// key:value -// key = value -// key : value -// key = val\ -// ue -// -// Properties stores all comments preceding a key and provides -// GetComments() and SetComments() methods to retrieve and -// update them. The convenience functions GetComment() and -// SetComment() allow access to the last comment. The -// WriteComment() method writes properties files including -// the comments and with the keys in the original order. -// This can be used for sanitizing properties files. -// -// Property expansion is recursive and circular references -// and malformed expressions are not allowed and cause an -// error. Expansion of environment variables is supported. -// -// # standard property -// key = value -// -// # property expansion: key2 = value -// key2 = ${key} -// -// # recursive expansion: key3 = value -// key3 = ${key2} -// -// # circular reference (error) -// key = ${key} -// -// # malformed expression (error) -// key = ${ke -// -// # refers to the users' home dir -// home = ${HOME} -// -// # local key takes precedence over env var: u = foo -// USER = foo -// u = ${USER} -// -// The default property expansion format is ${key} but can be -// changed by setting different pre- and postfix values on the -// Properties object. -// -// p := properties.NewProperties() -// p.Prefix = "#[" -// p.Postfix = "]#" -// -// Properties provides convenience functions for getting typed -// values with default values if the key does not exist or the -// type conversion failed. -// -// # Returns true if the value is either "1", "on", "yes" or "true" -// # Returns false for every other value and the default value if -// # the key does not exist. -// v = p.GetBool("key", false) -// -// # Returns the value if the key exists and the format conversion -// # was successful. Otherwise, the default value is returned. -// v = p.GetInt64("key", 999) -// v = p.GetUint64("key", 999) -// v = p.GetFloat64("key", 123.0) -// v = p.GetString("key", "def") -// v = p.GetDuration("key", 999) -// -// As an alternative properties may be applied with the standard -// library's flag implementation at any time. -// -// # Standard configuration -// v = flag.Int("key", 999, "help message") -// flag.Parse() -// -// # Merge p into the flag set -// p.MustFlag(flag.CommandLine) -// -// Properties provides several MustXXX() convenience functions -// which will terminate the app if an error occurs. The behavior -// of the failure is configurable and the default is to call -// log.Fatal(err). To have the MustXXX() functions panic instead -// of logging the error set a different ErrorHandler before -// you use the Properties package. -// -// properties.ErrorHandler = properties.PanicHandler -// -// # Will panic instead of logging an error -// p := properties.MustLoadFile("config.properties") -// -// You can also provide your own ErrorHandler function. The only requirement -// is that the error handler function must exit after handling the error. -// -// properties.ErrorHandler = func(err error) { -// fmt.Println(err) -// os.Exit(1) -// } -// -// # Will write to stdout and then exit -// p := properties.MustLoadFile("config.properties") -// -// Properties can also be loaded into a struct via the `Decode` -// method, e.g. -// -// type S struct { -// A string `properties:"a,default=foo"` -// D time.Duration `properties:"timeout,default=5s"` -// E time.Time `properties:"expires,layout=2006-01-02,default=2015-01-01"` -// } -// -// See `Decode()` method for the full documentation. -// -// The following documents provide a description of the properties -// file format. -// -// http://en.wikipedia.org/wiki/.properties -// -// http://docs.oracle.com/javase/7/docs/api/java/util/Properties.html#load%28java.io.Reader%29 -package properties diff --git a/vendor/github.com/magiconair/properties/integrate.go b/vendor/github.com/magiconair/properties/integrate.go deleted file mode 100644 index 35d0ae97b3..0000000000 --- a/vendor/github.com/magiconair/properties/integrate.go +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2013-2022 Frank Schroeder. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package properties - -import "flag" - -// MustFlag sets flags that are skipped by dst.Parse when p contains -// the respective key for flag.Flag.Name. -// -// It's use is recommended with command line arguments as in: -// -// flag.Parse() -// p.MustFlag(flag.CommandLine) -func (p *Properties) MustFlag(dst *flag.FlagSet) { - m := make(map[string]*flag.Flag) - dst.VisitAll(func(f *flag.Flag) { - m[f.Name] = f - }) - dst.Visit(func(f *flag.Flag) { - delete(m, f.Name) // overridden - }) - - for name, f := range m { - v, ok := p.Get(name) - if !ok { - continue - } - - if err := f.Value.Set(v); err != nil { - ErrorHandler(err) - } - } -} diff --git a/vendor/github.com/magiconair/properties/lex.go b/vendor/github.com/magiconair/properties/lex.go deleted file mode 100644 index 3d15a1f6ed..0000000000 --- a/vendor/github.com/magiconair/properties/lex.go +++ /dev/null @@ -1,395 +0,0 @@ -// Copyright 2013-2022 Frank Schroeder. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. -// -// Parts of the lexer are from the template/text/parser package -// For these parts the following applies: -// -// Copyright 2011 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file of the go 1.2 -// distribution. - -package properties - -import ( - "fmt" - "strconv" - "strings" - "unicode/utf8" -) - -// item represents a token or text string returned from the scanner. -type item struct { - typ itemType // The type of this item. - pos int // The starting position, in bytes, of this item in the input string. - val string // The value of this item. -} - -func (i item) String() string { - switch { - case i.typ == itemEOF: - return "EOF" - case i.typ == itemError: - return i.val - case len(i.val) > 10: - return fmt.Sprintf("%.10q...", i.val) - } - return fmt.Sprintf("%q", i.val) -} - -// itemType identifies the type of lex items. -type itemType int - -const ( - itemError itemType = iota // error occurred; value is text of error - itemEOF - itemKey // a key - itemValue // a value - itemComment // a comment -) - -// defines a constant for EOF -const eof = -1 - -// permitted whitespace characters space, FF and TAB -const whitespace = " \f\t" - -// stateFn represents the state of the scanner as a function that returns the next state. -type stateFn func(*lexer) stateFn - -// lexer holds the state of the scanner. -type lexer struct { - input string // the string being scanned - state stateFn // the next lexing function to enter - pos int // current position in the input - start int // start position of this item - width int // width of last rune read from input - lastPos int // position of most recent item returned by nextItem - runes []rune // scanned runes for this item - items chan item // channel of scanned items -} - -// next returns the next rune in the input. -func (l *lexer) next() rune { - if l.pos >= len(l.input) { - l.width = 0 - return eof - } - r, w := utf8.DecodeRuneInString(l.input[l.pos:]) - l.width = w - l.pos += l.width - return r -} - -// peek returns but does not consume the next rune in the input. -func (l *lexer) peek() rune { - r := l.next() - l.backup() - return r -} - -// backup steps back one rune. Can only be called once per call of next. -func (l *lexer) backup() { - l.pos -= l.width -} - -// emit passes an item back to the client. -func (l *lexer) emit(t itemType) { - i := item{t, l.start, string(l.runes)} - l.items <- i - l.start = l.pos - l.runes = l.runes[:0] -} - -// ignore skips over the pending input before this point. -func (l *lexer) ignore() { - l.start = l.pos -} - -// appends the rune to the current value -func (l *lexer) appendRune(r rune) { - l.runes = append(l.runes, r) -} - -// accept consumes the next rune if it's from the valid set. -func (l *lexer) accept(valid string) bool { - if strings.ContainsRune(valid, l.next()) { - return true - } - l.backup() - return false -} - -// acceptRun consumes a run of runes from the valid set. -func (l *lexer) acceptRun(valid string) { - for strings.ContainsRune(valid, l.next()) { - } - l.backup() -} - -// lineNumber reports which line we're on, based on the position of -// the previous item returned by nextItem. Doing it this way -// means we don't have to worry about peek double counting. -func (l *lexer) lineNumber() int { - return 1 + strings.Count(l.input[:l.lastPos], "\n") -} - -// errorf returns an error token and terminates the scan by passing -// back a nil pointer that will be the next state, terminating l.nextItem. -func (l *lexer) errorf(format string, args ...interface{}) stateFn { - l.items <- item{itemError, l.start, fmt.Sprintf(format, args...)} - return nil -} - -// nextItem returns the next item from the input. -func (l *lexer) nextItem() item { - i := <-l.items - l.lastPos = i.pos - return i -} - -// lex creates a new scanner for the input string. -func lex(input string) *lexer { - l := &lexer{ - input: input, - items: make(chan item), - runes: make([]rune, 0, 32), - } - go l.run() - return l -} - -// run runs the state machine for the lexer. -func (l *lexer) run() { - for l.state = lexBeforeKey(l); l.state != nil; { - l.state = l.state(l) - } -} - -// state functions - -// lexBeforeKey scans until a key begins. -func lexBeforeKey(l *lexer) stateFn { - switch r := l.next(); { - case isEOF(r): - l.emit(itemEOF) - return nil - - case isEOL(r): - l.ignore() - return lexBeforeKey - - case isComment(r): - return lexComment - - case isWhitespace(r): - l.ignore() - return lexBeforeKey - - default: - l.backup() - return lexKey - } -} - -// lexComment scans a comment line. The comment character has already been scanned. -func lexComment(l *lexer) stateFn { - l.acceptRun(whitespace) - l.ignore() - for { - switch r := l.next(); { - case isEOF(r): - l.ignore() - l.emit(itemEOF) - return nil - case isEOL(r): - l.emit(itemComment) - return lexBeforeKey - default: - l.appendRune(r) - } - } -} - -// lexKey scans the key up to a delimiter -func lexKey(l *lexer) stateFn { - var r rune - -Loop: - for { - switch r = l.next(); { - - case isEscape(r): - err := l.scanEscapeSequence() - if err != nil { - return l.errorf(err.Error()) - } - - case isEndOfKey(r): - l.backup() - break Loop - - case isEOF(r): - break Loop - - default: - l.appendRune(r) - } - } - - if len(l.runes) > 0 { - l.emit(itemKey) - } - - if isEOF(r) { - l.emit(itemEOF) - return nil - } - - return lexBeforeValue -} - -// lexBeforeValue scans the delimiter between key and value. -// Leading and trailing whitespace is ignored. -// We expect to be just after the key. -func lexBeforeValue(l *lexer) stateFn { - l.acceptRun(whitespace) - l.accept(":=") - l.acceptRun(whitespace) - l.ignore() - return lexValue -} - -// lexValue scans text until the end of the line. We expect to be just after the delimiter. -func lexValue(l *lexer) stateFn { - for { - switch r := l.next(); { - case isEscape(r): - if isEOL(l.peek()) { - l.next() - l.acceptRun(whitespace) - } else { - err := l.scanEscapeSequence() - if err != nil { - return l.errorf(err.Error()) - } - } - - case isEOL(r): - l.emit(itemValue) - l.ignore() - return lexBeforeKey - - case isEOF(r): - l.emit(itemValue) - l.emit(itemEOF) - return nil - - default: - l.appendRune(r) - } - } -} - -// scanEscapeSequence scans either one of the escaped characters -// or a unicode literal. We expect to be after the escape character. -func (l *lexer) scanEscapeSequence() error { - switch r := l.next(); { - - case isEscapedCharacter(r): - l.appendRune(decodeEscapedCharacter(r)) - return nil - - case atUnicodeLiteral(r): - return l.scanUnicodeLiteral() - - case isEOF(r): - return fmt.Errorf("premature EOF") - - // silently drop the escape character and append the rune as is - default: - l.appendRune(r) - return nil - } -} - -// scans a unicode literal in the form \uXXXX. We expect to be after the \u. -func (l *lexer) scanUnicodeLiteral() error { - // scan the digits - d := make([]rune, 4) - for i := 0; i < 4; i++ { - d[i] = l.next() - if d[i] == eof || !strings.ContainsRune("0123456789abcdefABCDEF", d[i]) { - return fmt.Errorf("invalid unicode literal") - } - } - - // decode the digits into a rune - r, err := strconv.ParseInt(string(d), 16, 0) - if err != nil { - return err - } - - l.appendRune(rune(r)) - return nil -} - -// decodeEscapedCharacter returns the unescaped rune. We expect to be after the escape character. -func decodeEscapedCharacter(r rune) rune { - switch r { - case 'f': - return '\f' - case 'n': - return '\n' - case 'r': - return '\r' - case 't': - return '\t' - default: - return r - } -} - -// atUnicodeLiteral reports whether we are at a unicode literal. -// The escape character has already been consumed. -func atUnicodeLiteral(r rune) bool { - return r == 'u' -} - -// isComment reports whether we are at the start of a comment. -func isComment(r rune) bool { - return r == '#' || r == '!' -} - -// isEndOfKey reports whether the rune terminates the current key. -func isEndOfKey(r rune) bool { - return strings.ContainsRune(" \f\t\r\n:=", r) -} - -// isEOF reports whether we are at EOF. -func isEOF(r rune) bool { - return r == eof -} - -// isEOL reports whether we are at a new line character. -func isEOL(r rune) bool { - return r == '\n' || r == '\r' -} - -// isEscape reports whether the rune is the escape character which -// prefixes unicode literals and other escaped characters. -func isEscape(r rune) bool { - return r == '\\' -} - -// isEscapedCharacter reports whether we are at one of the characters that need escaping. -// The escape character has already been consumed. -func isEscapedCharacter(r rune) bool { - return strings.ContainsRune(" :=fnrt", r) -} - -// isWhitespace reports whether the rune is a whitespace character. -func isWhitespace(r rune) bool { - return strings.ContainsRune(whitespace, r) -} diff --git a/vendor/github.com/magiconair/properties/load.go b/vendor/github.com/magiconair/properties/load.go deleted file mode 100644 index 635368dc8a..0000000000 --- a/vendor/github.com/magiconair/properties/load.go +++ /dev/null @@ -1,293 +0,0 @@ -// Copyright 2013-2022 Frank Schroeder. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package properties - -import ( - "fmt" - "io/ioutil" - "net/http" - "os" - "strings" -) - -// Encoding specifies encoding of the input data. -type Encoding uint - -const ( - // utf8Default is a private placeholder for the zero value of Encoding to - // ensure that it has the correct meaning. UTF8 is the default encoding but - // was assigned a non-zero value which cannot be changed without breaking - // existing code. Clients should continue to use the public constants. - utf8Default Encoding = iota - - // UTF8 interprets the input data as UTF-8. - UTF8 - - // ISO_8859_1 interprets the input data as ISO-8859-1. - ISO_8859_1 -) - -type Loader struct { - // Encoding determines how the data from files and byte buffers - // is interpreted. For URLs the Content-Type header is used - // to determine the encoding of the data. - Encoding Encoding - - // DisableExpansion configures the property expansion of the - // returned property object. When set to true, the property values - // will not be expanded and the Property object will not be checked - // for invalid expansion expressions. - DisableExpansion bool - - // IgnoreMissing configures whether missing files or URLs which return - // 404 are reported as errors. When set to true, missing files and 404 - // status codes are not reported as errors. - IgnoreMissing bool -} - -// Load reads a buffer into a Properties struct. -func (l *Loader) LoadBytes(buf []byte) (*Properties, error) { - return l.loadBytes(buf, l.Encoding) -} - -// LoadAll reads the content of multiple URLs or files in the given order into -// a Properties struct. If IgnoreMissing is true then a 404 status code or -// missing file will not be reported as error. Encoding sets the encoding for -// files. For the URLs see LoadURL for the Content-Type header and the -// encoding. -func (l *Loader) LoadAll(names []string) (*Properties, error) { - all := NewProperties() - for _, name := range names { - n, err := expandName(name) - if err != nil { - return nil, err - } - - var p *Properties - switch { - case strings.HasPrefix(n, "http://"): - p, err = l.LoadURL(n) - case strings.HasPrefix(n, "https://"): - p, err = l.LoadURL(n) - default: - p, err = l.LoadFile(n) - } - if err != nil { - return nil, err - } - all.Merge(p) - } - - all.DisableExpansion = l.DisableExpansion - if all.DisableExpansion { - return all, nil - } - return all, all.check() -} - -// LoadFile reads a file into a Properties struct. -// If IgnoreMissing is true then a missing file will not be -// reported as error. -func (l *Loader) LoadFile(filename string) (*Properties, error) { - data, err := ioutil.ReadFile(filename) - if err != nil { - if l.IgnoreMissing && os.IsNotExist(err) { - LogPrintf("properties: %s not found. skipping", filename) - return NewProperties(), nil - } - return nil, err - } - return l.loadBytes(data, l.Encoding) -} - -// LoadURL reads the content of the URL into a Properties struct. -// -// The encoding is determined via the Content-Type header which -// should be set to 'text/plain'. If the 'charset' parameter is -// missing, 'iso-8859-1' or 'latin1' the encoding is set to -// ISO-8859-1. If the 'charset' parameter is set to 'utf-8' the -// encoding is set to UTF-8. A missing content type header is -// interpreted as 'text/plain; charset=utf-8'. -func (l *Loader) LoadURL(url string) (*Properties, error) { - resp, err := http.Get(url) - if err != nil { - return nil, fmt.Errorf("properties: error fetching %q. %s", url, err) - } - defer resp.Body.Close() - - if resp.StatusCode == 404 && l.IgnoreMissing { - LogPrintf("properties: %s returned %d. skipping", url, resp.StatusCode) - return NewProperties(), nil - } - - if resp.StatusCode != 200 { - return nil, fmt.Errorf("properties: %s returned %d", url, resp.StatusCode) - } - - body, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, fmt.Errorf("properties: %s error reading response. %s", url, err) - } - - ct := resp.Header.Get("Content-Type") - ct = strings.Join(strings.Fields(ct), "") - var enc Encoding - switch strings.ToLower(ct) { - case "text/plain", "text/plain;charset=iso-8859-1", "text/plain;charset=latin1": - enc = ISO_8859_1 - case "", "text/plain;charset=utf-8": - enc = UTF8 - default: - return nil, fmt.Errorf("properties: invalid content type %s", ct) - } - - return l.loadBytes(body, enc) -} - -func (l *Loader) loadBytes(buf []byte, enc Encoding) (*Properties, error) { - p, err := parse(convert(buf, enc)) - if err != nil { - return nil, err - } - p.DisableExpansion = l.DisableExpansion - if p.DisableExpansion { - return p, nil - } - return p, p.check() -} - -// Load reads a buffer into a Properties struct. -func Load(buf []byte, enc Encoding) (*Properties, error) { - l := &Loader{Encoding: enc} - return l.LoadBytes(buf) -} - -// LoadString reads an UTF8 string into a properties struct. -func LoadString(s string) (*Properties, error) { - l := &Loader{Encoding: UTF8} - return l.LoadBytes([]byte(s)) -} - -// LoadMap creates a new Properties struct from a string map. -func LoadMap(m map[string]string) *Properties { - p := NewProperties() - for k, v := range m { - p.Set(k, v) - } - return p -} - -// LoadFile reads a file into a Properties struct. -func LoadFile(filename string, enc Encoding) (*Properties, error) { - l := &Loader{Encoding: enc} - return l.LoadAll([]string{filename}) -} - -// LoadFiles reads multiple files in the given order into -// a Properties struct. If 'ignoreMissing' is true then -// non-existent files will not be reported as error. -func LoadFiles(filenames []string, enc Encoding, ignoreMissing bool) (*Properties, error) { - l := &Loader{Encoding: enc, IgnoreMissing: ignoreMissing} - return l.LoadAll(filenames) -} - -// LoadURL reads the content of the URL into a Properties struct. -// See Loader#LoadURL for details. -func LoadURL(url string) (*Properties, error) { - l := &Loader{Encoding: UTF8} - return l.LoadAll([]string{url}) -} - -// LoadURLs reads the content of multiple URLs in the given order into a -// Properties struct. If IgnoreMissing is true then a 404 status code will -// not be reported as error. See Loader#LoadURL for the Content-Type header -// and the encoding. -func LoadURLs(urls []string, ignoreMissing bool) (*Properties, error) { - l := &Loader{Encoding: UTF8, IgnoreMissing: ignoreMissing} - return l.LoadAll(urls) -} - -// LoadAll reads the content of multiple URLs or files in the given order into a -// Properties struct. If 'ignoreMissing' is true then a 404 status code or missing file will -// not be reported as error. Encoding sets the encoding for files. For the URLs please see -// LoadURL for the Content-Type header and the encoding. -func LoadAll(names []string, enc Encoding, ignoreMissing bool) (*Properties, error) { - l := &Loader{Encoding: enc, IgnoreMissing: ignoreMissing} - return l.LoadAll(names) -} - -// MustLoadString reads an UTF8 string into a Properties struct and -// panics on error. -func MustLoadString(s string) *Properties { - return must(LoadString(s)) -} - -// MustLoadFile reads a file into a Properties struct and -// panics on error. -func MustLoadFile(filename string, enc Encoding) *Properties { - return must(LoadFile(filename, enc)) -} - -// MustLoadFiles reads multiple files in the given order into -// a Properties struct and panics on error. If 'ignoreMissing' -// is true then non-existent files will not be reported as error. -func MustLoadFiles(filenames []string, enc Encoding, ignoreMissing bool) *Properties { - return must(LoadFiles(filenames, enc, ignoreMissing)) -} - -// MustLoadURL reads the content of a URL into a Properties struct and -// panics on error. -func MustLoadURL(url string) *Properties { - return must(LoadURL(url)) -} - -// MustLoadURLs reads the content of multiple URLs in the given order into a -// Properties struct and panics on error. If 'ignoreMissing' is true then a 404 -// status code will not be reported as error. -func MustLoadURLs(urls []string, ignoreMissing bool) *Properties { - return must(LoadURLs(urls, ignoreMissing)) -} - -// MustLoadAll reads the content of multiple URLs or files in the given order into a -// Properties struct. If 'ignoreMissing' is true then a 404 status code or missing file will -// not be reported as error. Encoding sets the encoding for files. For the URLs please see -// LoadURL for the Content-Type header and the encoding. It panics on error. -func MustLoadAll(names []string, enc Encoding, ignoreMissing bool) *Properties { - return must(LoadAll(names, enc, ignoreMissing)) -} - -func must(p *Properties, err error) *Properties { - if err != nil { - ErrorHandler(err) - } - return p -} - -// expandName expands ${ENV_VAR} expressions in a name. -// If the environment variable does not exist then it will be replaced -// with an empty string. Malformed expressions like "${ENV_VAR" will -// be reported as error. -func expandName(name string) (string, error) { - return expand(name, []string{}, "${", "}", make(map[string]string)) -} - -// Interprets a byte buffer either as an ISO-8859-1 or UTF-8 encoded string. -// For ISO-8859-1 we can convert each byte straight into a rune since the -// first 256 unicode code points cover ISO-8859-1. -func convert(buf []byte, enc Encoding) string { - switch enc { - case utf8Default, UTF8: - return string(buf) - case ISO_8859_1: - runes := make([]rune, len(buf)) - for i, b := range buf { - runes[i] = rune(b) - } - return string(runes) - default: - ErrorHandler(fmt.Errorf("unsupported encoding %v", enc)) - } - panic("ErrorHandler should exit") -} diff --git a/vendor/github.com/magiconair/properties/parser.go b/vendor/github.com/magiconair/properties/parser.go deleted file mode 100644 index fccfd39f6b..0000000000 --- a/vendor/github.com/magiconair/properties/parser.go +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2013-2022 Frank Schroeder. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package properties - -import ( - "fmt" - "runtime" -) - -type parser struct { - lex *lexer -} - -func parse(input string) (properties *Properties, err error) { - p := &parser{lex: lex(input)} - defer p.recover(&err) - - properties = NewProperties() - key := "" - comments := []string{} - - for { - token := p.expectOneOf(itemComment, itemKey, itemEOF) - switch token.typ { - case itemEOF: - goto done - case itemComment: - comments = append(comments, token.val) - continue - case itemKey: - key = token.val - if _, ok := properties.m[key]; !ok { - properties.k = append(properties.k, key) - } - } - - token = p.expectOneOf(itemValue, itemEOF) - if len(comments) > 0 { - properties.c[key] = comments - comments = []string{} - } - switch token.typ { - case itemEOF: - properties.m[key] = "" - goto done - case itemValue: - properties.m[key] = token.val - } - } - -done: - return properties, nil -} - -func (p *parser) errorf(format string, args ...interface{}) { - format = fmt.Sprintf("properties: Line %d: %s", p.lex.lineNumber(), format) - panic(fmt.Errorf(format, args...)) -} - -func (p *parser) expectOneOf(expected ...itemType) (token item) { - token = p.lex.nextItem() - for _, v := range expected { - if token.typ == v { - return token - } - } - p.unexpected(token) - panic("unexpected token") -} - -func (p *parser) unexpected(token item) { - p.errorf(token.String()) -} - -// recover is the handler that turns panics into returns from the top level of Parse. -func (p *parser) recover(errp *error) { - e := recover() - if e != nil { - if _, ok := e.(runtime.Error); ok { - panic(e) - } - *errp = e.(error) - } -} diff --git a/vendor/github.com/magiconair/properties/properties.go b/vendor/github.com/magiconair/properties/properties.go deleted file mode 100644 index fb2f7b4048..0000000000 --- a/vendor/github.com/magiconair/properties/properties.go +++ /dev/null @@ -1,848 +0,0 @@ -// Copyright 2013-2022 Frank Schroeder. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package properties - -// BUG(frank): Set() does not check for invalid unicode literals since this is currently handled by the lexer. -// BUG(frank): Write() does not allow to configure the newline character. Therefore, on Windows LF is used. - -import ( - "bytes" - "fmt" - "io" - "log" - "os" - "regexp" - "sort" - "strconv" - "strings" - "time" - "unicode/utf8" -) - -const maxExpansionDepth = 64 - -// ErrorHandlerFunc defines the type of function which handles failures -// of the MustXXX() functions. An error handler function must exit -// the application after handling the error. -type ErrorHandlerFunc func(error) - -// ErrorHandler is the function which handles failures of the MustXXX() -// functions. The default is LogFatalHandler. -var ErrorHandler ErrorHandlerFunc = LogFatalHandler - -// LogHandlerFunc defines the function prototype for logging errors. -type LogHandlerFunc func(fmt string, args ...interface{}) - -// LogPrintf defines a log handler which uses log.Printf. -var LogPrintf LogHandlerFunc = log.Printf - -// LogFatalHandler handles the error by logging a fatal error and exiting. -func LogFatalHandler(err error) { - log.Fatal(err) -} - -// PanicHandler handles the error by panicking. -func PanicHandler(err error) { - panic(err) -} - -// ----------------------------------------------------------------------------- - -// A Properties contains the key/value pairs from the properties input. -// All values are stored in unexpanded form and are expanded at runtime -type Properties struct { - // Pre-/Postfix for property expansion. - Prefix string - Postfix string - - // DisableExpansion controls the expansion of properties on Get() - // and the check for circular references on Set(). When set to - // true Properties behaves like a simple key/value store and does - // not check for circular references on Get() or on Set(). - DisableExpansion bool - - // Stores the key/value pairs - m map[string]string - - // Stores the comments per key. - c map[string][]string - - // Stores the keys in order of appearance. - k []string - - // WriteSeparator specifies the separator of key and value while writing the properties. - WriteSeparator string -} - -// NewProperties creates a new Properties struct with the default -// configuration for "${key}" expressions. -func NewProperties() *Properties { - return &Properties{ - Prefix: "${", - Postfix: "}", - m: map[string]string{}, - c: map[string][]string{}, - k: []string{}, - } -} - -// Load reads a buffer into the given Properties struct. -func (p *Properties) Load(buf []byte, enc Encoding) error { - l := &Loader{Encoding: enc, DisableExpansion: p.DisableExpansion} - newProperties, err := l.LoadBytes(buf) - if err != nil { - return err - } - p.Merge(newProperties) - return nil -} - -// Get returns the expanded value for the given key if exists. -// Otherwise, ok is false. -func (p *Properties) Get(key string) (value string, ok bool) { - v, ok := p.m[key] - if p.DisableExpansion { - return v, ok - } - if !ok { - return "", false - } - - expanded, err := p.expand(key, v) - - // we guarantee that the expanded value is free of - // circular references and malformed expressions - // so we panic if we still get an error here. - if err != nil { - ErrorHandler(err) - } - - return expanded, true -} - -// MustGet returns the expanded value for the given key if exists. -// Otherwise, it panics. -func (p *Properties) MustGet(key string) string { - if v, ok := p.Get(key); ok { - return v - } - ErrorHandler(invalidKeyError(key)) - panic("ErrorHandler should exit") -} - -// ---------------------------------------------------------------------------- - -// ClearComments removes the comments for all keys. -func (p *Properties) ClearComments() { - p.c = map[string][]string{} -} - -// ---------------------------------------------------------------------------- - -// GetComment returns the last comment before the given key or an empty string. -func (p *Properties) GetComment(key string) string { - comments, ok := p.c[key] - if !ok || len(comments) == 0 { - return "" - } - return comments[len(comments)-1] -} - -// ---------------------------------------------------------------------------- - -// GetComments returns all comments that appeared before the given key or nil. -func (p *Properties) GetComments(key string) []string { - if comments, ok := p.c[key]; ok { - return comments - } - return nil -} - -// ---------------------------------------------------------------------------- - -// SetComment sets the comment for the key. -func (p *Properties) SetComment(key, comment string) { - p.c[key] = []string{comment} -} - -// ---------------------------------------------------------------------------- - -// SetComments sets the comments for the key. If the comments are nil then -// all comments for this key are deleted. -func (p *Properties) SetComments(key string, comments []string) { - if comments == nil { - delete(p.c, key) - return - } - p.c[key] = comments -} - -// ---------------------------------------------------------------------------- - -// GetBool checks if the expanded value is one of '1', 'yes', -// 'true' or 'on' if the key exists. The comparison is case-insensitive. -// If the key does not exist the default value is returned. -func (p *Properties) GetBool(key string, def bool) bool { - v, err := p.getBool(key) - if err != nil { - return def - } - return v -} - -// MustGetBool checks if the expanded value is one of '1', 'yes', -// 'true' or 'on' if the key exists. The comparison is case-insensitive. -// If the key does not exist the function panics. -func (p *Properties) MustGetBool(key string) bool { - v, err := p.getBool(key) - if err != nil { - ErrorHandler(err) - } - return v -} - -func (p *Properties) getBool(key string) (value bool, err error) { - if v, ok := p.Get(key); ok { - return boolVal(v), nil - } - return false, invalidKeyError(key) -} - -func boolVal(v string) bool { - v = strings.ToLower(v) - return v == "1" || v == "true" || v == "yes" || v == "on" -} - -// ---------------------------------------------------------------------------- - -// GetDuration parses the expanded value as an time.Duration (in ns) if the -// key exists. If key does not exist or the value cannot be parsed the default -// value is returned. In almost all cases you want to use GetParsedDuration(). -func (p *Properties) GetDuration(key string, def time.Duration) time.Duration { - v, err := p.getInt64(key) - if err != nil { - return def - } - return time.Duration(v) -} - -// MustGetDuration parses the expanded value as an time.Duration (in ns) if -// the key exists. If key does not exist or the value cannot be parsed the -// function panics. In almost all cases you want to use MustGetParsedDuration(). -func (p *Properties) MustGetDuration(key string) time.Duration { - v, err := p.getInt64(key) - if err != nil { - ErrorHandler(err) - } - return time.Duration(v) -} - -// ---------------------------------------------------------------------------- - -// GetParsedDuration parses the expanded value with time.ParseDuration() if the key exists. -// If key does not exist or the value cannot be parsed the default -// value is returned. -func (p *Properties) GetParsedDuration(key string, def time.Duration) time.Duration { - s, ok := p.Get(key) - if !ok { - return def - } - v, err := time.ParseDuration(s) - if err != nil { - return def - } - return v -} - -// MustGetParsedDuration parses the expanded value with time.ParseDuration() if the key exists. -// If key does not exist or the value cannot be parsed the function panics. -func (p *Properties) MustGetParsedDuration(key string) time.Duration { - s, ok := p.Get(key) - if !ok { - ErrorHandler(invalidKeyError(key)) - } - v, err := time.ParseDuration(s) - if err != nil { - ErrorHandler(err) - } - return v -} - -// ---------------------------------------------------------------------------- - -// GetFloat64 parses the expanded value as a float64 if the key exists. -// If key does not exist or the value cannot be parsed the default -// value is returned. -func (p *Properties) GetFloat64(key string, def float64) float64 { - v, err := p.getFloat64(key) - if err != nil { - return def - } - return v -} - -// MustGetFloat64 parses the expanded value as a float64 if the key exists. -// If key does not exist or the value cannot be parsed the function panics. -func (p *Properties) MustGetFloat64(key string) float64 { - v, err := p.getFloat64(key) - if err != nil { - ErrorHandler(err) - } - return v -} - -func (p *Properties) getFloat64(key string) (value float64, err error) { - if v, ok := p.Get(key); ok { - value, err = strconv.ParseFloat(v, 64) - if err != nil { - return 0, err - } - return value, nil - } - return 0, invalidKeyError(key) -} - -// ---------------------------------------------------------------------------- - -// GetInt parses the expanded value as an int if the key exists. -// If key does not exist or the value cannot be parsed the default -// value is returned. If the value does not fit into an int the -// function panics with an out of range error. -func (p *Properties) GetInt(key string, def int) int { - v, err := p.getInt64(key) - if err != nil { - return def - } - return intRangeCheck(key, v) -} - -// MustGetInt parses the expanded value as an int if the key exists. -// If key does not exist or the value cannot be parsed the function panics. -// If the value does not fit into an int the function panics with -// an out of range error. -func (p *Properties) MustGetInt(key string) int { - v, err := p.getInt64(key) - if err != nil { - ErrorHandler(err) - } - return intRangeCheck(key, v) -} - -// ---------------------------------------------------------------------------- - -// GetInt64 parses the expanded value as an int64 if the key exists. -// If key does not exist or the value cannot be parsed the default -// value is returned. -func (p *Properties) GetInt64(key string, def int64) int64 { - v, err := p.getInt64(key) - if err != nil { - return def - } - return v -} - -// MustGetInt64 parses the expanded value as an int if the key exists. -// If key does not exist or the value cannot be parsed the function panics. -func (p *Properties) MustGetInt64(key string) int64 { - v, err := p.getInt64(key) - if err != nil { - ErrorHandler(err) - } - return v -} - -func (p *Properties) getInt64(key string) (value int64, err error) { - if v, ok := p.Get(key); ok { - value, err = strconv.ParseInt(v, 10, 64) - if err != nil { - return 0, err - } - return value, nil - } - return 0, invalidKeyError(key) -} - -// ---------------------------------------------------------------------------- - -// GetUint parses the expanded value as an uint if the key exists. -// If key does not exist or the value cannot be parsed the default -// value is returned. If the value does not fit into an int the -// function panics with an out of range error. -func (p *Properties) GetUint(key string, def uint) uint { - v, err := p.getUint64(key) - if err != nil { - return def - } - return uintRangeCheck(key, v) -} - -// MustGetUint parses the expanded value as an int if the key exists. -// If key does not exist or the value cannot be parsed the function panics. -// If the value does not fit into an int the function panics with -// an out of range error. -func (p *Properties) MustGetUint(key string) uint { - v, err := p.getUint64(key) - if err != nil { - ErrorHandler(err) - } - return uintRangeCheck(key, v) -} - -// ---------------------------------------------------------------------------- - -// GetUint64 parses the expanded value as an uint64 if the key exists. -// If key does not exist or the value cannot be parsed the default -// value is returned. -func (p *Properties) GetUint64(key string, def uint64) uint64 { - v, err := p.getUint64(key) - if err != nil { - return def - } - return v -} - -// MustGetUint64 parses the expanded value as an int if the key exists. -// If key does not exist or the value cannot be parsed the function panics. -func (p *Properties) MustGetUint64(key string) uint64 { - v, err := p.getUint64(key) - if err != nil { - ErrorHandler(err) - } - return v -} - -func (p *Properties) getUint64(key string) (value uint64, err error) { - if v, ok := p.Get(key); ok { - value, err = strconv.ParseUint(v, 10, 64) - if err != nil { - return 0, err - } - return value, nil - } - return 0, invalidKeyError(key) -} - -// ---------------------------------------------------------------------------- - -// GetString returns the expanded value for the given key if exists or -// the default value otherwise. -func (p *Properties) GetString(key, def string) string { - if v, ok := p.Get(key); ok { - return v - } - return def -} - -// MustGetString returns the expanded value for the given key if exists or -// panics otherwise. -func (p *Properties) MustGetString(key string) string { - if v, ok := p.Get(key); ok { - return v - } - ErrorHandler(invalidKeyError(key)) - panic("ErrorHandler should exit") -} - -// ---------------------------------------------------------------------------- - -// Filter returns a new properties object which contains all properties -// for which the key matches the pattern. -func (p *Properties) Filter(pattern string) (*Properties, error) { - re, err := regexp.Compile(pattern) - if err != nil { - return nil, err - } - - return p.FilterRegexp(re), nil -} - -// FilterRegexp returns a new properties object which contains all properties -// for which the key matches the regular expression. -func (p *Properties) FilterRegexp(re *regexp.Regexp) *Properties { - pp := NewProperties() - for _, k := range p.k { - if re.MatchString(k) { - // TODO(fs): we are ignoring the error which flags a circular reference. - // TODO(fs): since we are just copying a subset of keys this cannot happen (fingers crossed) - pp.Set(k, p.m[k]) - } - } - return pp -} - -// FilterPrefix returns a new properties object with a subset of all keys -// with the given prefix. -func (p *Properties) FilterPrefix(prefix string) *Properties { - pp := NewProperties() - for _, k := range p.k { - if strings.HasPrefix(k, prefix) { - // TODO(fs): we are ignoring the error which flags a circular reference. - // TODO(fs): since we are just copying a subset of keys this cannot happen (fingers crossed) - pp.Set(k, p.m[k]) - } - } - return pp -} - -// FilterStripPrefix returns a new properties object with a subset of all keys -// with the given prefix and the prefix removed from the keys. -func (p *Properties) FilterStripPrefix(prefix string) *Properties { - pp := NewProperties() - n := len(prefix) - for _, k := range p.k { - if len(k) > len(prefix) && strings.HasPrefix(k, prefix) { - // TODO(fs): we are ignoring the error which flags a circular reference. - // TODO(fs): since we are modifying keys I am not entirely sure whether we can create a circular reference - // TODO(fs): this function should probably return an error but the signature is fixed - pp.Set(k[n:], p.m[k]) - } - } - return pp -} - -// Len returns the number of keys. -func (p *Properties) Len() int { - return len(p.m) -} - -// Keys returns all keys in the same order as in the input. -func (p *Properties) Keys() []string { - keys := make([]string, len(p.k)) - copy(keys, p.k) - return keys -} - -// Set sets the property key to the corresponding value. -// If a value for key existed before then ok is true and prev -// contains the previous value. If the value contains a -// circular reference or a malformed expression then -// an error is returned. -// An empty key is silently ignored. -func (p *Properties) Set(key, value string) (prev string, ok bool, err error) { - if key == "" { - return "", false, nil - } - - // if expansion is disabled we allow circular references - if p.DisableExpansion { - prev, ok = p.Get(key) - p.m[key] = value - if !ok { - p.k = append(p.k, key) - } - return prev, ok, nil - } - - // to check for a circular reference we temporarily need - // to set the new value. If there is an error then revert - // to the previous state. Only if all tests are successful - // then we add the key to the p.k list. - prev, ok = p.Get(key) - p.m[key] = value - - // now check for a circular reference - _, err = p.expand(key, value) - if err != nil { - - // revert to the previous state - if ok { - p.m[key] = prev - } else { - delete(p.m, key) - } - - return "", false, err - } - - if !ok { - p.k = append(p.k, key) - } - - return prev, ok, nil -} - -// SetValue sets property key to the default string value -// as defined by fmt.Sprintf("%v"). -func (p *Properties) SetValue(key string, value interface{}) error { - _, _, err := p.Set(key, fmt.Sprintf("%v", value)) - return err -} - -// MustSet sets the property key to the corresponding value. -// If a value for key existed before then ok is true and prev -// contains the previous value. An empty key is silently ignored. -func (p *Properties) MustSet(key, value string) (prev string, ok bool) { - prev, ok, err := p.Set(key, value) - if err != nil { - ErrorHandler(err) - } - return prev, ok -} - -// String returns a string of all expanded 'key = value' pairs. -func (p *Properties) String() string { - var s string - for _, key := range p.k { - value, _ := p.Get(key) - s = fmt.Sprintf("%s%s = %s\n", s, key, value) - } - return s -} - -// Sort sorts the properties keys in alphabetical order. -// This is helpfully before writing the properties. -func (p *Properties) Sort() { - sort.Strings(p.k) -} - -// Write writes all unexpanded 'key = value' pairs to the given writer. -// Write returns the number of bytes written and any write error encountered. -func (p *Properties) Write(w io.Writer, enc Encoding) (n int, err error) { - return p.WriteComment(w, "", enc) -} - -// WriteComment writes all unexpanced 'key = value' pairs to the given writer. -// If prefix is not empty then comments are written with a blank line and the -// given prefix. The prefix should be either "# " or "! " to be compatible with -// the properties file format. Otherwise, the properties parser will not be -// able to read the file back in. It returns the number of bytes written and -// any write error encountered. -func (p *Properties) WriteComment(w io.Writer, prefix string, enc Encoding) (n int, err error) { - var x int - - for _, key := range p.k { - value := p.m[key] - - if prefix != "" { - if comments, ok := p.c[key]; ok { - // don't print comments if they are all empty - allEmpty := true - for _, c := range comments { - if c != "" { - allEmpty = false - break - } - } - - if !allEmpty { - // add a blank line between entries but not at the top - if len(comments) > 0 && n > 0 { - x, err = fmt.Fprintln(w) - if err != nil { - return - } - n += x - } - - for _, c := range comments { - x, err = fmt.Fprintf(w, "%s%s\n", prefix, c) - if err != nil { - return - } - n += x - } - } - } - } - sep := " = " - if p.WriteSeparator != "" { - sep = p.WriteSeparator - } - x, err = fmt.Fprintf(w, "%s%s%s\n", encode(key, " :", enc), sep, encode(value, "", enc)) - if err != nil { - return - } - n += x - } - return -} - -// Map returns a copy of the properties as a map. -func (p *Properties) Map() map[string]string { - m := make(map[string]string) - for k, v := range p.m { - m[k] = v - } - return m -} - -// FilterFunc returns a copy of the properties which includes the values which passed all filters. -func (p *Properties) FilterFunc(filters ...func(k, v string) bool) *Properties { - pp := NewProperties() -outer: - for k, v := range p.m { - for _, f := range filters { - if !f(k, v) { - continue outer - } - pp.Set(k, v) - } - } - return pp -} - -// ---------------------------------------------------------------------------- - -// Delete removes the key and its comments. -func (p *Properties) Delete(key string) { - delete(p.m, key) - delete(p.c, key) - newKeys := []string{} - for _, k := range p.k { - if k != key { - newKeys = append(newKeys, k) - } - } - p.k = newKeys -} - -// Merge merges properties, comments and keys from other *Properties into p -func (p *Properties) Merge(other *Properties) { - for _, k := range other.k { - if _, ok := p.m[k]; !ok { - p.k = append(p.k, k) - } - } - for k, v := range other.m { - p.m[k] = v - } - for k, v := range other.c { - p.c[k] = v - } -} - -// ---------------------------------------------------------------------------- - -// check expands all values and returns an error if a circular reference or -// a malformed expression was found. -func (p *Properties) check() error { - for key, value := range p.m { - if _, err := p.expand(key, value); err != nil { - return err - } - } - return nil -} - -func (p *Properties) expand(key, input string) (string, error) { - // no pre/postfix -> nothing to expand - if p.Prefix == "" && p.Postfix == "" { - return input, nil - } - - return expand(input, []string{key}, p.Prefix, p.Postfix, p.m) -} - -// expand recursively expands expressions of '(prefix)key(postfix)' to their corresponding values. -// The function keeps track of the keys that were already expanded and stops if it -// detects a circular reference or a malformed expression of the form '(prefix)key'. -func expand(s string, keys []string, prefix, postfix string, values map[string]string) (string, error) { - if len(keys) > maxExpansionDepth { - return "", fmt.Errorf("expansion too deep") - } - - for { - start := strings.Index(s, prefix) - if start == -1 { - return s, nil - } - - keyStart := start + len(prefix) - keyLen := strings.Index(s[keyStart:], postfix) - if keyLen == -1 { - return "", fmt.Errorf("malformed expression") - } - - end := keyStart + keyLen + len(postfix) - 1 - key := s[keyStart : keyStart+keyLen] - - // fmt.Printf("s:%q pp:%q start:%d end:%d keyStart:%d keyLen:%d key:%q\n", s, prefix + "..." + postfix, start, end, keyStart, keyLen, key) - - for _, k := range keys { - if key == k { - var b bytes.Buffer - b.WriteString("circular reference in:\n") - for _, k1 := range keys { - fmt.Fprintf(&b, "%s=%s\n", k1, values[k1]) - } - return "", fmt.Errorf(b.String()) - } - } - - val, ok := values[key] - if !ok { - val = os.Getenv(key) - } - new_val, err := expand(val, append(keys, key), prefix, postfix, values) - if err != nil { - return "", err - } - s = s[:start] + new_val + s[end+1:] - } -} - -// encode encodes a UTF-8 string to ISO-8859-1 and escapes some characters. -func encode(s string, special string, enc Encoding) string { - switch enc { - case UTF8: - return encodeUtf8(s, special) - case ISO_8859_1: - return encodeIso(s, special) - default: - panic(fmt.Sprintf("unsupported encoding %v", enc)) - } -} - -func encodeUtf8(s string, special string) string { - v := "" - for pos := 0; pos < len(s); { - r, w := utf8.DecodeRuneInString(s[pos:]) - pos += w - v += escape(r, special) - } - return v -} - -func encodeIso(s string, special string) string { - var r rune - var w int - var v string - for pos := 0; pos < len(s); { - switch r, w = utf8.DecodeRuneInString(s[pos:]); { - case r < 1<<8: // single byte rune -> escape special chars only - v += escape(r, special) - case r < 1<<16: // two byte rune -> unicode literal - v += fmt.Sprintf("\\u%04x", r) - default: // more than two bytes per rune -> can't encode - v += "?" - } - pos += w - } - return v -} - -func escape(r rune, special string) string { - switch r { - case '\f': - return "\\f" - case '\n': - return "\\n" - case '\r': - return "\\r" - case '\t': - return "\\t" - case '\\': - return "\\\\" - default: - if strings.ContainsRune(special, r) { - return "\\" + string(r) - } - return string(r) - } -} - -func invalidKeyError(key string) error { - return fmt.Errorf("unknown property: %s", key) -} diff --git a/vendor/github.com/magiconair/properties/rangecheck.go b/vendor/github.com/magiconair/properties/rangecheck.go deleted file mode 100644 index dbd60b36e7..0000000000 --- a/vendor/github.com/magiconair/properties/rangecheck.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2013-2022 Frank Schroeder. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package properties - -import ( - "fmt" - "math" -) - -// make this a var to overwrite it in a test -var is32Bit = ^uint(0) == math.MaxUint32 - -// intRangeCheck checks if the value fits into the int type and -// panics if it does not. -func intRangeCheck(key string, v int64) int { - if is32Bit && (v < math.MinInt32 || v > math.MaxInt32) { - panic(fmt.Sprintf("Value %d for key %s out of range", v, key)) - } - return int(v) -} - -// uintRangeCheck checks if the value fits into the uint type and -// panics if it does not. -func uintRangeCheck(key string, v uint64) uint { - if is32Bit && v > math.MaxUint32 { - panic(fmt.Sprintf("Value %d for key %s out of range", v, key)) - } - return uint(v) -} diff --git a/vendor/github.com/mailru/easyjson/jlexer/bytestostr.go b/vendor/github.com/mailru/easyjson/jlexer/bytestostr.go index ff7b27c5b2..e68108f868 100644 --- a/vendor/github.com/mailru/easyjson/jlexer/bytestostr.go +++ b/vendor/github.com/mailru/easyjson/jlexer/bytestostr.go @@ -8,7 +8,6 @@ package jlexer import ( - "reflect" "unsafe" ) @@ -18,7 +17,5 @@ import ( // chunk may be either blocked from being freed by GC because of a single string or the buffer.Data // may be garbage-collected even when the string exists. func bytesToStr(data []byte) string { - h := (*reflect.SliceHeader)(unsafe.Pointer(&data)) - shdr := reflect.StringHeader{Data: h.Data, Len: h.Len} - return *(*string)(unsafe.Pointer(&shdr)) + return *(*string)(unsafe.Pointer(&data)) } diff --git a/vendor/github.com/mailru/easyjson/jlexer/lexer.go b/vendor/github.com/mailru/easyjson/jlexer/lexer.go index b5f5e26132..a27705b12b 100644 --- a/vendor/github.com/mailru/easyjson/jlexer/lexer.go +++ b/vendor/github.com/mailru/easyjson/jlexer/lexer.go @@ -19,21 +19,21 @@ import ( "github.com/josharian/intern" ) -// tokenKind determines type of a token. -type tokenKind byte +// TokenKind determines type of a token. +type TokenKind byte const ( - tokenUndef tokenKind = iota // No token. - tokenDelim // Delimiter: one of '{', '}', '[' or ']'. - tokenString // A string literal, e.g. "abc\u1234" - tokenNumber // Number literal, e.g. 1.5e5 - tokenBool // Boolean literal: true or false. - tokenNull // null keyword. + TokenUndef TokenKind = iota // No token. + TokenDelim // Delimiter: one of '{', '}', '[' or ']'. + TokenString // A string literal, e.g. "abc\u1234" + TokenNumber // Number literal, e.g. 1.5e5 + TokenBool // Boolean literal: true or false. + TokenNull // null keyword. ) // token describes a single token: type, position in the input and value. type token struct { - kind tokenKind // Type of a token. + kind TokenKind // Type of a token. boolValue bool // Value if a boolean literal token. byteValueCloned bool // true if byteValue was allocated and does not refer to original json body @@ -47,7 +47,7 @@ type Lexer struct { start int // Start of the current token. pos int // Current unscanned position in the input stream. - token token // Last scanned token, if token.kind != tokenUndef. + token token // Last scanned token, if token.kind != TokenUndef. firstElement bool // Whether current element is the first in array or an object. wantSep byte // A comma or a colon character, which need to occur before a token. @@ -59,7 +59,7 @@ type Lexer struct { // FetchToken scans the input for the next token. func (r *Lexer) FetchToken() { - r.token.kind = tokenUndef + r.token.kind = TokenUndef r.start = r.pos // Check if r.Data has r.pos element @@ -90,7 +90,7 @@ func (r *Lexer) FetchToken() { r.errSyntax() } - r.token.kind = tokenString + r.token.kind = TokenString r.fetchString() return @@ -99,7 +99,7 @@ func (r *Lexer) FetchToken() { r.errSyntax() } r.firstElement = true - r.token.kind = tokenDelim + r.token.kind = TokenDelim r.token.delimValue = r.Data[r.pos] r.pos++ return @@ -109,7 +109,7 @@ func (r *Lexer) FetchToken() { r.errSyntax() } r.wantSep = 0 - r.token.kind = tokenDelim + r.token.kind = TokenDelim r.token.delimValue = r.Data[r.pos] r.pos++ return @@ -118,7 +118,7 @@ func (r *Lexer) FetchToken() { if r.wantSep != 0 { r.errSyntax() } - r.token.kind = tokenNumber + r.token.kind = TokenNumber r.fetchNumber() return @@ -127,7 +127,7 @@ func (r *Lexer) FetchToken() { r.errSyntax() } - r.token.kind = tokenNull + r.token.kind = TokenNull r.fetchNull() return @@ -136,7 +136,7 @@ func (r *Lexer) FetchToken() { r.errSyntax() } - r.token.kind = tokenBool + r.token.kind = TokenBool r.token.boolValue = true r.fetchTrue() return @@ -146,7 +146,7 @@ func (r *Lexer) FetchToken() { r.errSyntax() } - r.token.kind = tokenBool + r.token.kind = TokenBool r.token.boolValue = false r.fetchFalse() return @@ -391,7 +391,7 @@ func (r *Lexer) fetchString() { // scanToken scans the next token if no token is currently available in the lexer. func (r *Lexer) scanToken() { - if r.token.kind != tokenUndef || r.fatalError != nil { + if r.token.kind != TokenUndef || r.fatalError != nil { return } @@ -400,7 +400,7 @@ func (r *Lexer) scanToken() { // consume resets the current token to allow scanning the next one. func (r *Lexer) consume() { - r.token.kind = tokenUndef + r.token.kind = TokenUndef r.token.byteValueCloned = false r.token.delimValue = 0 } @@ -443,10 +443,10 @@ func (r *Lexer) errInvalidToken(expected string) { switch expected { case "[": r.token.delimValue = ']' - r.token.kind = tokenDelim + r.token.kind = TokenDelim case "{": r.token.delimValue = '}' - r.token.kind = tokenDelim + r.token.kind = TokenDelim } r.addNonfatalError(&LexerError{ Reason: fmt.Sprintf("expected %s", expected), @@ -475,7 +475,7 @@ func (r *Lexer) GetPos() int { // Delim consumes a token and verifies that it is the given delimiter. func (r *Lexer) Delim(c byte) { - if r.token.kind == tokenUndef && r.Ok() { + if r.token.kind == TokenUndef && r.Ok() { r.FetchToken() } @@ -489,7 +489,7 @@ func (r *Lexer) Delim(c byte) { // IsDelim returns true if there was no scanning error and next token is the given delimiter. func (r *Lexer) IsDelim(c byte) bool { - if r.token.kind == tokenUndef && r.Ok() { + if r.token.kind == TokenUndef && r.Ok() { r.FetchToken() } return !r.Ok() || r.token.delimValue == c @@ -497,10 +497,10 @@ func (r *Lexer) IsDelim(c byte) bool { // Null verifies that the next token is null and consumes it. func (r *Lexer) Null() { - if r.token.kind == tokenUndef && r.Ok() { + if r.token.kind == TokenUndef && r.Ok() { r.FetchToken() } - if !r.Ok() || r.token.kind != tokenNull { + if !r.Ok() || r.token.kind != TokenNull { r.errInvalidToken("null") } r.consume() @@ -508,15 +508,15 @@ func (r *Lexer) Null() { // IsNull returns true if the next token is a null keyword. func (r *Lexer) IsNull() bool { - if r.token.kind == tokenUndef && r.Ok() { + if r.token.kind == TokenUndef && r.Ok() { r.FetchToken() } - return r.Ok() && r.token.kind == tokenNull + return r.Ok() && r.token.kind == TokenNull } // Skip skips a single token. func (r *Lexer) Skip() { - if r.token.kind == tokenUndef && r.Ok() { + if r.token.kind == TokenUndef && r.Ok() { r.FetchToken() } r.consume() @@ -621,10 +621,10 @@ func (r *Lexer) Consumed() { } func (r *Lexer) unsafeString(skipUnescape bool) (string, []byte) { - if r.token.kind == tokenUndef && r.Ok() { + if r.token.kind == TokenUndef && r.Ok() { r.FetchToken() } - if !r.Ok() || r.token.kind != tokenString { + if !r.Ok() || r.token.kind != TokenString { r.errInvalidToken("string") return "", nil } @@ -664,10 +664,10 @@ func (r *Lexer) UnsafeFieldName(skipUnescape bool) string { // String reads a string literal. func (r *Lexer) String() string { - if r.token.kind == tokenUndef && r.Ok() { + if r.token.kind == TokenUndef && r.Ok() { r.FetchToken() } - if !r.Ok() || r.token.kind != tokenString { + if !r.Ok() || r.token.kind != TokenString { r.errInvalidToken("string") return "" } @@ -687,10 +687,10 @@ func (r *Lexer) String() string { // StringIntern reads a string literal, and performs string interning on it. func (r *Lexer) StringIntern() string { - if r.token.kind == tokenUndef && r.Ok() { + if r.token.kind == TokenUndef && r.Ok() { r.FetchToken() } - if !r.Ok() || r.token.kind != tokenString { + if !r.Ok() || r.token.kind != TokenString { r.errInvalidToken("string") return "" } @@ -705,10 +705,10 @@ func (r *Lexer) StringIntern() string { // Bytes reads a string literal and base64 decodes it into a byte slice. func (r *Lexer) Bytes() []byte { - if r.token.kind == tokenUndef && r.Ok() { + if r.token.kind == TokenUndef && r.Ok() { r.FetchToken() } - if !r.Ok() || r.token.kind != tokenString { + if !r.Ok() || r.token.kind != TokenString { r.errInvalidToken("string") return nil } @@ -731,10 +731,10 @@ func (r *Lexer) Bytes() []byte { // Bool reads a true or false boolean keyword. func (r *Lexer) Bool() bool { - if r.token.kind == tokenUndef && r.Ok() { + if r.token.kind == TokenUndef && r.Ok() { r.FetchToken() } - if !r.Ok() || r.token.kind != tokenBool { + if !r.Ok() || r.token.kind != TokenBool { r.errInvalidToken("bool") return false } @@ -744,10 +744,10 @@ func (r *Lexer) Bool() bool { } func (r *Lexer) number() string { - if r.token.kind == tokenUndef && r.Ok() { + if r.token.kind == TokenUndef && r.Ok() { r.FetchToken() } - if !r.Ok() || r.token.kind != tokenNumber { + if !r.Ok() || r.token.kind != TokenNumber { r.errInvalidToken("number") return "" } @@ -1151,7 +1151,7 @@ func (r *Lexer) GetNonFatalErrors() []*LexerError { // JsonNumber fetches and json.Number from 'encoding/json' package. // Both int, float or string, contains them are valid values func (r *Lexer) JsonNumber() json.Number { - if r.token.kind == tokenUndef && r.Ok() { + if r.token.kind == TokenUndef && r.Ok() { r.FetchToken() } if !r.Ok() { @@ -1160,11 +1160,11 @@ func (r *Lexer) JsonNumber() json.Number { } switch r.token.kind { - case tokenString: + case TokenString: return json.Number(r.String()) - case tokenNumber: + case TokenNumber: return json.Number(r.Raw()) - case tokenNull: + case TokenNull: r.Null() return json.Number("") default: @@ -1175,7 +1175,7 @@ func (r *Lexer) JsonNumber() json.Number { // Interface fetches an interface{} analogous to the 'encoding/json' package. func (r *Lexer) Interface() interface{} { - if r.token.kind == tokenUndef && r.Ok() { + if r.token.kind == TokenUndef && r.Ok() { r.FetchToken() } @@ -1183,13 +1183,13 @@ func (r *Lexer) Interface() interface{} { return nil } switch r.token.kind { - case tokenString: + case TokenString: return r.String() - case tokenNumber: + case TokenNumber: return r.Float64() - case tokenBool: + case TokenBool: return r.Bool() - case tokenNull: + case TokenNull: r.Null() return nil } @@ -1242,3 +1242,16 @@ func (r *Lexer) WantColon() { r.wantSep = ':' r.firstElement = false } + +// CurrentToken returns current token kind if there were no errors and TokenUndef otherwise +func (r *Lexer) CurrentToken() TokenKind { + if r.token.kind == TokenUndef && r.Ok() { + r.FetchToken() + } + + if !r.Ok() { + return TokenUndef + } + + return r.token.kind +} diff --git a/vendor/github.com/mailru/easyjson/jwriter/writer.go b/vendor/github.com/mailru/easyjson/jwriter/writer.go index 2c5b20105b..34b0ade468 100644 --- a/vendor/github.com/mailru/easyjson/jwriter/writer.go +++ b/vendor/github.com/mailru/easyjson/jwriter/writer.go @@ -67,6 +67,18 @@ func (w *Writer) RawString(s string) { w.Buffer.AppendString(s) } +// RawBytesString appends string from bytes to the buffer. +func (w *Writer) RawBytesString(data []byte, err error) { + switch { + case w.Error != nil: + return + case err != nil: + w.Error = err + default: + w.String(string(data)) + } +} + // Raw appends raw binary data to the buffer or sets the error if it is given. Useful for // calling with results of MarshalJSON-like functions. func (w *Writer) Raw(data []byte, err error) { diff --git a/vendor/github.com/osrg/gobgp/v3/pkg/packet/bgp/bgp.go b/vendor/github.com/osrg/gobgp/v3/pkg/packet/bgp/bgp.go index f16af09d6e..17211f1249 100644 --- a/vendor/github.com/osrg/gobgp/v3/pkg/packet/bgp/bgp.go +++ b/vendor/github.com/osrg/gobgp/v3/pkg/packet/bgp/bgp.go @@ -1094,7 +1094,7 @@ func (c *CapSoftwareVersion) DecodeFromBytes(data []byte) error { return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "Not all CapabilitySoftwareVersion bytes allowed") } softwareVersionLen := uint8(data[0]) - if len(data[1:]) < int(softwareVersionLen) || softwareVersionLen > 64 { + if len(data[1:]) < int(softwareVersionLen) || softwareVersionLen > 64 || softwareVersionLen == 0 { return NewMessageError(BGP_ERROR_OPEN_MESSAGE_ERROR, BGP_ERROR_SUB_UNSUPPORTED_CAPABILITY, nil, "invalid length of software version capablity") } c.SoftwareVersionLen = softwareVersionLen @@ -12708,6 +12708,10 @@ func parseGenericTransitiveExperimentalExtended(data []byte) (ExtendedCommunityI dscp := data[7] return NewTrafficRemarkExtended(dscp), nil case EC_SUBTYPE_FLOWSPEC_REDIRECT_IP6: + if len(data) < 20 { + return nil, NewMessageError(BGP_ERROR_UPDATE_MESSAGE_ERROR, BGP_ERROR_SUB_MALFORMED_ATTRIBUTE_LIST, nil, "not all extended community bytes for IPv6 FlowSpec are available") + } + ipv6 := net.IP(data[2:18]).String() localAdmin := binary.BigEndian.Uint16(data[18:20]) return NewRedirectIPv6AddressSpecificExtended(ipv6, localAdmin), nil diff --git a/vendor/github.com/pelletier/go-toml/v2/.goreleaser.yaml b/vendor/github.com/pelletier/go-toml/v2/.goreleaser.yaml index 1d8b69e65e..ec52857a3e 100644 --- a/vendor/github.com/pelletier/go-toml/v2/.goreleaser.yaml +++ b/vendor/github.com/pelletier/go-toml/v2/.goreleaser.yaml @@ -1,3 +1,4 @@ +version: 2 before: hooks: - go mod tidy diff --git a/vendor/github.com/pelletier/go-toml/v2/README.md b/vendor/github.com/pelletier/go-toml/v2/README.md index d964b25fe1..0755e55642 100644 --- a/vendor/github.com/pelletier/go-toml/v2/README.md +++ b/vendor/github.com/pelletier/go-toml/v2/README.md @@ -565,7 +565,7 @@ complete solutions exist out there. ## Versioning -Expect for parts explicitely marked otherwise, go-toml follows [Semantic +Expect for parts explicitly marked otherwise, go-toml follows [Semantic Versioning](https://semver.org). The supported version of [TOML](https://github.com/toml-lang/toml) is indicated at the beginning of this document. The last two major versions of Go are supported (see [Go Release diff --git a/vendor/github.com/pelletier/go-toml/v2/marshaler.go b/vendor/github.com/pelletier/go-toml/v2/marshaler.go index 7f4e20c128..161acd9343 100644 --- a/vendor/github.com/pelletier/go-toml/v2/marshaler.go +++ b/vendor/github.com/pelletier/go-toml/v2/marshaler.go @@ -8,7 +8,7 @@ import ( "io" "math" "reflect" - "sort" + "slices" "strconv" "strings" "time" @@ -280,7 +280,7 @@ func (enc *Encoder) encode(b []byte, ctx encoderCtx, v reflect.Value) ([]byte, e } hasTextMarshaler := v.Type().Implements(textMarshalerType) - if hasTextMarshaler || (v.CanAddr() && reflect.PtrTo(v.Type()).Implements(textMarshalerType)) { + if hasTextMarshaler || (v.CanAddr() && reflect.PointerTo(v.Type()).Implements(textMarshalerType)) { if !hasTextMarshaler { v = v.Addr() } @@ -631,6 +631,18 @@ func (enc *Encoder) keyToString(k reflect.Value) (string, error) { return "", fmt.Errorf("toml: error marshalling key %v from text: %w", k, err) } return string(keyB), nil + + case keyType.Kind() == reflect.Int || keyType.Kind() == reflect.Int8 || keyType.Kind() == reflect.Int16 || keyType.Kind() == reflect.Int32 || keyType.Kind() == reflect.Int64: + return strconv.FormatInt(k.Int(), 10), nil + + case keyType.Kind() == reflect.Uint || keyType.Kind() == reflect.Uint8 || keyType.Kind() == reflect.Uint16 || keyType.Kind() == reflect.Uint32 || keyType.Kind() == reflect.Uint64: + return strconv.FormatUint(k.Uint(), 10), nil + + case keyType.Kind() == reflect.Float32: + return strconv.FormatFloat(k.Float(), 'f', -1, 32), nil + + case keyType.Kind() == reflect.Float64: + return strconv.FormatFloat(k.Float(), 'f', -1, 64), nil } return "", fmt.Errorf("toml: type %s is not supported as a map key", keyType.Kind()) } @@ -668,8 +680,8 @@ func (enc *Encoder) encodeMap(b []byte, ctx encoderCtx, v reflect.Value) ([]byte } func sortEntriesByKey(e []entry) { - sort.Slice(e, func(i, j int) bool { - return e[i].Key < e[j].Key + slices.SortFunc(e, func(a, b entry) int { + return strings.Compare(a.Key, b.Key) }) } @@ -732,7 +744,7 @@ func walkStruct(ctx encoderCtx, t *table, v reflect.Value) { if fieldType.Anonymous { if fieldType.Type.Kind() == reflect.Struct { walkStruct(ctx, t, f) - } else if fieldType.Type.Kind() == reflect.Pointer && !f.IsNil() && f.Elem().Kind() == reflect.Struct { + } else if fieldType.Type.Kind() == reflect.Ptr && !f.IsNil() && f.Elem().Kind() == reflect.Struct { walkStruct(ctx, t, f.Elem()) } continue @@ -951,7 +963,7 @@ func willConvertToTable(ctx encoderCtx, v reflect.Value) bool { if !v.IsValid() { return false } - if v.Type() == timeType || v.Type().Implements(textMarshalerType) || (v.Kind() != reflect.Ptr && v.CanAddr() && reflect.PtrTo(v.Type()).Implements(textMarshalerType)) { + if v.Type() == timeType || v.Type().Implements(textMarshalerType) || (v.Kind() != reflect.Ptr && v.CanAddr() && reflect.PointerTo(v.Type()).Implements(textMarshalerType)) { return false } diff --git a/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go b/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go index 98231bae65..c3df8bee1c 100644 --- a/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go +++ b/vendor/github.com/pelletier/go-toml/v2/unmarshaler.go @@ -5,9 +5,9 @@ import ( "errors" "fmt" "io" - "io/ioutil" "math" "reflect" + "strconv" "strings" "sync/atomic" "time" @@ -21,10 +21,8 @@ import ( // // It is a shortcut for Decoder.Decode() with the default options. func Unmarshal(data []byte, v interface{}) error { - p := unstable.Parser{} - p.Reset(data) - d := decoder{p: &p} - + d := decoder{} + d.p.Reset(data) return d.FromParser(v) } @@ -117,27 +115,25 @@ func (d *Decoder) EnableUnmarshalerInterface() *Decoder { // Inline Table -> same as Table // Array of Tables -> same as Array and Table func (d *Decoder) Decode(v interface{}) error { - b, err := ioutil.ReadAll(d.r) + b, err := io.ReadAll(d.r) if err != nil { return fmt.Errorf("toml: %w", err) } - p := unstable.Parser{} - p.Reset(b) dec := decoder{ - p: &p, strict: strict{ Enabled: d.strict, }, unmarshalerInterface: d.unmarshalerInterface, } + dec.p.Reset(b) return dec.FromParser(v) } type decoder struct { // Which parser instance in use for this decoding session. - p *unstable.Parser + p unstable.Parser // Flag indicating that the current expression is stashed. // If set to true, calling nextExpr will not actually pull a new expression @@ -1078,12 +1074,39 @@ func (d *decoder) keyFromData(keyType reflect.Type, data []byte) (reflect.Value, } return mk, nil - case reflect.PtrTo(keyType).Implements(textUnmarshalerType): + case reflect.PointerTo(keyType).Implements(textUnmarshalerType): mk := reflect.New(keyType) if err := mk.Interface().(encoding.TextUnmarshaler).UnmarshalText(data); err != nil { return reflect.Value{}, fmt.Errorf("toml: error unmarshalling key type %s from text: %w", stringType, err) } return mk.Elem(), nil + + case keyType.Kind() == reflect.Int || keyType.Kind() == reflect.Int8 || keyType.Kind() == reflect.Int16 || keyType.Kind() == reflect.Int32 || keyType.Kind() == reflect.Int64: + key, err := strconv.ParseInt(string(data), 10, 64) + if err != nil { + return reflect.Value{}, fmt.Errorf("toml: error parsing key of type %s from integer: %w", stringType, err) + } + return reflect.ValueOf(key).Convert(keyType), nil + case keyType.Kind() == reflect.Uint || keyType.Kind() == reflect.Uint8 || keyType.Kind() == reflect.Uint16 || keyType.Kind() == reflect.Uint32 || keyType.Kind() == reflect.Uint64: + key, err := strconv.ParseUint(string(data), 10, 64) + if err != nil { + return reflect.Value{}, fmt.Errorf("toml: error parsing key of type %s from unsigned integer: %w", stringType, err) + } + return reflect.ValueOf(key).Convert(keyType), nil + + case keyType.Kind() == reflect.Float32: + key, err := strconv.ParseFloat(string(data), 32) + if err != nil { + return reflect.Value{}, fmt.Errorf("toml: error parsing key of type %s from float: %w", stringType, err) + } + return reflect.ValueOf(float32(key)), nil + + case keyType.Kind() == reflect.Float64: + key, err := strconv.ParseFloat(string(data), 64) + if err != nil { + return reflect.Value{}, fmt.Errorf("toml: error parsing key of type %s from float: %w", stringType, err) + } + return reflect.ValueOf(float64(key)), nil } return reflect.Value{}, fmt.Errorf("toml: cannot convert map key of type %s to expected type %s", stringType, keyType) } diff --git a/vendor/github.com/prometheus/client_golang/prometheus/desc.go b/vendor/github.com/prometheus/client_golang/prometheus/desc.go index 68ffe3c248..ad347113c0 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/desc.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/desc.go @@ -189,12 +189,15 @@ func (d *Desc) String() string { fmt.Sprintf("%s=%q", lp.GetName(), lp.GetValue()), ) } - vlStrings := make([]string, 0, len(d.variableLabels.names)) - for _, vl := range d.variableLabels.names { - if fn, ok := d.variableLabels.labelConstraints[vl]; ok && fn != nil { - vlStrings = append(vlStrings, fmt.Sprintf("c(%s)", vl)) - } else { - vlStrings = append(vlStrings, vl) + vlStrings := []string{} + if d.variableLabels != nil { + vlStrings = make([]string, 0, len(d.variableLabels.names)) + for _, vl := range d.variableLabels.names { + if fn, ok := d.variableLabels.labelConstraints[vl]; ok && fn != nil { + vlStrings = append(vlStrings, fmt.Sprintf("c(%s)", vl)) + } else { + vlStrings = append(vlStrings, vl) + } } } return fmt.Sprintf( diff --git a/vendor/github.com/prometheus/client_golang/prometheus/go_collector_latest.go b/vendor/github.com/prometheus/client_golang/prometheus/go_collector_latest.go index 5117464172..6b8684731c 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/go_collector_latest.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/go_collector_latest.go @@ -288,7 +288,7 @@ func NewGoCollector(opts ...func(o *internal.GoCollectorOptions)) Collector { } func attachOriginalName(desc, origName string) string { - return fmt.Sprintf("%s Sourced from %s", desc, origName) + return fmt.Sprintf("%s Sourced from %s.", desc, origName) } // Describe returns all descriptions of the collector. diff --git a/vendor/github.com/prometheus/client_golang/prometheus/histogram.go b/vendor/github.com/prometheus/client_golang/prometheus/histogram.go index 519db348a7..c453b754a7 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/histogram.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/histogram.go @@ -14,6 +14,7 @@ package prometheus import ( + "errors" "fmt" "math" "runtime" @@ -28,6 +29,11 @@ import ( "google.golang.org/protobuf/types/known/timestamppb" ) +const ( + nativeHistogramSchemaMaximum = 8 + nativeHistogramSchemaMinimum = -4 +) + // nativeHistogramBounds for the frac of observed values. Only relevant for // schema > 0. The position in the slice is the schema. (0 is never used, just // here for convenience of using the schema directly as the index.) @@ -330,11 +336,11 @@ func ExponentialBuckets(start, factor float64, count int) []float64 { // used for the Buckets field of HistogramOpts. // // The function panics if 'count' is 0 or negative, if 'min' is 0 or negative. -func ExponentialBucketsRange(min, max float64, count int) []float64 { +func ExponentialBucketsRange(minBucket, maxBucket float64, count int) []float64 { if count < 1 { panic("ExponentialBucketsRange count needs a positive count") } - if min <= 0 { + if minBucket <= 0 { panic("ExponentialBucketsRange min needs to be greater than 0") } @@ -342,12 +348,12 @@ func ExponentialBucketsRange(min, max float64, count int) []float64 { // max = min*growthFactor^(bucketCount-1) // We know max/min and highest bucket. Solve for growthFactor. - growthFactor := math.Pow(max/min, 1.0/float64(count-1)) + growthFactor := math.Pow(maxBucket/minBucket, 1.0/float64(count-1)) // Now that we know growthFactor, solve for each bucket. buckets := make([]float64, count) for i := 1; i <= count; i++ { - buckets[i-1] = min * math.Pow(growthFactor, float64(i-1)) + buckets[i-1] = minBucket * math.Pow(growthFactor, float64(i-1)) } return buckets } @@ -858,15 +864,35 @@ func (h *histogram) Write(out *dto.Metric) error { // findBucket returns the index of the bucket for the provided value, or // len(h.upperBounds) for the +Inf bucket. func (h *histogram) findBucket(v float64) int { - // TODO(beorn7): For small numbers of buckets (<30), a linear search is - // slightly faster than the binary search. If we really care, we could - // switch from one search strategy to the other depending on the number - // of buckets. - // - // Microbenchmarks (BenchmarkHistogramNoLabels): - // 11 buckets: 38.3 ns/op linear - binary 48.7 ns/op - // 100 buckets: 78.1 ns/op linear - binary 54.9 ns/op - // 300 buckets: 154 ns/op linear - binary 61.6 ns/op + n := len(h.upperBounds) + if n == 0 { + return 0 + } + + // Early exit: if v is less than or equal to the first upper bound, return 0 + if v <= h.upperBounds[0] { + return 0 + } + + // Early exit: if v is greater than the last upper bound, return len(h.upperBounds) + if v > h.upperBounds[n-1] { + return n + } + + // For small arrays, use simple linear search + // "magic number" 35 is result of tests on couple different (AWS and baremetal) servers + // see more details here: https://github.com/prometheus/client_golang/pull/1662 + if n < 35 { + for i, bound := range h.upperBounds { + if v <= bound { + return i + } + } + // If v is greater than all upper bounds, return len(h.upperBounds) + return n + } + + // For larger arrays, use stdlib's binary search return sort.SearchFloat64s(h.upperBounds, v) } @@ -1440,9 +1466,9 @@ func pickSchema(bucketFactor float64) int32 { floor := math.Floor(math.Log2(math.Log2(bucketFactor))) switch { case floor <= -8: - return 8 + return nativeHistogramSchemaMaximum case floor >= 4: - return -4 + return nativeHistogramSchemaMinimum default: return -int32(floor) } @@ -1835,3 +1861,196 @@ func (n *nativeExemplars) addExemplar(e *dto.Exemplar) { n.exemplars = append(n.exemplars[:nIdx], append([]*dto.Exemplar{e}, append(n.exemplars[nIdx:rIdx], n.exemplars[rIdx+1:]...)...)...) } } + +type constNativeHistogram struct { + desc *Desc + dto.Histogram + labelPairs []*dto.LabelPair +} + +func validateCount(sum float64, count uint64, negativeBuckets, positiveBuckets map[int]int64, zeroBucket uint64) error { + var bucketPopulationSum int64 + for _, v := range positiveBuckets { + bucketPopulationSum += v + } + for _, v := range negativeBuckets { + bucketPopulationSum += v + } + bucketPopulationSum += int64(zeroBucket) + + // If the sum of observations is NaN, the number of observations must be greater or equal to the sum of all bucket counts. + // Otherwise, the number of observations must be equal to the sum of all bucket counts . + + if math.IsNaN(sum) && bucketPopulationSum > int64(count) || + !math.IsNaN(sum) && bucketPopulationSum != int64(count) { + return errors.New("the sum of all bucket populations exceeds the count of observations") + } + return nil +} + +// NewConstNativeHistogram returns a metric representing a Prometheus native histogram with +// fixed values for the count, sum, and positive/negative/zero bucket counts. As those parameters +// cannot be changed, the returned value does not implement the Histogram +// interface (but only the Metric interface). Users of this package will not +// have much use for it in regular operations. However, when implementing custom +// OpenTelemetry Collectors, it is useful as a throw-away metric that is generated on the fly +// to send it to Prometheus in the Collect method. +// +// zeroBucket counts all (positive and negative) +// observations in the zero bucket (with an absolute value less or equal +// the current threshold). +// positiveBuckets and negativeBuckets are separate maps for negative and positive +// observations. The map's value is an int64, counting observations in +// that bucket. The map's key is the +// index of the bucket according to the used +// Schema. Index 0 is for an upper bound of 1 in positive buckets and for a lower bound of -1 in negative buckets. +// NewConstNativeHistogram returns an error if +// - the length of labelValues is not consistent with the variable labels in Desc or if Desc is invalid. +// - the schema passed is not between 8 and -4 +// - the sum of counts in all buckets including the zero bucket does not equal the count if sum is not NaN (or exceeds the count if sum is NaN) +// +// See https://opentelemetry.io/docs/specs/otel/compatibility/prometheus_and_openmetrics/#exponential-histograms for more details about the conversion from OTel to Prometheus. +func NewConstNativeHistogram( + desc *Desc, + count uint64, + sum float64, + positiveBuckets, negativeBuckets map[int]int64, + zeroBucket uint64, + schema int32, + zeroThreshold float64, + createdTimestamp time.Time, + labelValues ...string, +) (Metric, error) { + if desc.err != nil { + return nil, desc.err + } + if err := validateLabelValues(labelValues, len(desc.variableLabels.names)); err != nil { + return nil, err + } + if schema > nativeHistogramSchemaMaximum || schema < nativeHistogramSchemaMinimum { + return nil, errors.New("invalid native histogram schema") + } + if err := validateCount(sum, count, negativeBuckets, positiveBuckets, zeroBucket); err != nil { + return nil, err + } + + NegativeSpan, NegativeDelta := makeBucketsFromMap(negativeBuckets) + PositiveSpan, PositiveDelta := makeBucketsFromMap(positiveBuckets) + ret := &constNativeHistogram{ + desc: desc, + Histogram: dto.Histogram{ + CreatedTimestamp: timestamppb.New(createdTimestamp), + Schema: &schema, + ZeroThreshold: &zeroThreshold, + SampleCount: &count, + SampleSum: &sum, + + NegativeSpan: NegativeSpan, + NegativeDelta: NegativeDelta, + + PositiveSpan: PositiveSpan, + PositiveDelta: PositiveDelta, + + ZeroCount: proto.Uint64(zeroBucket), + }, + labelPairs: MakeLabelPairs(desc, labelValues), + } + if *ret.ZeroThreshold == 0 && *ret.ZeroCount == 0 && len(ret.PositiveSpan) == 0 && len(ret.NegativeSpan) == 0 { + ret.PositiveSpan = []*dto.BucketSpan{{ + Offset: proto.Int32(0), + Length: proto.Uint32(0), + }} + } + return ret, nil +} + +// MustNewConstNativeHistogram is a version of NewConstNativeHistogram that panics where +// NewConstNativeHistogram would have returned an error. +func MustNewConstNativeHistogram( + desc *Desc, + count uint64, + sum float64, + positiveBuckets, negativeBuckets map[int]int64, + zeroBucket uint64, + nativeHistogramSchema int32, + nativeHistogramZeroThreshold float64, + createdTimestamp time.Time, + labelValues ...string, +) Metric { + nativehistogram, err := NewConstNativeHistogram(desc, + count, + sum, + positiveBuckets, + negativeBuckets, + zeroBucket, + nativeHistogramSchema, + nativeHistogramZeroThreshold, + createdTimestamp, + labelValues...) + if err != nil { + panic(err) + } + return nativehistogram +} + +func (h *constNativeHistogram) Desc() *Desc { + return h.desc +} + +func (h *constNativeHistogram) Write(out *dto.Metric) error { + out.Histogram = &h.Histogram + out.Label = h.labelPairs + return nil +} + +func makeBucketsFromMap(buckets map[int]int64) ([]*dto.BucketSpan, []int64) { + if len(buckets) == 0 { + return nil, nil + } + var ii []int + for k := range buckets { + ii = append(ii, k) + } + sort.Ints(ii) + + var ( + spans []*dto.BucketSpan + deltas []int64 + prevCount int64 + nextI int + ) + + appendDelta := func(count int64) { + *spans[len(spans)-1].Length++ + deltas = append(deltas, count-prevCount) + prevCount = count + } + + for n, i := range ii { + count := buckets[i] + // Multiple spans with only small gaps in between are probably + // encoded more efficiently as one larger span with a few empty + // buckets. Needs some research to find the sweet spot. For now, + // we assume that gaps of one or two buckets should not create + // a new span. + iDelta := int32(i - nextI) + if n == 0 || iDelta > 2 { + // We have to create a new span, either because we are + // at the very beginning, or because we have found a gap + // of more than two buckets. + spans = append(spans, &dto.BucketSpan{ + Offset: proto.Int32(iDelta), + Length: proto.Uint32(0), + }) + } else { + // We have found a small gap (or no gap at all). + // Insert empty buckets as needed. + for j := int32(0); j < iDelta; j++ { + appendDelta(0) + } + } + appendDelta(count) + nextI = i + 1 + } + return spans, deltas +} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/internal/difflib.go b/vendor/github.com/prometheus/client_golang/prometheus/internal/difflib.go index a595a20362..8b016355ad 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/internal/difflib.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/internal/difflib.go @@ -22,17 +22,18 @@ import ( "bytes" "fmt" "io" + "strconv" "strings" ) -func min(a, b int) int { +func minInt(a, b int) int { if a < b { return a } return b } -func max(a, b int) int { +func maxInt(a, b int) int { if a > b { return a } @@ -427,12 +428,12 @@ func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode { 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} + codes[0] = OpCode{c.Tag, maxInt(i1, i2-n), i2, maxInt(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)} + codes[len(codes)-1] = OpCode{c.Tag, i1, minInt(i2, i1+n), j1, minInt(j2, j1+n)} } nn := n + n groups := [][]OpCode{} @@ -443,12 +444,12 @@ func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode { // 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), + c.Tag, i1, minInt(i2, i1+n), + j1, minInt(j2, j1+n), }) groups = append(groups, group) group = []OpCode{} - i1, j1 = max(i1, i2-n), max(j1, j2-n) + i1, j1 = maxInt(i1, i2-n), maxInt(j1, j2-n) } group = append(group, OpCode{c.Tag, i1, i2, j1, j2}) } @@ -515,7 +516,7 @@ func (m *SequenceMatcher) QuickRatio() float64 { // is faster to compute than either .Ratio() or .QuickRatio(). func (m *SequenceMatcher) RealQuickRatio() float64 { la, lb := len(m.a), len(m.b) - return calculateRatio(min(la, lb), la+lb) + return calculateRatio(minInt(la, lb), la+lb) } // Convert range to the "ed" format @@ -524,7 +525,7 @@ func formatRangeUnified(start, stop int) string { beginning := start + 1 // lines start numbering with one length := stop - start if length == 1 { - return fmt.Sprintf("%d", beginning) + return strconv.Itoa(beginning) } if length == 0 { beginning-- // empty ranges begin at line just before the range diff --git a/vendor/github.com/prometheus/client_golang/prometheus/internal/go_runtime_metrics.go b/vendor/github.com/prometheus/client_golang/prometheus/internal/go_runtime_metrics.go index 97d17d6cb6..f7f97ef926 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/internal/go_runtime_metrics.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/internal/go_runtime_metrics.go @@ -66,7 +66,8 @@ func RuntimeMetricsToProm(d *metrics.Description) (string, string, string, bool) name += "_total" } - valid := model.IsValidMetricName(model.LabelValue(namespace + "_" + subsystem + "_" + name)) + // Our current conversion moves to legacy naming, so use legacy validation. + valid := model.IsValidLegacyMetricName(namespace + "_" + subsystem + "_" + name) switch d.Kind { case metrics.KindUint64: case metrics.KindFloat64: diff --git a/vendor/github.com/prometheus/client_golang/prometheus/metric.go b/vendor/github.com/prometheus/client_golang/prometheus/metric.go index 9d9b81ab44..592eec3e24 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/metric.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/metric.go @@ -108,15 +108,23 @@ func BuildFQName(namespace, subsystem, name string) string { if name == "" { return "" } - switch { - case namespace != "" && subsystem != "": - return strings.Join([]string{namespace, subsystem, name}, "_") - case namespace != "": - return strings.Join([]string{namespace, name}, "_") - case subsystem != "": - return strings.Join([]string{subsystem, name}, "_") + + sb := strings.Builder{} + sb.Grow(len(namespace) + len(subsystem) + len(name) + 2) + + if namespace != "" { + sb.WriteString(namespace) + sb.WriteString("_") } - return name + + if subsystem != "" { + sb.WriteString(subsystem) + sb.WriteString("_") + } + + sb.WriteString(name) + + return sb.String() } type invalidMetric struct { diff --git a/vendor/github.com/prometheus/client_golang/prometheus/process_collector.go b/vendor/github.com/prometheus/client_golang/prometheus/process_collector.go index 62a4e7ad9a..e7bce8b58e 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/process_collector.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/process_collector.go @@ -23,6 +23,7 @@ import ( type processCollector struct { collectFn func(chan<- Metric) + describeFn func(chan<- *Desc) pidFn func() (int, error) reportErrors bool cpuTotal *Desc @@ -122,26 +123,23 @@ func NewProcessCollector(opts ProcessCollectorOpts) Collector { // Set up process metric collection if supported by the runtime. if canCollectProcess() { c.collectFn = c.processCollect + c.describeFn = c.describe } else { - c.collectFn = func(ch chan<- Metric) { - c.reportError(ch, nil, errors.New("process metrics not supported on this platform")) - } + c.collectFn = c.errorCollectFn + c.describeFn = c.errorDescribeFn } return c } -// Describe returns all descriptions of the collector. -func (c *processCollector) Describe(ch chan<- *Desc) { - ch <- c.cpuTotal - ch <- c.openFDs - ch <- c.maxFDs - ch <- c.vsize - ch <- c.maxVsize - ch <- c.rss - ch <- c.startTime - ch <- c.inBytes - ch <- c.outBytes +func (c *processCollector) errorCollectFn(ch chan<- Metric) { + c.reportError(ch, nil, errors.New("process metrics not supported on this platform")) +} + +func (c *processCollector) errorDescribeFn(ch chan<- *Desc) { + if c.reportErrors { + ch <- NewInvalidDesc(errors.New("process metrics not supported on this platform")) + } } // Collect returns the current state of all metrics of the collector. @@ -149,6 +147,11 @@ func (c *processCollector) Collect(ch chan<- Metric) { c.collectFn(ch) } +// Describe returns all descriptions of the collector. +func (c *processCollector) Describe(ch chan<- *Desc) { + c.describeFn(ch) +} + func (c *processCollector) reportError(ch chan<- Metric, desc *Desc, err error) { if !c.reportErrors { return diff --git a/vendor/github.com/prometheus/client_golang/prometheus/process_collector_darwin.go b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_darwin.go new file mode 100644 index 0000000000..0a61b98461 --- /dev/null +++ b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_darwin.go @@ -0,0 +1,130 @@ +// Copyright 2024 The Prometheus Authors +// 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. + +//go:build darwin && !ios + +package prometheus + +import ( + "errors" + "fmt" + "os" + "syscall" + "time" + + "golang.org/x/sys/unix" +) + +// notImplementedErr is returned by stub functions that replace cgo functions, when cgo +// isn't available. +var notImplementedErr = errors.New("not implemented") + +type memoryInfo struct { + vsize uint64 // Virtual memory size in bytes + rss uint64 // Resident memory size in bytes +} + +func canCollectProcess() bool { + return true +} + +func getSoftLimit(which int) (uint64, error) { + rlimit := syscall.Rlimit{} + + if err := syscall.Getrlimit(which, &rlimit); err != nil { + return 0, err + } + + return rlimit.Cur, nil +} + +func getOpenFileCount() (float64, error) { + // Alternately, the undocumented proc_pidinfo(PROC_PIDLISTFDS) can be used to + // return a list of open fds, but that requires a way to call C APIs. The + // benefits, however, include fewer system calls and not failing when at the + // open file soft limit. + + if dir, err := os.Open("/dev/fd"); err != nil { + return 0.0, err + } else { + defer dir.Close() + + // Avoid ReadDir(), as it calls stat(2) on each descriptor. Not only is + // that info not used, but KQUEUE descriptors fail stat(2), which causes + // the whole method to fail. + if names, err := dir.Readdirnames(0); err != nil { + return 0.0, err + } else { + // Subtract 1 to ignore the open /dev/fd descriptor above. + return float64(len(names) - 1), nil + } + } +} + +func (c *processCollector) processCollect(ch chan<- Metric) { + if procs, err := unix.SysctlKinfoProcSlice("kern.proc.pid", os.Getpid()); err == nil { + if len(procs) == 1 { + startTime := float64(procs[0].Proc.P_starttime.Nano() / 1e9) + ch <- MustNewConstMetric(c.startTime, GaugeValue, startTime) + } else { + err = fmt.Errorf("sysctl() returned %d proc structs (expected 1)", len(procs)) + c.reportError(ch, c.startTime, err) + } + } else { + c.reportError(ch, c.startTime, err) + } + + // The proc structure returned by kern.proc.pid above has an Rusage member, + // but it is not filled in, so it needs to be fetched by getrusage(2). For + // that call, the UTime, STime, and Maxrss members are filled out, but not + // Ixrss, Idrss, or Isrss for the memory usage. Memory stats will require + // access to the C API to call task_info(TASK_BASIC_INFO). + rusage := unix.Rusage{} + + if err := unix.Getrusage(syscall.RUSAGE_SELF, &rusage); err == nil { + cpuTime := time.Duration(rusage.Stime.Nano() + rusage.Utime.Nano()).Seconds() + ch <- MustNewConstMetric(c.cpuTotal, CounterValue, cpuTime) + } else { + c.reportError(ch, c.cpuTotal, err) + } + + if memInfo, err := getMemory(); err == nil { + ch <- MustNewConstMetric(c.rss, GaugeValue, float64(memInfo.rss)) + ch <- MustNewConstMetric(c.vsize, GaugeValue, float64(memInfo.vsize)) + } else if !errors.Is(err, notImplementedErr) { + // Don't report an error when support is not compiled in. + c.reportError(ch, c.rss, err) + c.reportError(ch, c.vsize, err) + } + + if fds, err := getOpenFileCount(); err == nil { + ch <- MustNewConstMetric(c.openFDs, GaugeValue, fds) + } else { + c.reportError(ch, c.openFDs, err) + } + + if openFiles, err := getSoftLimit(syscall.RLIMIT_NOFILE); err == nil { + ch <- MustNewConstMetric(c.maxFDs, GaugeValue, float64(openFiles)) + } else { + c.reportError(ch, c.maxFDs, err) + } + + if addressSpace, err := getSoftLimit(syscall.RLIMIT_AS); err == nil { + ch <- MustNewConstMetric(c.maxVsize, GaugeValue, float64(addressSpace)) + } else { + c.reportError(ch, c.maxVsize, err) + } + + // TODO: socket(PF_SYSTEM) to fetch "com.apple.network.statistics" might + // be able to get the per-process network send/receive counts. +} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/process_collector_mem_cgo_darwin.c b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_mem_cgo_darwin.c new file mode 100644 index 0000000000..d00a24315d --- /dev/null +++ b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_mem_cgo_darwin.c @@ -0,0 +1,84 @@ +// Copyright 2024 The Prometheus Authors +// 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. + +//go:build darwin && !ios && cgo + +#include +#include +#include + +// The compiler warns that mach/shared_memory_server.h is deprecated, and to use +// mach/shared_region.h instead. But that doesn't define +// SHARED_DATA_REGION_SIZE or SHARED_TEXT_REGION_SIZE, so redefine them here and +// avoid a warning message when running tests. +#define GLOBAL_SHARED_TEXT_SEGMENT 0x90000000U +#define SHARED_DATA_REGION_SIZE 0x10000000 +#define SHARED_TEXT_REGION_SIZE 0x10000000 + + +int get_memory_info(unsigned long long *rss, unsigned long long *vsize) +{ + // This is lightly adapted from how ps(1) obtains its memory info. + // https://github.com/apple-oss-distributions/adv_cmds/blob/8744084ea0ff41ca4bb96b0f9c22407d0e48e9b7/ps/tasks.c#L109 + + kern_return_t error; + task_t task = MACH_PORT_NULL; + mach_task_basic_info_data_t info; + mach_msg_type_number_t info_count = MACH_TASK_BASIC_INFO_COUNT; + + error = task_info( + mach_task_self(), + MACH_TASK_BASIC_INFO, + (task_info_t) &info, + &info_count ); + + if( error != KERN_SUCCESS ) + { + return error; + } + + *rss = info.resident_size; + *vsize = info.virtual_size; + + { + vm_region_basic_info_data_64_t b_info; + mach_vm_address_t address = GLOBAL_SHARED_TEXT_SEGMENT; + mach_vm_size_t size; + mach_port_t object_name; + + /* + * try to determine if this task has the split libraries + * mapped in... if so, adjust its virtual size down by + * the 2 segments that are used for split libraries + */ + info_count = VM_REGION_BASIC_INFO_COUNT_64; + + error = mach_vm_region( + mach_task_self(), + &address, + &size, + VM_REGION_BASIC_INFO_64, + (vm_region_info_t) &b_info, + &info_count, + &object_name); + + if (error == KERN_SUCCESS) { + if (b_info.reserved && size == (SHARED_TEXT_REGION_SIZE) && + *vsize > (SHARED_TEXT_REGION_SIZE + SHARED_DATA_REGION_SIZE)) { + *vsize -= (SHARED_TEXT_REGION_SIZE + SHARED_DATA_REGION_SIZE); + } + } + } + + return 0; +} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/process_collector_mem_cgo_darwin.go b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_mem_cgo_darwin.go new file mode 100644 index 0000000000..9ac53f9992 --- /dev/null +++ b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_mem_cgo_darwin.go @@ -0,0 +1,51 @@ +// Copyright 2024 The Prometheus Authors +// 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. + +//go:build darwin && !ios && cgo + +package prometheus + +/* +int get_memory_info(unsigned long long *rss, unsigned long long *vs); +*/ +import "C" +import "fmt" + +func getMemory() (*memoryInfo, error) { + var rss, vsize C.ulonglong + + if err := C.get_memory_info(&rss, &vsize); err != 0 { + return nil, fmt.Errorf("task_info() failed with 0x%x", int(err)) + } + + return &memoryInfo{vsize: uint64(vsize), rss: uint64(rss)}, nil +} + +// describe returns all descriptions of the collector for Darwin. +// Ensure that this list of descriptors is kept in sync with the metrics collected +// in the processCollect method. Any changes to the metrics in processCollect +// (such as adding or removing metrics) should be reflected in this list of descriptors. +func (c *processCollector) describe(ch chan<- *Desc) { + ch <- c.cpuTotal + ch <- c.openFDs + ch <- c.maxFDs + ch <- c.maxVsize + ch <- c.startTime + ch <- c.rss + ch <- c.vsize + + /* the process could be collected but not implemented yet + ch <- c.inBytes + ch <- c.outBytes + */ +} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/process_collector_mem_nocgo_darwin.go b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_mem_nocgo_darwin.go new file mode 100644 index 0000000000..8ddb0995d6 --- /dev/null +++ b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_mem_nocgo_darwin.go @@ -0,0 +1,39 @@ +// Copyright 2024 The Prometheus Authors +// 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. + +//go:build darwin && !ios && !cgo + +package prometheus + +func getMemory() (*memoryInfo, error) { + return nil, notImplementedErr +} + +// describe returns all descriptions of the collector for Darwin. +// Ensure that this list of descriptors is kept in sync with the metrics collected +// in the processCollect method. Any changes to the metrics in processCollect +// (such as adding or removing metrics) should be reflected in this list of descriptors. +func (c *processCollector) describe(ch chan<- *Desc) { + ch <- c.cpuTotal + ch <- c.openFDs + ch <- c.maxFDs + ch <- c.maxVsize + ch <- c.startTime + + /* the process could be collected but not implemented yet + ch <- c.rss + ch <- c.vsize + ch <- c.inBytes + ch <- c.outBytes + */ +} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/process_collector_js.go b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_not_supported.go similarity index 56% rename from vendor/github.com/prometheus/client_golang/prometheus/process_collector_js.go rename to vendor/github.com/prometheus/client_golang/prometheus/process_collector_not_supported.go index b1e363d6cf..7732b7f376 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/process_collector_js.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_not_supported.go @@ -1,4 +1,4 @@ -// Copyright 2019 The Prometheus Authors +// Copyright 2023 The Prometheus Authors // 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 @@ -11,8 +11,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build js -// +build js +//go:build wasip1 || js || ios +// +build wasip1 js ios package prometheus @@ -21,6 +21,13 @@ func canCollectProcess() bool { } func (c *processCollector) processCollect(ch chan<- Metric) { - // noop on this platform - return + c.errorCollectFn(ch) +} + +// describe returns all descriptions of the collector for wasip1 and js. +// Ensure that this list of descriptors is kept in sync with the metrics collected +// in the processCollect method. Any changes to the metrics in processCollect +// (such as adding or removing metrics) should be reflected in this list of descriptors. +func (c *processCollector) describe(ch chan<- *Desc) { + c.errorDescribeFn(ch) } diff --git a/vendor/github.com/prometheus/client_golang/prometheus/process_collector_other.go b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_procfsenabled.go similarity index 77% rename from vendor/github.com/prometheus/client_golang/prometheus/process_collector_other.go rename to vendor/github.com/prometheus/client_golang/prometheus/process_collector_procfsenabled.go index 14d56d2d06..9f4b130bef 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/process_collector_other.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_procfsenabled.go @@ -11,8 +11,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -//go:build !windows && !js && !wasip1 -// +build !windows,!js,!wasip1 +//go:build !windows && !js && !wasip1 && !darwin +// +build !windows,!js,!wasip1,!darwin package prometheus @@ -78,3 +78,19 @@ func (c *processCollector) processCollect(ch chan<- Metric) { c.reportError(ch, nil, err) } } + +// describe returns all descriptions of the collector for others than windows, js, wasip1 and darwin. +// Ensure that this list of descriptors is kept in sync with the metrics collected +// in the processCollect method. Any changes to the metrics in processCollect +// (such as adding or removing metrics) should be reflected in this list of descriptors. +func (c *processCollector) describe(ch chan<- *Desc) { + ch <- c.cpuTotal + ch <- c.openFDs + ch <- c.maxFDs + ch <- c.vsize + ch <- c.maxVsize + ch <- c.rss + ch <- c.startTime + ch <- c.inBytes + ch <- c.outBytes +} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/process_collector_wasip1.go b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_wasip1.go deleted file mode 100644 index d8d9a6d7a2..0000000000 --- a/vendor/github.com/prometheus/client_golang/prometheus/process_collector_wasip1.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2023 The Prometheus Authors -// 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. - -//go:build wasip1 -// +build wasip1 - -package prometheus - -func canCollectProcess() bool { - return false -} - -func (*processCollector) processCollect(chan<- Metric) { - // noop on this platform - return -} diff --git a/vendor/github.com/prometheus/client_golang/prometheus/process_collector_windows.go b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_windows.go index f973398df2..fa474289ef 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/process_collector_windows.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/process_collector_windows.go @@ -79,14 +79,10 @@ func getProcessHandleCount(handle windows.Handle) (uint32, error) { } func (c *processCollector) processCollect(ch chan<- Metric) { - h, err := windows.GetCurrentProcess() - if err != nil { - c.reportError(ch, nil, err) - return - } + h := windows.CurrentProcess() var startTime, exitTime, kernelTime, userTime windows.Filetime - err = windows.GetProcessTimes(h, &startTime, &exitTime, &kernelTime, &userTime) + err := windows.GetProcessTimes(h, &startTime, &exitTime, &kernelTime, &userTime) if err != nil { c.reportError(ch, nil, err) return @@ -111,6 +107,19 @@ func (c *processCollector) processCollect(ch chan<- Metric) { ch <- MustNewConstMetric(c.maxFDs, GaugeValue, float64(16*1024*1024)) // Windows has a hard-coded max limit, not per-process. } +// describe returns all descriptions of the collector for windows. +// Ensure that this list of descriptors is kept in sync with the metrics collected +// in the processCollect method. Any changes to the metrics in processCollect +// (such as adding or removing metrics) should be reflected in this list of descriptors. +func (c *processCollector) describe(ch chan<- *Desc) { + ch <- c.cpuTotal + ch <- c.openFDs + ch <- c.maxFDs + ch <- c.vsize + ch <- c.rss + ch <- c.startTime +} + func fileTimeToSeconds(ft windows.Filetime) float64 { return float64(uint64(ft.HighDateTime)<<32+uint64(ft.LowDateTime)) / 1e7 } diff --git a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go index e598e66e68..28eed26727 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/promhttp/http.go @@ -207,7 +207,13 @@ func HandlerForTransactional(reg prometheus.TransactionalGatherer, opts HandlerO if encodingHeader != string(Identity) { rsp.Header().Set(contentEncodingHeader, encodingHeader) } - enc := expfmt.NewEncoder(w, contentType) + + var enc expfmt.Encoder + if opts.EnableOpenMetricsTextCreatedSamples { + enc = expfmt.NewEncoder(w, contentType, expfmt.WithCreatedLines()) + } else { + enc = expfmt.NewEncoder(w, contentType) + } // handleError handles the error according to opts.ErrorHandling // and returns true if we have to abort after the handling. @@ -408,6 +414,21 @@ type HandlerOpts struct { // (which changes the identity of the resulting series on the Prometheus // server). EnableOpenMetrics bool + // EnableOpenMetricsTextCreatedSamples specifies if this handler should add, extra, synthetic + // Created Timestamps for counters, histograms and summaries, which for the current + // version of OpenMetrics are defined as extra series with the same name and "_created" + // suffix. See also the OpenMetrics specification for more details + // https://github.com/prometheus/OpenMetrics/blob/v1.0.0/specification/OpenMetrics.md#counter-1 + // + // Created timestamps are used to improve the accuracy of reset detection, + // but the way it's designed in OpenMetrics 1.0 it also dramatically increases cardinality + // if the scraper does not handle those metrics correctly (converting to created timestamp + // instead of leaving those series as-is). New OpenMetrics versions might improve + // this situation. + // + // Prometheus introduced the feature flag 'created-timestamp-zero-ingestion' + // in version 2.50.0 to handle this situation. + EnableOpenMetricsTextCreatedSamples bool // ProcessStartTime allows setting process start timevalue that will be exposed // with "Process-Start-Time-Unix" response header along with the metrics // payload. This allow callers to have efficient transformations to cumulative diff --git a/vendor/github.com/prometheus/client_golang/prometheus/summary.go b/vendor/github.com/prometheus/client_golang/prometheus/summary.go index 1ab0e47965..ac5203c6fa 100644 --- a/vendor/github.com/prometheus/client_golang/prometheus/summary.go +++ b/vendor/github.com/prometheus/client_golang/prometheus/summary.go @@ -243,6 +243,7 @@ func newSummary(desc *Desc, opts SummaryOpts, labelValues ...string) Summary { s := &summary{ desc: desc, + now: opts.now, objectives: opts.Objectives, sortedObjectives: make([]float64, 0, len(opts.Objectives)), @@ -280,6 +281,8 @@ type summary struct { desc *Desc + now func() time.Time + objectives map[float64]float64 sortedObjectives []float64 @@ -307,7 +310,7 @@ func (s *summary) Observe(v float64) { s.bufMtx.Lock() defer s.bufMtx.Unlock() - now := time.Now() + now := s.now() if now.After(s.hotBufExpTime) { s.asyncFlush(now) } @@ -326,7 +329,7 @@ func (s *summary) Write(out *dto.Metric) error { s.bufMtx.Lock() s.mtx.Lock() // Swap bufs even if hotBuf is empty to set new hotBufExpTime. - s.swapBufs(time.Now()) + s.swapBufs(s.now()) s.bufMtx.Unlock() s.flushColdBuf() diff --git a/vendor/github.com/prometheus/common/model/alert.go b/vendor/github.com/prometheus/common/model/alert.go index bd3a39e3e1..460f554f29 100644 --- a/vendor/github.com/prometheus/common/model/alert.go +++ b/vendor/github.com/prometheus/common/model/alert.go @@ -65,7 +65,7 @@ func (a *Alert) Resolved() bool { return a.ResolvedAt(time.Now()) } -// ResolvedAt returns true off the activity interval ended before +// ResolvedAt returns true iff the activity interval ended before // the given timestamp. func (a *Alert) ResolvedAt(ts time.Time) bool { if a.EndsAt.IsZero() { diff --git a/vendor/github.com/prometheus/common/model/labels.go b/vendor/github.com/prometheus/common/model/labels.go index 73b7aa3e60..f4a387605f 100644 --- a/vendor/github.com/prometheus/common/model/labels.go +++ b/vendor/github.com/prometheus/common/model/labels.go @@ -22,7 +22,7 @@ import ( ) const ( - // AlertNameLabel is the name of the label containing the an alert's name. + // AlertNameLabel is the name of the label containing the alert's name. AlertNameLabel = "alertname" // ExportedLabelPrefix is the prefix to prepend to the label names present in diff --git a/vendor/github.com/prometheus/common/model/metric.go b/vendor/github.com/prometheus/common/model/metric.go index 5766107cf9..a6b01755bd 100644 --- a/vendor/github.com/prometheus/common/model/metric.go +++ b/vendor/github.com/prometheus/common/model/metric.go @@ -27,13 +27,25 @@ import ( ) var ( - // NameValidationScheme determines the method of name validation to be used by - // all calls to IsValidMetricName() and LabelName IsValid(). Setting UTF-8 - // mode in isolation from other components that don't support UTF-8 may result - // in bugs or other undefined behavior. This value can be set to - // LegacyValidation during startup if a binary is not UTF-8-aware binaries. To - // avoid need for locking, this value should be set once, ideally in an - // init(), before multiple goroutines are started. + // NameValidationScheme determines the global default method of the name + // validation to be used by all calls to IsValidMetricName() and LabelName + // IsValid(). + // + // Deprecated: This variable should not be used and might be removed in the + // far future. If you wish to stick to the legacy name validation use + // `IsValidLegacyMetricName()` and `LabelName.IsValidLegacy()` methods + // instead. This variable is here as an escape hatch for emergency cases, + // given the recent change from `LegacyValidation` to `UTF8Validation`, e.g., + // to delay UTF-8 migrations in time or aid in debugging unforeseen results of + // the change. In such a case, a temporary assignment to `LegacyValidation` + // value in the `init()` function in your main.go or so, could be considered. + // + // Historically we opted for a global variable for feature gating different + // validation schemes in operations that were not otherwise easily adjustable + // (e.g. Labels yaml unmarshaling). That could have been a mistake, a separate + // Labels structure or package might have been a better choice. Given the + // change was made and many upgraded the common already, we live this as-is + // with this warning and learning for the future. NameValidationScheme = UTF8Validation // NameEscapingScheme defines the default way that names will be escaped when @@ -50,7 +62,7 @@ var ( type ValidationScheme int const ( - // LegacyValidation is a setting that requirets that metric and label names + // LegacyValidation is a setting that requires that all metric and label names // conform to the original Prometheus character requirements described by // MetricNameRE and LabelNameRE. LegacyValidation ValidationScheme = iota diff --git a/vendor/github.com/prometheus/procfs/.golangci.yml b/vendor/github.com/prometheus/procfs/.golangci.yml index 126df9e67a..b43e09f683 100644 --- a/vendor/github.com/prometheus/procfs/.golangci.yml +++ b/vendor/github.com/prometheus/procfs/.golangci.yml @@ -2,7 +2,10 @@ linters: enable: - errcheck + - forbidigo - godot + - gofmt + - goimports - gosimple - govet - ineffassign @@ -12,11 +15,17 @@ linters: - testifylint - unused -linter-settings: +linters-settings: + forbidigo: + forbid: + - p: ^fmt\.Print.*$ + msg: Do not commit print statements. godot: capital: true exclude: # Ignore "See: URL" - 'See:' + goimports: + local-prefixes: github.com/prometheus/procfs misspell: locale: US diff --git a/vendor/github.com/prometheus/procfs/Makefile.common b/vendor/github.com/prometheus/procfs/Makefile.common index 1617292350..cbb5d86382 100644 --- a/vendor/github.com/prometheus/procfs/Makefile.common +++ b/vendor/github.com/prometheus/procfs/Makefile.common @@ -61,7 +61,7 @@ PROMU_URL := https://github.com/prometheus/promu/releases/download/v$(PROMU_ SKIP_GOLANGCI_LINT := GOLANGCI_LINT := GOLANGCI_LINT_OPTS ?= -GOLANGCI_LINT_VERSION ?= v1.59.0 +GOLANGCI_LINT_VERSION ?= v1.60.2 # golangci-lint only supports linux, darwin and windows platforms on i386/amd64/arm64. # windows isn't included here because of the path separator being different. ifeq ($(GOHOSTOS),$(filter $(GOHOSTOS),linux darwin)) @@ -275,3 +275,9 @@ $(1)_precheck: exit 1; \ fi endef + +govulncheck: install-govulncheck + govulncheck ./... + +install-govulncheck: + command -v govulncheck > /dev/null || go install golang.org/x/vuln/cmd/govulncheck@latest diff --git a/vendor/github.com/prometheus/procfs/README.md b/vendor/github.com/prometheus/procfs/README.md index 1224816c2a..0718239cf1 100644 --- a/vendor/github.com/prometheus/procfs/README.md +++ b/vendor/github.com/prometheus/procfs/README.md @@ -47,15 +47,15 @@ However, most of the API includes unit tests which can be run with `make test`. The procfs library includes a set of test fixtures which include many example files from the `/proc` and `/sys` filesystems. These fixtures are included as a [ttar](https://github.com/ideaship/ttar) file which is extracted automatically during testing. To add/update the test fixtures, first -ensure the `fixtures` directory is up to date by removing the existing directory and then -extracting the ttar file using `make fixtures/.unpacked` or just `make test`. +ensure the `testdata/fixtures` directory is up to date by removing the existing directory and then +extracting the ttar file using `make testdata/fixtures/.unpacked` or just `make test`. ```bash rm -rf testdata/fixtures make test ``` -Next, make the required changes to the extracted files in the `fixtures` directory. When +Next, make the required changes to the extracted files in the `testdata/fixtures` directory. When the changes are complete, run `make update_fixtures` to create a new `fixtures.ttar` file based on the updated `fixtures` directory. And finally, verify the changes using `git diff testdata/fixtures.ttar`. diff --git a/vendor/github.com/prometheus/procfs/arp.go b/vendor/github.com/prometheus/procfs/arp.go index cdcc8a7ccc..2e53344151 100644 --- a/vendor/github.com/prometheus/procfs/arp.go +++ b/vendor/github.com/prometheus/procfs/arp.go @@ -23,9 +23,9 @@ import ( // Learned from include/uapi/linux/if_arp.h. const ( - // completed entry (ha valid). + // Completed entry (ha valid). ATFComplete = 0x02 - // permanent entry. + // Permanent entry. ATFPermanent = 0x04 // Publish entry. ATFPublish = 0x08 diff --git a/vendor/github.com/prometheus/procfs/fs.go b/vendor/github.com/prometheus/procfs/fs.go index 4980c875bf..9bdaccc7c8 100644 --- a/vendor/github.com/prometheus/procfs/fs.go +++ b/vendor/github.com/prometheus/procfs/fs.go @@ -24,8 +24,14 @@ type FS struct { isReal bool } -// DefaultMountPoint is the common mount point of the proc filesystem. -const DefaultMountPoint = fs.DefaultProcMountPoint +const ( + // DefaultMountPoint is the common mount point of the proc filesystem. + DefaultMountPoint = fs.DefaultProcMountPoint + + // SectorSize represents the size of a sector in bytes. + // It is specific to Linux block I/O operations. + SectorSize = 512 +) // NewDefaultFS returns a new proc FS mounted under the default proc mountPoint. // It will error if the mount point directory can't be read or is a file. diff --git a/vendor/github.com/prometheus/procfs/fs_statfs_notype.go b/vendor/github.com/prometheus/procfs/fs_statfs_notype.go index 134767d69a..1b5bdbdf84 100644 --- a/vendor/github.com/prometheus/procfs/fs_statfs_notype.go +++ b/vendor/github.com/prometheus/procfs/fs_statfs_notype.go @@ -17,7 +17,7 @@ package procfs // isRealProc returns true on architectures that don't have a Type argument -// in their Statfs_t struct -func isRealProc(mountPoint string) (bool, error) { +// in their Statfs_t struct. +func isRealProc(_ string) (bool, error) { return true, nil } diff --git a/vendor/github.com/prometheus/procfs/fscache.go b/vendor/github.com/prometheus/procfs/fscache.go index cf2e3eaa03..7db8633077 100644 --- a/vendor/github.com/prometheus/procfs/fscache.go +++ b/vendor/github.com/prometheus/procfs/fscache.go @@ -162,7 +162,7 @@ type Fscacheinfo struct { ReleaseRequestsAgainstPagesStoredByTimeLockGranted uint64 // Number of release reqs ignored due to in-progress store ReleaseRequestsIgnoredDueToInProgressStore uint64 - // Number of page stores cancelled due to release req + // Number of page stores canceled due to release req PageStoresCancelledByReleaseRequests uint64 VmscanWaiting uint64 // Number of times async ops added to pending queues @@ -171,11 +171,11 @@ type Fscacheinfo struct { OpsRunning uint64 // Number of times async ops queued for processing OpsEnqueued uint64 - // Number of async ops cancelled + // Number of async ops canceled OpsCancelled uint64 // Number of async ops rejected due to object lookup/create failure OpsRejected uint64 - // Number of async ops initialised + // Number of async ops initialized OpsInitialised uint64 // Number of async ops queued for deferred release OpsDeferred uint64 diff --git a/vendor/github.com/prometheus/procfs/internal/fs/fs.go b/vendor/github.com/prometheus/procfs/internal/fs/fs.go index 3c18c7610e..3a43e83915 100644 --- a/vendor/github.com/prometheus/procfs/internal/fs/fs.go +++ b/vendor/github.com/prometheus/procfs/internal/fs/fs.go @@ -28,6 +28,9 @@ const ( // DefaultConfigfsMountPoint is the common mount point of the configfs. DefaultConfigfsMountPoint = "/sys/kernel/config" + + // DefaultSelinuxMountPoint is the common mount point of the selinuxfs. + DefaultSelinuxMountPoint = "/sys/fs/selinux" ) // FS represents a pseudo-filesystem, normally /proc or /sys, which provides an diff --git a/vendor/github.com/prometheus/procfs/internal/util/parse.go b/vendor/github.com/prometheus/procfs/internal/util/parse.go index 14272dc788..5a7d2df06a 100644 --- a/vendor/github.com/prometheus/procfs/internal/util/parse.go +++ b/vendor/github.com/prometheus/procfs/internal/util/parse.go @@ -14,6 +14,7 @@ package util import ( + "errors" "os" "strconv" "strings" @@ -110,3 +111,16 @@ func ParseBool(b string) *bool { } return &truth } + +// ReadHexFromFile reads a file and attempts to parse a uint64 from a hexadecimal format 0xXX. +func ReadHexFromFile(path string) (uint64, error) { + data, err := os.ReadFile(path) + if err != nil { + return 0, err + } + hexString := strings.TrimSpace(string(data)) + if !strings.HasPrefix(hexString, "0x") { + return 0, errors.New("invalid format: hex string does not start with '0x'") + } + return strconv.ParseUint(hexString[2:], 16, 64) +} diff --git a/vendor/github.com/prometheus/procfs/mountstats.go b/vendor/github.com/prometheus/procfs/mountstats.go index 75a3b6c810..b6c8d1a570 100644 --- a/vendor/github.com/prometheus/procfs/mountstats.go +++ b/vendor/github.com/prometheus/procfs/mountstats.go @@ -45,11 +45,11 @@ const ( fieldTransport11TCPLen = 13 fieldTransport11UDPLen = 10 - // kernel version >= 4.14 MaxLen + // Kernel version >= 4.14 MaxLen // See: https://elixir.bootlin.com/linux/v6.4.8/source/net/sunrpc/xprtrdma/xprt_rdma.h#L393 fieldTransport11RDMAMaxLen = 28 - // kernel version <= 4.2 MinLen + // Kernel version <= 4.2 MinLen // See: https://elixir.bootlin.com/linux/v4.2.8/source/net/sunrpc/xprtrdma/xprt_rdma.h#L331 fieldTransport11RDMAMinLen = 20 ) diff --git a/vendor/github.com/prometheus/procfs/net_dev_snmp6.go b/vendor/github.com/prometheus/procfs/net_dev_snmp6.go new file mode 100644 index 0000000000..f50b38e352 --- /dev/null +++ b/vendor/github.com/prometheus/procfs/net_dev_snmp6.go @@ -0,0 +1,96 @@ +// Copyright 2018 The Prometheus Authors +// 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 procfs + +import ( + "bufio" + "errors" + "io" + "os" + "strconv" + "strings" +) + +// NetDevSNMP6 is parsed from files in /proc/net/dev_snmp6/ or /proc//net/dev_snmp6/. +// The outer map's keys are interface names and the inner map's keys are stat names. +// +// If you'd like a total across all interfaces, please use the Snmp6() method of the Proc type. +type NetDevSNMP6 map[string]map[string]uint64 + +// Returns kernel/system statistics read from interface files within the /proc/net/dev_snmp6/ +// directory. +func (fs FS) NetDevSNMP6() (NetDevSNMP6, error) { + return newNetDevSNMP6(fs.proc.Path("net/dev_snmp6")) +} + +// Returns kernel/system statistics read from interface files within the /proc//net/dev_snmp6/ +// directory. +func (p Proc) NetDevSNMP6() (NetDevSNMP6, error) { + return newNetDevSNMP6(p.path("net/dev_snmp6")) +} + +// newNetDevSNMP6 creates a new NetDevSNMP6 from the contents of the given directory. +func newNetDevSNMP6(dir string) (NetDevSNMP6, error) { + netDevSNMP6 := make(NetDevSNMP6) + + // The net/dev_snmp6 folders contain one file per interface + ifaceFiles, err := os.ReadDir(dir) + if err != nil { + // On systems with IPv6 disabled, this directory won't exist. + // Do nothing. + if errors.Is(err, os.ErrNotExist) { + return netDevSNMP6, err + } + return netDevSNMP6, err + } + + for _, iFaceFile := range ifaceFiles { + f, err := os.Open(dir + "/" + iFaceFile.Name()) + if err != nil { + return netDevSNMP6, err + } + defer f.Close() + + netDevSNMP6[iFaceFile.Name()], err = parseNetDevSNMP6Stats(f) + if err != nil { + return netDevSNMP6, err + } + } + + return netDevSNMP6, nil +} + +func parseNetDevSNMP6Stats(r io.Reader) (map[string]uint64, error) { + m := make(map[string]uint64) + + scanner := bufio.NewScanner(r) + for scanner.Scan() { + stat := strings.Fields(scanner.Text()) + if len(stat) < 2 { + continue + } + key, val := stat[0], stat[1] + + // Expect stat name to contain "6" or be "ifIndex" + if strings.Contains(key, "6") || key == "ifIndex" { + v, err := strconv.ParseUint(val, 10, 64) + if err != nil { + return m, err + } + + m[key] = v + } + } + return m, scanner.Err() +} diff --git a/vendor/github.com/prometheus/procfs/net_ip_socket.go b/vendor/github.com/prometheus/procfs/net_ip_socket.go index b70f1fc7a4..19e3378f72 100644 --- a/vendor/github.com/prometheus/procfs/net_ip_socket.go +++ b/vendor/github.com/prometheus/procfs/net_ip_socket.go @@ -25,7 +25,7 @@ import ( ) const ( - // readLimit is used by io.LimitReader while reading the content of the + // Maximum size limit used by io.LimitReader while reading the content of the // /proc/net/udp{,6} files. The number of lines inside such a file is dynamic // as each line represents a single used socket. // In theory, the number of available sockets is 65535 (2^16 - 1) per IP. @@ -50,12 +50,12 @@ type ( // UsedSockets shows the total number of parsed lines representing the // number of used sockets. UsedSockets uint64 - // Drops shows the total number of dropped packets of all UPD sockets. + // Drops shows the total number of dropped packets of all UDP sockets. Drops *uint64 } - // netIPSocketLine represents the fields parsed from a single line - // in /proc/net/{t,u}dp{,6}. Fields which are not used by IPSocket are skipped. + // A single line parser for fields from /proc/net/{t,u}dp{,6}. + // Fields which are not used by IPSocket are skipped. // Drops is non-nil for udp{,6}, but nil for tcp{,6}. // For the proc file format details, see https://linux.die.net/man/5/proc. netIPSocketLine struct { diff --git a/vendor/github.com/prometheus/procfs/net_tcp.go b/vendor/github.com/prometheus/procfs/net_tcp.go index 5277629557..0396d72015 100644 --- a/vendor/github.com/prometheus/procfs/net_tcp.go +++ b/vendor/github.com/prometheus/procfs/net_tcp.go @@ -25,24 +25,28 @@ type ( // NetTCP returns the IPv4 kernel/networking statistics for TCP datagrams // read from /proc/net/tcp. +// Deprecated: Use github.com/mdlayher/netlink#Conn (with syscall.AF_INET) instead. func (fs FS) NetTCP() (NetTCP, error) { return newNetTCP(fs.proc.Path("net/tcp")) } // NetTCP6 returns the IPv6 kernel/networking statistics for TCP datagrams // read from /proc/net/tcp6. +// Deprecated: Use github.com/mdlayher/netlink#Conn (with syscall.AF_INET6) instead. func (fs FS) NetTCP6() (NetTCP, error) { return newNetTCP(fs.proc.Path("net/tcp6")) } // NetTCPSummary returns already computed statistics like the total queue lengths // for TCP datagrams read from /proc/net/tcp. +// Deprecated: Use github.com/mdlayher/netlink#Conn (with syscall.AF_INET) instead. func (fs FS) NetTCPSummary() (*NetTCPSummary, error) { return newNetTCPSummary(fs.proc.Path("net/tcp")) } // NetTCP6Summary returns already computed statistics like the total queue lengths // for TCP datagrams read from /proc/net/tcp6. +// Deprecated: Use github.com/mdlayher/netlink#Conn (with syscall.AF_INET6) instead. func (fs FS) NetTCP6Summary() (*NetTCPSummary, error) { return newNetTCPSummary(fs.proc.Path("net/tcp6")) } diff --git a/vendor/github.com/prometheus/procfs/net_unix.go b/vendor/github.com/prometheus/procfs/net_unix.go index d868cebdaa..d7e0cacb4c 100644 --- a/vendor/github.com/prometheus/procfs/net_unix.go +++ b/vendor/github.com/prometheus/procfs/net_unix.go @@ -121,12 +121,12 @@ func parseNetUNIX(r io.Reader) (*NetUNIX, error) { return &nu, nil } -func (u *NetUNIX) parseLine(line string, hasInode bool, min int) (*NetUNIXLine, error) { +func (u *NetUNIX) parseLine(line string, hasInode bool, minFields int) (*NetUNIXLine, error) { fields := strings.Fields(line) l := len(fields) - if l < min { - return nil, fmt.Errorf("%w: expected at least %d fields but got %d", ErrFileParse, min, l) + if l < minFields { + return nil, fmt.Errorf("%w: expected at least %d fields but got %d", ErrFileParse, minFields, l) } // Field offsets are as follows: @@ -172,7 +172,7 @@ func (u *NetUNIX) parseLine(line string, hasInode bool, min int) (*NetUNIXLine, } // Path field is optional. - if l > min { + if l > minFields { // Path occurs at either index 6 or 7 depending on whether inode is // already present. pathIdx := 7 diff --git a/vendor/github.com/prometheus/procfs/proc_cgroup.go b/vendor/github.com/prometheus/procfs/proc_cgroup.go index daeed7f571..4a64347c03 100644 --- a/vendor/github.com/prometheus/procfs/proc_cgroup.go +++ b/vendor/github.com/prometheus/procfs/proc_cgroup.go @@ -24,7 +24,7 @@ import ( ) // Cgroup models one line from /proc/[pid]/cgroup. Each Cgroup struct describes the placement of a PID inside a -// specific control hierarchy. The kernel has two cgroup APIs, v1 and v2. v1 has one hierarchy per available resource +// specific control hierarchy. The kernel has two cgroup APIs, v1 and v2. The v1 has one hierarchy per available resource // controller, while v2 has one unified hierarchy shared by all controllers. Regardless of v1 or v2, all hierarchies // contain all running processes, so the question answerable with a Cgroup struct is 'where is this process in // this hierarchy' (where==what path on the specific cgroupfs). By prefixing this path with the mount point of diff --git a/vendor/github.com/prometheus/procfs/proc_io.go b/vendor/github.com/prometheus/procfs/proc_io.go index 776f349717..d15b66ddb6 100644 --- a/vendor/github.com/prometheus/procfs/proc_io.go +++ b/vendor/github.com/prometheus/procfs/proc_io.go @@ -50,7 +50,7 @@ func (p Proc) IO() (ProcIO, error) { ioFormat := "rchar: %d\nwchar: %d\nsyscr: %d\nsyscw: %d\n" + "read_bytes: %d\nwrite_bytes: %d\n" + - "cancelled_write_bytes: %d\n" + "cancelled_write_bytes: %d\n" //nolint:misspell _, err = fmt.Sscanf(string(data), ioFormat, &pio.RChar, &pio.WChar, &pio.SyscR, &pio.SyscW, &pio.ReadBytes, &pio.WriteBytes, &pio.CancelledWriteBytes) diff --git a/vendor/github.com/prometheus/procfs/proc_smaps.go b/vendor/github.com/prometheus/procfs/proc_smaps.go index 09060e8208..9a297afcf8 100644 --- a/vendor/github.com/prometheus/procfs/proc_smaps.go +++ b/vendor/github.com/prometheus/procfs/proc_smaps.go @@ -19,7 +19,6 @@ package procfs import ( "bufio" "errors" - "fmt" "os" "regexp" "strconv" @@ -29,7 +28,7 @@ import ( ) var ( - // match the header line before each mapped zone in `/proc/pid/smaps`. + // Match the header line before each mapped zone in `/proc/pid/smaps`. procSMapsHeaderLine = regexp.MustCompile(`^[a-f0-9].*$`) ) @@ -117,7 +116,6 @@ func (p Proc) procSMapsRollupManual() (ProcSMapsRollup, error) { func (s *ProcSMapsRollup) parseLine(line string) error { kv := strings.SplitN(line, ":", 2) if len(kv) != 2 { - fmt.Println(line) return errors.New("invalid net/dev line, missing colon") } diff --git a/vendor/github.com/prometheus/procfs/proc_status.go b/vendor/github.com/prometheus/procfs/proc_status.go index a055197c63..dd8aa56885 100644 --- a/vendor/github.com/prometheus/procfs/proc_status.go +++ b/vendor/github.com/prometheus/procfs/proc_status.go @@ -146,7 +146,11 @@ func (s *ProcStatus) fillStatus(k string, vString string, vUint uint64, vUintByt } } case "NSpid": - s.NSpids = calcNSPidsList(vString) + nspids, err := calcNSPidsList(vString) + if err != nil { + return err + } + s.NSpids = nspids case "VmPeak": s.VmPeak = vUintBytes case "VmSize": @@ -222,17 +226,17 @@ func calcCpusAllowedList(cpuString string) []uint64 { return g } -func calcNSPidsList(nspidsString string) []uint64 { - s := strings.Split(nspidsString, " ") +func calcNSPidsList(nspidsString string) ([]uint64, error) { + s := strings.Split(nspidsString, "\t") var nspids []uint64 for _, nspid := range s { - nspid, _ := strconv.ParseUint(nspid, 10, 64) - if nspid == 0 { - continue + nspid, err := strconv.ParseUint(nspid, 10, 64) + if err != nil { + return nil, err } nspids = append(nspids, nspid) } - return nspids + return nspids, nil } diff --git a/vendor/github.com/sagikazarmark/locafero/finder.go b/vendor/github.com/sagikazarmark/locafero/finder.go index 754c8b260e..ef8d547122 100644 --- a/vendor/github.com/sagikazarmark/locafero/finder.go +++ b/vendor/github.com/sagikazarmark/locafero/finder.go @@ -27,7 +27,7 @@ type Finder struct { // It provides the capability to search for entries with depth, // meaning it can target deeper locations within the directory structure. // - // It also supports glob syntax (as defined by [filepat.Match]), offering greater flexibility in search patterns. + // It also supports glob syntax (as defined by [filepath.Match]), offering greater flexibility in search patterns. // // Examples: // - config.yaml @@ -63,7 +63,7 @@ func (f Finder) Find(fsys afero.Fs) ([]string, error) { // pool.Go(func() ([]string, error) { // // If the name contains any glob character, perform a glob match - // if strings.ContainsAny(searchName, "*?[]\\^") { + // if strings.ContainsAny(searchName, globMatch) { // return globWalkSearch(fsys, searchPath, searchName, f.Type) // } // @@ -79,7 +79,7 @@ func (f Finder) Find(fsys afero.Fs) ([]string, error) { allResults, err := iter.MapErr(searchItems, func(item *searchItem) ([]string, error) { // If the name contains any glob character, perform a glob match - if strings.ContainsAny(item.name, "*?[]\\^") { + if strings.ContainsAny(item.name, globMatch) { return globWalkSearch(fsys, item.path, item.name, f.Type) } diff --git a/vendor/github.com/sagikazarmark/locafero/flake.lock b/vendor/github.com/sagikazarmark/locafero/flake.lock index 46d28f805a..df2a8cceca 100644 --- a/vendor/github.com/sagikazarmark/locafero/flake.lock +++ b/vendor/github.com/sagikazarmark/locafero/flake.lock @@ -1,22 +1,84 @@ { "nodes": { + "cachix": { + "inputs": { + "devenv": "devenv_2", + "flake-compat": [ + "devenv", + "flake-compat" + ], + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "pre-commit-hooks": [ + "devenv", + "pre-commit-hooks" + ] + }, + "locked": { + "lastModified": 1712055811, + "narHash": "sha256-7FcfMm5A/f02yyzuavJe06zLa9hcMHsagE28ADcmQvk=", + "owner": "cachix", + "repo": "cachix", + "rev": "02e38da89851ec7fec3356a5c04bc8349cae0e30", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "cachix", + "type": "github" + } + }, "devenv": { "inputs": { - "flake-compat": "flake-compat", + "cachix": "cachix", + "flake-compat": "flake-compat_2", + "nix": "nix_2", + "nixpkgs": "nixpkgs_2", + "pre-commit-hooks": "pre-commit-hooks" + }, + "locked": { + "lastModified": 1725907707, + "narHash": "sha256-s3pbtzZmVPHzc86WQjK7MGZMNvvw6hWnFMljEkllAfM=", + "owner": "cachix", + "repo": "devenv", + "rev": "2bbbbc468fc02257265a79652a8350651cca495a", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "devenv_2": { + "inputs": { + "flake-compat": [ + "devenv", + "cachix", + "flake-compat" + ], "nix": "nix", "nixpkgs": "nixpkgs", - "pre-commit-hooks": "pre-commit-hooks" + "poetry2nix": "poetry2nix", + "pre-commit-hooks": [ + "devenv", + "cachix", + "pre-commit-hooks" + ] }, "locked": { - "lastModified": 1694097209, - "narHash": "sha256-gQmBjjxeSyySjbh0yQVBKApo2KWIFqqbRUvG+Fa+QpM=", + "lastModified": 1708704632, + "narHash": "sha256-w+dOIW60FKMaHI1q5714CSibk99JfYxm0CzTinYWr+Q=", "owner": "cachix", "repo": "devenv", - "rev": "7a8e6a91510efe89d8dcb8e43233f93e86f6b189", + "rev": "2ee4450b0f4b95a1b90f2eb5ffea98b90e48c196", "type": "github" }, "original": { "owner": "cachix", + "ref": "python-rewrite", "repo": "devenv", "type": "github" } @@ -37,16 +99,32 @@ "type": "github" } }, + "flake-compat_2": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, "flake-parts": { "inputs": { "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1693611461, - "narHash": "sha256-aPODl8vAgGQ0ZYFIRisxYG5MOGSkIczvu2Cd8Gb9+1Y=", + "lastModified": 1725234343, + "narHash": "sha256-+ebgonl3NbiKD2UD0x4BszCZQ6sTfL4xioaM49o5B3Y=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "7f53fdb7bdc5bb237da7fefef12d099e4fd611ca", + "rev": "567b938d64d4b4112ee253b9274472dc3a346eb6", "type": "github" }, "original": { @@ -60,11 +138,29 @@ "systems": "systems" }, "locked": { - "lastModified": 1685518550, - "narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=", + "lastModified": 1689068808, + "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", "owner": "numtide", "repo": "flake-utils", - "rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", "type": "github" }, "original": { @@ -82,11 +178,11 @@ ] }, "locked": { - "lastModified": 1660459072, - "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", "owner": "hercules-ci", "repo": "gitignore.nix", - "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", "type": "github" }, "original": { @@ -95,53 +191,90 @@ "type": "github" } }, - "lowdown-src": { - "flake": false, + "nix": { + "inputs": { + "flake-compat": "flake-compat", + "nixpkgs": [ + "devenv", + "cachix", + "devenv", + "nixpkgs" + ], + "nixpkgs-regression": "nixpkgs-regression" + }, "locked": { - "lastModified": 1633514407, - "narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=", - "owner": "kristapsdz", - "repo": "lowdown", - "rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8", + "lastModified": 1712911606, + "narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=", + "owner": "domenkozar", + "repo": "nix", + "rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12", "type": "github" }, "original": { - "owner": "kristapsdz", - "repo": "lowdown", + "owner": "domenkozar", + "ref": "devenv-2.21", + "repo": "nix", "type": "github" } }, - "nix": { + "nix-github-actions": { + "inputs": { + "nixpkgs": [ + "devenv", + "cachix", + "devenv", + "poetry2nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1688870561, + "narHash": "sha256-4UYkifnPEw1nAzqqPOTL2MvWtm3sNGw1UTYTalkTcGY=", + "owner": "nix-community", + "repo": "nix-github-actions", + "rev": "165b1650b753316aa7f1787f3005a8d2da0f5301", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nix-github-actions", + "type": "github" + } + }, + "nix_2": { "inputs": { - "lowdown-src": "lowdown-src", + "flake-compat": [ + "devenv", + "flake-compat" + ], "nixpkgs": [ "devenv", "nixpkgs" ], - "nixpkgs-regression": "nixpkgs-regression" + "nixpkgs-regression": "nixpkgs-regression_2" }, "locked": { - "lastModified": 1676545802, - "narHash": "sha256-EK4rZ+Hd5hsvXnzSzk2ikhStJnD63odF7SzsQ8CuSPU=", + "lastModified": 1712911606, + "narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=", "owner": "domenkozar", "repo": "nix", - "rev": "7c91803598ffbcfe4a55c44ac6d49b2cf07a527f", + "rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12", "type": "github" }, "original": { "owner": "domenkozar", - "ref": "relaxed-flakes", + "ref": "devenv-2.21", "repo": "nix", "type": "github" } }, "nixpkgs": { "locked": { - "lastModified": 1678875422, - "narHash": "sha256-T3o6NcQPwXjxJMn2shz86Chch4ljXgZn746c2caGxd8=", + "lastModified": 1692808169, + "narHash": "sha256-x9Opq06rIiwdwGeK2Ykj69dNc2IvUH1fY55Wm7atwrE=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "126f49a01de5b7e35a43fd43f891ecf6d3a51459", + "rev": "9201b5ff357e781bf014d0330d18555695df7ba8", "type": "github" }, "original": { @@ -153,23 +286,33 @@ }, "nixpkgs-lib": { "locked": { - "dir": "lib", - "lastModified": 1693471703, - "narHash": "sha256-0l03ZBL8P1P6z8MaSDS/MvuU8E75rVxe5eE1N6gxeTo=", + "lastModified": 1725233747, + "narHash": "sha256-Ss8QWLXdr2JCBPcYChJhz4xJm+h/xjl4G0c0XlP6a74=", + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/356624c12086a18f2ea2825fed34523d60ccc4e3.tar.gz" + } + }, + "nixpkgs-regression": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "3e52e76b70d5508f3cec70b882a29199f4d1ee85", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", "type": "github" }, "original": { - "dir": "lib", "owner": "NixOS", - "ref": "nixos-unstable", "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", "type": "github" } }, - "nixpkgs-regression": { + "nixpkgs-regression_2": { "locked": { "lastModified": 1643052045, "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", @@ -187,27 +330,43 @@ }, "nixpkgs-stable": { "locked": { - "lastModified": 1685801374, - "narHash": "sha256-otaSUoFEMM+LjBI1XL/xGB5ao6IwnZOXc47qhIgJe8U=", + "lastModified": 1710695816, + "narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "c37ca420157f4abc31e26f436c1145f8951ff373", + "rev": "614b4613980a522ba49f0d194531beddbb7220d3", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-23.05", + "ref": "nixos-23.11", "repo": "nixpkgs", "type": "github" } }, "nixpkgs_2": { "locked": { - "lastModified": 1694343207, - "narHash": "sha256-jWi7OwFxU5Owi4k2JmiL1sa/OuBCQtpaAesuj5LXC8w=", + "lastModified": 1713361204, + "narHash": "sha256-TA6EDunWTkc5FvDCqU3W2T3SFn0gRZqh6D/hJnM02MM=", + "owner": "cachix", + "repo": "devenv-nixpkgs", + "rev": "285676e87ad9f0ca23d8714a6ab61e7e027020c6", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "rolling", + "repo": "devenv-nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1725910328, + "narHash": "sha256-n9pCtzGZ0httmTwMuEbi5E78UQ4ZbQMr1pzi5N0LAG8=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "78058d810644f5ed276804ce7ea9e82d92bee293", + "rev": "5775c2583f1801df7b790bf7f7d710a19bac66f4", "type": "github" }, "original": { @@ -217,13 +376,38 @@ "type": "github" } }, + "poetry2nix": { + "inputs": { + "flake-utils": "flake-utils", + "nix-github-actions": "nix-github-actions", + "nixpkgs": [ + "devenv", + "cachix", + "devenv", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1692876271, + "narHash": "sha256-IXfZEkI0Mal5y1jr6IRWMqK8GW2/f28xJenZIPQqkY0=", + "owner": "nix-community", + "repo": "poetry2nix", + "rev": "d5006be9c2c2417dafb2e2e5034d83fabd207ee3", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "poetry2nix", + "type": "github" + } + }, "pre-commit-hooks": { "inputs": { "flake-compat": [ "devenv", "flake-compat" ], - "flake-utils": "flake-utils", + "flake-utils": "flake-utils_2", "gitignore": "gitignore", "nixpkgs": [ "devenv", @@ -232,11 +416,11 @@ "nixpkgs-stable": "nixpkgs-stable" }, "locked": { - "lastModified": 1688056373, - "narHash": "sha256-2+SDlNRTKsgo3LBRiMUcoEUb6sDViRNQhzJquZ4koOI=", + "lastModified": 1713775815, + "narHash": "sha256-Wu9cdYTnGQQwtT20QQMg7jzkANKQjwBD9iccfGKkfls=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "5843cf069272d92b60c3ed9e55b7a8989c01d4c7", + "rev": "2ac4dcbf55ed43f3be0bae15e181f08a57af24a4", "type": "github" }, "original": { @@ -249,7 +433,7 @@ "inputs": { "devenv": "devenv", "flake-parts": "flake-parts", - "nixpkgs": "nixpkgs_2" + "nixpkgs": "nixpkgs_3" } }, "systems": { @@ -266,6 +450,21 @@ "repo": "default", "type": "github" } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } } }, "root": "root", diff --git a/vendor/github.com/sagikazarmark/locafero/flake.nix b/vendor/github.com/sagikazarmark/locafero/flake.nix index 209ecf2860..312f1ec8cf 100644 --- a/vendor/github.com/sagikazarmark/locafero/flake.nix +++ b/vendor/github.com/sagikazarmark/locafero/flake.nix @@ -20,6 +20,7 @@ default = { languages = { go.enable = true; + go.package = pkgs.lib.mkDefault pkgs.go_1_23; }; packages = with pkgs; [ @@ -34,11 +35,27 @@ ci = devenv.shells.default; - ci_1_20 = { + ci_1_21 = { imports = [ devenv.shells.ci ]; languages = { - go.package = pkgs.go_1_20; + go.package = pkgs.go_1_21; + }; + }; + + ci_1_22 = { + imports = [ devenv.shells.ci ]; + + languages = { + go.package = pkgs.go_1_22; + }; + }; + + ci_1_23 = { + imports = [ devenv.shells.ci ]; + + languages = { + go.package = pkgs.go_1_23; }; }; }; diff --git a/vendor/github.com/sagikazarmark/locafero/glob.go b/vendor/github.com/sagikazarmark/locafero/glob.go new file mode 100644 index 0000000000..00f833e99c --- /dev/null +++ b/vendor/github.com/sagikazarmark/locafero/glob.go @@ -0,0 +1,5 @@ +//go:build !windows + +package locafero + +const globMatch = "*?[]\\^" diff --git a/vendor/github.com/sagikazarmark/locafero/glob_windows.go b/vendor/github.com/sagikazarmark/locafero/glob_windows.go new file mode 100644 index 0000000000..7aec2b247d --- /dev/null +++ b/vendor/github.com/sagikazarmark/locafero/glob_windows.go @@ -0,0 +1,8 @@ +//go:build windows + +package locafero + +// See [filepath.Match]: +// +// On Windows, escaping is disabled. Instead, '\\' is treated as path separator. +const globMatch = "*?[]^" diff --git a/vendor/github.com/sagikazarmark/slog-shim/.gitignore b/vendor/github.com/sagikazarmark/slog-shim/.gitignore deleted file mode 100644 index dc6d8b5875..0000000000 --- a/vendor/github.com/sagikazarmark/slog-shim/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -/.devenv/ -/.direnv/ -/.task/ -/build/ diff --git a/vendor/github.com/sagikazarmark/slog-shim/LICENSE b/vendor/github.com/sagikazarmark/slog-shim/LICENSE deleted file mode 100644 index 6a66aea5ea..0000000000 --- a/vendor/github.com/sagikazarmark/slog-shim/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2009 The 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/sagikazarmark/slog-shim/README.md b/vendor/github.com/sagikazarmark/slog-shim/README.md deleted file mode 100644 index 1f5be85e10..0000000000 --- a/vendor/github.com/sagikazarmark/slog-shim/README.md +++ /dev/null @@ -1,81 +0,0 @@ -# [slog](https://pkg.go.dev/log/slog) shim - -[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/sagikazarmark/slog-shim/ci.yaml?style=flat-square)](https://github.com/sagikazarmark/slog-shim/actions/workflows/ci.yaml) -[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/mod/github.com/sagikazarmark/slog-shim) -![Go Version](https://img.shields.io/badge/go%20version-%3E=1.20-61CFDD.svg?style=flat-square) -[![built with nix](https://img.shields.io/badge/builtwith-nix-7d81f7?style=flat-square)](https://builtwithnix.org) - -Go 1.21 introduced a [new structured logging package](https://golang.org/doc/go1.21#slog), `log/slog`, to the standard library. -Although it's been eagerly anticipated by many, widespread adoption isn't expected to occur immediately, -especially since updating to Go 1.21 is a decision that most libraries won't make overnight. - -Before this package was added to the standard library, there was an _experimental_ version available at [golang.org/x/exp/slog](https://pkg.go.dev/golang.org/x/exp/slog). -While it's generally advised against using experimental packages in production, -this one served as a sort of backport package for the last few years, -incorporating new features before they were added to the standard library (like `slices`, `maps` or `errors`). - -This package serves as a bridge, helping libraries integrate slog in a backward-compatible way without having to immediately update their Go version requirement to 1.21. On Go 1.21 (and above), it acts as a drop-in replacement for `log/slog`, while below 1.21 it falls back to `golang.org/x/exp/slog`. - -**How does it achieve backwards compatibility?** - -Although there's no consensus on whether dropping support for older Go versions is considered backward compatible, a majority seems to believe it is. -(I don't have scientific proof for this, but it's based on conversations with various individuals across different channels.) - -This package adheres to that interpretation of backward compatibility. On Go 1.21, the shim uses type aliases to offer the same API as `slog/log`. -Once a library upgrades its version requirement to Go 1.21, it should be able to discard this shim and use `log/slog` directly. - -For older Go versions, the library might become unstable after removing the shim. -However, since those older versions are no longer supported, the promise of backward compatibility remains intact. - -## Installation - -```shell -go get github.com/sagikazarmark/slog-shim -``` - -## Usage - -Import this package into your library and use it in your public API: - -```go -package mylib - -import slog "github.com/sagikazarmark/slog-shim" - -func New(logger *slog.Logger) MyLib { - // ... -} -``` - -When using the library, clients can either use `log/slog` (when on Go 1.21) or `golang.org/x/exp/slog` (below Go 1.21): - -```go -package main - -import "log/slog" - -// OR - -import "golang.org/x/exp/slog" - -mylib.New(slog.Default()) -``` - -**Make sure consumers are aware that your API behaves differently on different Go versions.** - -Once you bump your Go version requirement to Go 1.21, you can drop the shim entirely from your code: - -```diff -package mylib - -- import slog "github.com/sagikazarmark/slog-shim" -+ import "log/slog" - -func New(logger *slog.Logger) MyLib { - // ... -} -``` - -## License - -The project is licensed under a [BSD-style license](LICENSE). diff --git a/vendor/github.com/sagikazarmark/slog-shim/attr.go b/vendor/github.com/sagikazarmark/slog-shim/attr.go deleted file mode 100644 index 89608bf3a7..0000000000 --- a/vendor/github.com/sagikazarmark/slog-shim/attr.go +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2022 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. - -//go:build go1.21 - -package slog - -import ( - "log/slog" - "time" -) - -// An Attr is a key-value pair. -type Attr = slog.Attr - -// String returns an Attr for a string value. -func String(key, value string) Attr { - return slog.String(key, value) -} - -// Int64 returns an Attr for an int64. -func Int64(key string, value int64) Attr { - return slog.Int64(key, value) -} - -// Int converts an int to an int64 and returns -// an Attr with that value. -func Int(key string, value int) Attr { - return slog.Int(key, value) -} - -// Uint64 returns an Attr for a uint64. -func Uint64(key string, v uint64) Attr { - return slog.Uint64(key, v) -} - -// Float64 returns an Attr for a floating-point number. -func Float64(key string, v float64) Attr { - return slog.Float64(key, v) -} - -// Bool returns an Attr for a bool. -func Bool(key string, v bool) Attr { - return slog.Bool(key, v) -} - -// Time returns an Attr for a time.Time. -// It discards the monotonic portion. -func Time(key string, v time.Time) Attr { - return slog.Time(key, v) -} - -// Duration returns an Attr for a time.Duration. -func Duration(key string, v time.Duration) Attr { - return slog.Duration(key, v) -} - -// Group returns an Attr for a Group Value. -// The first argument is the key; the remaining arguments -// are converted to Attrs as in [Logger.Log]. -// -// Use Group to collect several key-value pairs under a single -// key on a log line, or as the result of LogValue -// in order to log a single value as multiple Attrs. -func Group(key string, args ...any) Attr { - return slog.Group(key, args...) -} - -// Any returns an Attr for the supplied value. -// See [Value.AnyValue] for how values are treated. -func Any(key string, value any) Attr { - return slog.Any(key, value) -} diff --git a/vendor/github.com/sagikazarmark/slog-shim/attr_120.go b/vendor/github.com/sagikazarmark/slog-shim/attr_120.go deleted file mode 100644 index b664813331..0000000000 --- a/vendor/github.com/sagikazarmark/slog-shim/attr_120.go +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2022 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. - -//go:build !go1.21 - -package slog - -import ( - "time" - - "golang.org/x/exp/slog" -) - -// An Attr is a key-value pair. -type Attr = slog.Attr - -// String returns an Attr for a string value. -func String(key, value string) Attr { - return slog.String(key, value) -} - -// Int64 returns an Attr for an int64. -func Int64(key string, value int64) Attr { - return slog.Int64(key, value) -} - -// Int converts an int to an int64 and returns -// an Attr with that value. -func Int(key string, value int) Attr { - return slog.Int(key, value) -} - -// Uint64 returns an Attr for a uint64. -func Uint64(key string, v uint64) Attr { - return slog.Uint64(key, v) -} - -// Float64 returns an Attr for a floating-point number. -func Float64(key string, v float64) Attr { - return slog.Float64(key, v) -} - -// Bool returns an Attr for a bool. -func Bool(key string, v bool) Attr { - return slog.Bool(key, v) -} - -// Time returns an Attr for a time.Time. -// It discards the monotonic portion. -func Time(key string, v time.Time) Attr { - return slog.Time(key, v) -} - -// Duration returns an Attr for a time.Duration. -func Duration(key string, v time.Duration) Attr { - return slog.Duration(key, v) -} - -// Group returns an Attr for a Group Value. -// The first argument is the key; the remaining arguments -// are converted to Attrs as in [Logger.Log]. -// -// Use Group to collect several key-value pairs under a single -// key on a log line, or as the result of LogValue -// in order to log a single value as multiple Attrs. -func Group(key string, args ...any) Attr { - return slog.Group(key, args...) -} - -// Any returns an Attr for the supplied value. -// See [Value.AnyValue] for how values are treated. -func Any(key string, value any) Attr { - return slog.Any(key, value) -} diff --git a/vendor/github.com/sagikazarmark/slog-shim/flake.lock b/vendor/github.com/sagikazarmark/slog-shim/flake.lock deleted file mode 100644 index 7e8898e9e3..0000000000 --- a/vendor/github.com/sagikazarmark/slog-shim/flake.lock +++ /dev/null @@ -1,273 +0,0 @@ -{ - "nodes": { - "devenv": { - "inputs": { - "flake-compat": "flake-compat", - "nix": "nix", - "nixpkgs": "nixpkgs", - "pre-commit-hooks": "pre-commit-hooks" - }, - "locked": { - "lastModified": 1694097209, - "narHash": "sha256-gQmBjjxeSyySjbh0yQVBKApo2KWIFqqbRUvG+Fa+QpM=", - "owner": "cachix", - "repo": "devenv", - "rev": "7a8e6a91510efe89d8dcb8e43233f93e86f6b189", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "devenv", - "type": "github" - } - }, - "flake-compat": { - "flake": false, - "locked": { - "lastModified": 1673956053, - "narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=", - "owner": "edolstra", - "repo": "flake-compat", - "rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9", - "type": "github" - }, - "original": { - "owner": "edolstra", - "repo": "flake-compat", - "type": "github" - } - }, - "flake-parts": { - "inputs": { - "nixpkgs-lib": "nixpkgs-lib" - }, - "locked": { - "lastModified": 1693611461, - "narHash": "sha256-aPODl8vAgGQ0ZYFIRisxYG5MOGSkIczvu2Cd8Gb9+1Y=", - "owner": "hercules-ci", - "repo": "flake-parts", - "rev": "7f53fdb7bdc5bb237da7fefef12d099e4fd611ca", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "flake-parts", - "type": "github" - } - }, - "flake-utils": { - "inputs": { - "systems": "systems" - }, - "locked": { - "lastModified": 1685518550, - "narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "gitignore": { - "inputs": { - "nixpkgs": [ - "devenv", - "pre-commit-hooks", - "nixpkgs" - ] - }, - "locked": { - "lastModified": 1660459072, - "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", - "owner": "hercules-ci", - "repo": "gitignore.nix", - "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", - "type": "github" - }, - "original": { - "owner": "hercules-ci", - "repo": "gitignore.nix", - "type": "github" - } - }, - "lowdown-src": { - "flake": false, - "locked": { - "lastModified": 1633514407, - "narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=", - "owner": "kristapsdz", - "repo": "lowdown", - "rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8", - "type": "github" - }, - "original": { - "owner": "kristapsdz", - "repo": "lowdown", - "type": "github" - } - }, - "nix": { - "inputs": { - "lowdown-src": "lowdown-src", - "nixpkgs": [ - "devenv", - "nixpkgs" - ], - "nixpkgs-regression": "nixpkgs-regression" - }, - "locked": { - "lastModified": 1676545802, - "narHash": "sha256-EK4rZ+Hd5hsvXnzSzk2ikhStJnD63odF7SzsQ8CuSPU=", - "owner": "domenkozar", - "repo": "nix", - "rev": "7c91803598ffbcfe4a55c44ac6d49b2cf07a527f", - "type": "github" - }, - "original": { - "owner": "domenkozar", - "ref": "relaxed-flakes", - "repo": "nix", - "type": "github" - } - }, - "nixpkgs": { - "locked": { - "lastModified": 1678875422, - "narHash": "sha256-T3o6NcQPwXjxJMn2shz86Chch4ljXgZn746c2caGxd8=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "126f49a01de5b7e35a43fd43f891ecf6d3a51459", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixpkgs-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs-lib": { - "locked": { - "dir": "lib", - "lastModified": 1693471703, - "narHash": "sha256-0l03ZBL8P1P6z8MaSDS/MvuU8E75rVxe5eE1N6gxeTo=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "3e52e76b70d5508f3cec70b882a29199f4d1ee85", - "type": "github" - }, - "original": { - "dir": "lib", - "owner": "NixOS", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs-regression": { - "locked": { - "lastModified": 1643052045, - "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", - "type": "github" - }, - "original": { - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", - "type": "github" - } - }, - "nixpkgs-stable": { - "locked": { - "lastModified": 1685801374, - "narHash": "sha256-otaSUoFEMM+LjBI1XL/xGB5ao6IwnZOXc47qhIgJe8U=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "c37ca420157f4abc31e26f436c1145f8951ff373", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "nixos-23.05", - "repo": "nixpkgs", - "type": "github" - } - }, - "nixpkgs_2": { - "locked": { - "lastModified": 1694345580, - "narHash": "sha256-BbG0NUxQTz1dN/Y87yPWZc/0Kp/coJ0vM3+7sNa5kUM=", - "owner": "NixOS", - "repo": "nixpkgs", - "rev": "f002de6834fdde9c864f33c1ec51da7df19cd832", - "type": "github" - }, - "original": { - "owner": "NixOS", - "ref": "master", - "repo": "nixpkgs", - "type": "github" - } - }, - "pre-commit-hooks": { - "inputs": { - "flake-compat": [ - "devenv", - "flake-compat" - ], - "flake-utils": "flake-utils", - "gitignore": "gitignore", - "nixpkgs": [ - "devenv", - "nixpkgs" - ], - "nixpkgs-stable": "nixpkgs-stable" - }, - "locked": { - "lastModified": 1688056373, - "narHash": "sha256-2+SDlNRTKsgo3LBRiMUcoEUb6sDViRNQhzJquZ4koOI=", - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "rev": "5843cf069272d92b60c3ed9e55b7a8989c01d4c7", - "type": "github" - }, - "original": { - "owner": "cachix", - "repo": "pre-commit-hooks.nix", - "type": "github" - } - }, - "root": { - "inputs": { - "devenv": "devenv", - "flake-parts": "flake-parts", - "nixpkgs": "nixpkgs_2" - } - }, - "systems": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/vendor/github.com/sagikazarmark/slog-shim/handler.go b/vendor/github.com/sagikazarmark/slog-shim/handler.go deleted file mode 100644 index f55556ae18..0000000000 --- a/vendor/github.com/sagikazarmark/slog-shim/handler.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2022 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. - -//go:build go1.21 - -package slog - -import ( - "log/slog" -) - -// A Handler handles log records produced by a Logger.. -// -// A typical handler may print log records to standard error, -// or write them to a file or database, or perhaps augment them -// with additional attributes and pass them on to another handler. -// -// Any of the Handler's methods may be called concurrently with itself -// or with other methods. It is the responsibility of the Handler to -// manage this concurrency. -// -// Users of the slog package should not invoke Handler methods directly. -// They should use the methods of [Logger] instead. -type Handler = slog.Handler - -// HandlerOptions are options for a TextHandler or JSONHandler. -// A zero HandlerOptions consists entirely of default values. -type HandlerOptions = slog.HandlerOptions - -// Keys for "built-in" attributes. -const ( - // TimeKey is the key used by the built-in handlers for the time - // when the log method is called. The associated Value is a [time.Time]. - TimeKey = slog.TimeKey - // LevelKey is the key used by the built-in handlers for the level - // of the log call. The associated value is a [Level]. - LevelKey = slog.LevelKey - // MessageKey is the key used by the built-in handlers for the - // message of the log call. The associated value is a string. - MessageKey = slog.MessageKey - // SourceKey is the key used by the built-in handlers for the source file - // and line of the log call. The associated value is a string. - SourceKey = slog.SourceKey -) diff --git a/vendor/github.com/sagikazarmark/slog-shim/handler_120.go b/vendor/github.com/sagikazarmark/slog-shim/handler_120.go deleted file mode 100644 index 670057573f..0000000000 --- a/vendor/github.com/sagikazarmark/slog-shim/handler_120.go +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2022 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. - -//go:build !go1.21 - -package slog - -import ( - "golang.org/x/exp/slog" -) - -// A Handler handles log records produced by a Logger.. -// -// A typical handler may print log records to standard error, -// or write them to a file or database, or perhaps augment them -// with additional attributes and pass them on to another handler. -// -// Any of the Handler's methods may be called concurrently with itself -// or with other methods. It is the responsibility of the Handler to -// manage this concurrency. -// -// Users of the slog package should not invoke Handler methods directly. -// They should use the methods of [Logger] instead. -type Handler = slog.Handler - -// HandlerOptions are options for a TextHandler or JSONHandler. -// A zero HandlerOptions consists entirely of default values. -type HandlerOptions = slog.HandlerOptions - -// Keys for "built-in" attributes. -const ( - // TimeKey is the key used by the built-in handlers for the time - // when the log method is called. The associated Value is a [time.Time]. - TimeKey = slog.TimeKey - // LevelKey is the key used by the built-in handlers for the level - // of the log call. The associated value is a [Level]. - LevelKey = slog.LevelKey - // MessageKey is the key used by the built-in handlers for the - // message of the log call. The associated value is a string. - MessageKey = slog.MessageKey - // SourceKey is the key used by the built-in handlers for the source file - // and line of the log call. The associated value is a string. - SourceKey = slog.SourceKey -) diff --git a/vendor/github.com/sagikazarmark/slog-shim/json_handler.go b/vendor/github.com/sagikazarmark/slog-shim/json_handler.go deleted file mode 100644 index 7c22bd81e4..0000000000 --- a/vendor/github.com/sagikazarmark/slog-shim/json_handler.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2022 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. - -//go:build go1.21 - -package slog - -import ( - "io" - "log/slog" -) - -// JSONHandler is a Handler that writes Records to an io.Writer as -// line-delimited JSON objects. -type JSONHandler = slog.JSONHandler - -// NewJSONHandler creates a JSONHandler that writes to w, -// using the given options. -// If opts is nil, the default options are used. -func NewJSONHandler(w io.Writer, opts *HandlerOptions) *JSONHandler { - return slog.NewJSONHandler(w, opts) -} diff --git a/vendor/github.com/sagikazarmark/slog-shim/json_handler_120.go b/vendor/github.com/sagikazarmark/slog-shim/json_handler_120.go deleted file mode 100644 index 7b14f10ba9..0000000000 --- a/vendor/github.com/sagikazarmark/slog-shim/json_handler_120.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2022 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. - -//go:build !go1.21 - -package slog - -import ( - "io" - - "golang.org/x/exp/slog" -) - -// JSONHandler is a Handler that writes Records to an io.Writer as -// line-delimited JSON objects. -type JSONHandler = slog.JSONHandler - -// NewJSONHandler creates a JSONHandler that writes to w, -// using the given options. -// If opts is nil, the default options are used. -func NewJSONHandler(w io.Writer, opts *HandlerOptions) *JSONHandler { - return slog.NewJSONHandler(w, opts) -} diff --git a/vendor/github.com/sagikazarmark/slog-shim/level.go b/vendor/github.com/sagikazarmark/slog-shim/level.go deleted file mode 100644 index 07288cf891..0000000000 --- a/vendor/github.com/sagikazarmark/slog-shim/level.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2022 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. - -//go:build go1.21 - -package slog - -import ( - "log/slog" -) - -// A Level is the importance or severity of a log event. -// The higher the level, the more important or severe the event. -type Level = slog.Level - -// Level numbers are inherently arbitrary, -// but we picked them to satisfy three constraints. -// Any system can map them to another numbering scheme if it wishes. -// -// First, we wanted the default level to be Info, Since Levels are ints, Info is -// the default value for int, zero. -// -// Second, we wanted to make it easy to use levels to specify logger verbosity. -// Since a larger level means a more severe event, a logger that accepts events -// with smaller (or more negative) level means a more verbose logger. Logger -// verbosity is thus the negation of event severity, and the default verbosity -// of 0 accepts all events at least as severe as INFO. -// -// Third, we wanted some room between levels to accommodate schemes with named -// levels between ours. For example, Google Cloud Logging defines a Notice level -// between Info and Warn. Since there are only a few of these intermediate -// levels, the gap between the numbers need not be large. Our gap of 4 matches -// OpenTelemetry's mapping. Subtracting 9 from an OpenTelemetry level in the -// DEBUG, INFO, WARN and ERROR ranges converts it to the corresponding slog -// Level range. OpenTelemetry also has the names TRACE and FATAL, which slog -// does not. But those OpenTelemetry levels can still be represented as slog -// Levels by using the appropriate integers. -// -// Names for common levels. -const ( - LevelDebug Level = slog.LevelDebug - LevelInfo Level = slog.LevelInfo - LevelWarn Level = slog.LevelWarn - LevelError Level = slog.LevelError -) - -// A LevelVar is a Level variable, to allow a Handler level to change -// dynamically. -// It implements Leveler as well as a Set method, -// and it is safe for use by multiple goroutines. -// The zero LevelVar corresponds to LevelInfo. -type LevelVar = slog.LevelVar - -// A Leveler provides a Level value. -// -// As Level itself implements Leveler, clients typically supply -// a Level value wherever a Leveler is needed, such as in HandlerOptions. -// Clients who need to vary the level dynamically can provide a more complex -// Leveler implementation such as *LevelVar. -type Leveler = slog.Leveler diff --git a/vendor/github.com/sagikazarmark/slog-shim/level_120.go b/vendor/github.com/sagikazarmark/slog-shim/level_120.go deleted file mode 100644 index d3feb94203..0000000000 --- a/vendor/github.com/sagikazarmark/slog-shim/level_120.go +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2022 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. - -//go:build !go1.21 - -package slog - -import ( - "golang.org/x/exp/slog" -) - -// A Level is the importance or severity of a log event. -// The higher the level, the more important or severe the event. -type Level = slog.Level - -// Level numbers are inherently arbitrary, -// but we picked them to satisfy three constraints. -// Any system can map them to another numbering scheme if it wishes. -// -// First, we wanted the default level to be Info, Since Levels are ints, Info is -// the default value for int, zero. -// -// Second, we wanted to make it easy to use levels to specify logger verbosity. -// Since a larger level means a more severe event, a logger that accepts events -// with smaller (or more negative) level means a more verbose logger. Logger -// verbosity is thus the negation of event severity, and the default verbosity -// of 0 accepts all events at least as severe as INFO. -// -// Third, we wanted some room between levels to accommodate schemes with named -// levels between ours. For example, Google Cloud Logging defines a Notice level -// between Info and Warn. Since there are only a few of these intermediate -// levels, the gap between the numbers need not be large. Our gap of 4 matches -// OpenTelemetry's mapping. Subtracting 9 from an OpenTelemetry level in the -// DEBUG, INFO, WARN and ERROR ranges converts it to the corresponding slog -// Level range. OpenTelemetry also has the names TRACE and FATAL, which slog -// does not. But those OpenTelemetry levels can still be represented as slog -// Levels by using the appropriate integers. -// -// Names for common levels. -const ( - LevelDebug Level = slog.LevelDebug - LevelInfo Level = slog.LevelInfo - LevelWarn Level = slog.LevelWarn - LevelError Level = slog.LevelError -) - -// A LevelVar is a Level variable, to allow a Handler level to change -// dynamically. -// It implements Leveler as well as a Set method, -// and it is safe for use by multiple goroutines. -// The zero LevelVar corresponds to LevelInfo. -type LevelVar = slog.LevelVar - -// A Leveler provides a Level value. -// -// As Level itself implements Leveler, clients typically supply -// a Level value wherever a Leveler is needed, such as in HandlerOptions. -// Clients who need to vary the level dynamically can provide a more complex -// Leveler implementation such as *LevelVar. -type Leveler = slog.Leveler diff --git a/vendor/github.com/sagikazarmark/slog-shim/logger.go b/vendor/github.com/sagikazarmark/slog-shim/logger.go deleted file mode 100644 index e80036bec5..0000000000 --- a/vendor/github.com/sagikazarmark/slog-shim/logger.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2022 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. - -//go:build go1.21 - -package slog - -import ( - "context" - "log" - "log/slog" -) - -// Default returns the default Logger. -func Default() *Logger { return slog.Default() } - -// SetDefault makes l the default Logger. -// After this call, output from the log package's default Logger -// (as with [log.Print], etc.) will be logged at LevelInfo using l's Handler. -func SetDefault(l *Logger) { - slog.SetDefault(l) -} - -// A Logger records structured information about each call to its -// Log, Debug, Info, Warn, and Error methods. -// For each call, it creates a Record and passes it to a Handler. -// -// To create a new Logger, call [New] or a Logger method -// that begins "With". -type Logger = slog.Logger - -// New creates a new Logger with the given non-nil Handler. -func New(h Handler) *Logger { - return slog.New(h) -} - -// With calls Logger.With on the default logger. -func With(args ...any) *Logger { - return slog.With(args...) -} - -// NewLogLogger returns a new log.Logger such that each call to its Output method -// dispatches a Record to the specified handler. The logger acts as a bridge from -// the older log API to newer structured logging handlers. -func NewLogLogger(h Handler, level Level) *log.Logger { - return slog.NewLogLogger(h, level) -} - -// Debug calls Logger.Debug on the default logger. -func Debug(msg string, args ...any) { - slog.Debug(msg, args...) -} - -// DebugContext calls Logger.DebugContext on the default logger. -func DebugContext(ctx context.Context, msg string, args ...any) { - slog.DebugContext(ctx, msg, args...) -} - -// Info calls Logger.Info on the default logger. -func Info(msg string, args ...any) { - slog.Info(msg, args...) -} - -// InfoContext calls Logger.InfoContext on the default logger. -func InfoContext(ctx context.Context, msg string, args ...any) { - slog.InfoContext(ctx, msg, args...) -} - -// Warn calls Logger.Warn on the default logger. -func Warn(msg string, args ...any) { - slog.Warn(msg, args...) -} - -// WarnContext calls Logger.WarnContext on the default logger. -func WarnContext(ctx context.Context, msg string, args ...any) { - slog.WarnContext(ctx, msg, args...) -} - -// Error calls Logger.Error on the default logger. -func Error(msg string, args ...any) { - slog.Error(msg, args...) -} - -// ErrorContext calls Logger.ErrorContext on the default logger. -func ErrorContext(ctx context.Context, msg string, args ...any) { - slog.ErrorContext(ctx, msg, args...) -} - -// Log calls Logger.Log on the default logger. -func Log(ctx context.Context, level Level, msg string, args ...any) { - slog.Log(ctx, level, msg, args...) -} - -// LogAttrs calls Logger.LogAttrs on the default logger. -func LogAttrs(ctx context.Context, level Level, msg string, attrs ...Attr) { - slog.LogAttrs(ctx, level, msg, attrs...) -} diff --git a/vendor/github.com/sagikazarmark/slog-shim/logger_120.go b/vendor/github.com/sagikazarmark/slog-shim/logger_120.go deleted file mode 100644 index 97ebdd5e1c..0000000000 --- a/vendor/github.com/sagikazarmark/slog-shim/logger_120.go +++ /dev/null @@ -1,99 +0,0 @@ -// Copyright 2022 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. - -//go:build !go1.21 - -package slog - -import ( - "context" - "log" - - "golang.org/x/exp/slog" -) - -// Default returns the default Logger. -func Default() *Logger { return slog.Default() } - -// SetDefault makes l the default Logger. -// After this call, output from the log package's default Logger -// (as with [log.Print], etc.) will be logged at LevelInfo using l's Handler. -func SetDefault(l *Logger) { - slog.SetDefault(l) -} - -// A Logger records structured information about each call to its -// Log, Debug, Info, Warn, and Error methods. -// For each call, it creates a Record and passes it to a Handler. -// -// To create a new Logger, call [New] or a Logger method -// that begins "With". -type Logger = slog.Logger - -// New creates a new Logger with the given non-nil Handler. -func New(h Handler) *Logger { - return slog.New(h) -} - -// With calls Logger.With on the default logger. -func With(args ...any) *Logger { - return slog.With(args...) -} - -// NewLogLogger returns a new log.Logger such that each call to its Output method -// dispatches a Record to the specified handler. The logger acts as a bridge from -// the older log API to newer structured logging handlers. -func NewLogLogger(h Handler, level Level) *log.Logger { - return slog.NewLogLogger(h, level) -} - -// Debug calls Logger.Debug on the default logger. -func Debug(msg string, args ...any) { - slog.Debug(msg, args...) -} - -// DebugContext calls Logger.DebugContext on the default logger. -func DebugContext(ctx context.Context, msg string, args ...any) { - slog.DebugContext(ctx, msg, args...) -} - -// Info calls Logger.Info on the default logger. -func Info(msg string, args ...any) { - slog.Info(msg, args...) -} - -// InfoContext calls Logger.InfoContext on the default logger. -func InfoContext(ctx context.Context, msg string, args ...any) { - slog.InfoContext(ctx, msg, args...) -} - -// Warn calls Logger.Warn on the default logger. -func Warn(msg string, args ...any) { - slog.Warn(msg, args...) -} - -// WarnContext calls Logger.WarnContext on the default logger. -func WarnContext(ctx context.Context, msg string, args ...any) { - slog.WarnContext(ctx, msg, args...) -} - -// Error calls Logger.Error on the default logger. -func Error(msg string, args ...any) { - slog.Error(msg, args...) -} - -// ErrorContext calls Logger.ErrorContext on the default logger. -func ErrorContext(ctx context.Context, msg string, args ...any) { - slog.ErrorContext(ctx, msg, args...) -} - -// Log calls Logger.Log on the default logger. -func Log(ctx context.Context, level Level, msg string, args ...any) { - slog.Log(ctx, level, msg, args...) -} - -// LogAttrs calls Logger.LogAttrs on the default logger. -func LogAttrs(ctx context.Context, level Level, msg string, attrs ...Attr) { - slog.LogAttrs(ctx, level, msg, attrs...) -} diff --git a/vendor/github.com/sagikazarmark/slog-shim/record.go b/vendor/github.com/sagikazarmark/slog-shim/record.go deleted file mode 100644 index 85ad1f7842..0000000000 --- a/vendor/github.com/sagikazarmark/slog-shim/record.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2022 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. - -//go:build go1.21 - -package slog - -import ( - "log/slog" - "time" -) - -// A Record holds information about a log event. -// Copies of a Record share state. -// Do not modify a Record after handing out a copy to it. -// Call [NewRecord] to create a new Record. -// Use [Record.Clone] to create a copy with no shared state. -type Record = slog.Record - -// NewRecord creates a Record from the given arguments. -// Use [Record.AddAttrs] to add attributes to the Record. -// -// NewRecord is intended for logging APIs that want to support a [Handler] as -// a backend. -func NewRecord(t time.Time, level Level, msg string, pc uintptr) Record { - return slog.NewRecord(t, level, msg, pc) -} - -// Source describes the location of a line of source code. -type Source = slog.Source diff --git a/vendor/github.com/sagikazarmark/slog-shim/record_120.go b/vendor/github.com/sagikazarmark/slog-shim/record_120.go deleted file mode 100644 index c2eaf4e796..0000000000 --- a/vendor/github.com/sagikazarmark/slog-shim/record_120.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2022 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. - -//go:build !go1.21 - -package slog - -import ( - "time" - - "golang.org/x/exp/slog" -) - -// A Record holds information about a log event. -// Copies of a Record share state. -// Do not modify a Record after handing out a copy to it. -// Call [NewRecord] to create a new Record. -// Use [Record.Clone] to create a copy with no shared state. -type Record = slog.Record - -// NewRecord creates a Record from the given arguments. -// Use [Record.AddAttrs] to add attributes to the Record. -// -// NewRecord is intended for logging APIs that want to support a [Handler] as -// a backend. -func NewRecord(t time.Time, level Level, msg string, pc uintptr) Record { - return slog.NewRecord(t, level, msg, pc) -} - -// Source describes the location of a line of source code. -type Source = slog.Source diff --git a/vendor/github.com/sagikazarmark/slog-shim/text_handler.go b/vendor/github.com/sagikazarmark/slog-shim/text_handler.go deleted file mode 100644 index 45f6cfcba5..0000000000 --- a/vendor/github.com/sagikazarmark/slog-shim/text_handler.go +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2022 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. - -//go:build go1.21 - -package slog - -import ( - "io" - "log/slog" -) - -// TextHandler is a Handler that writes Records to an io.Writer as a -// sequence of key=value pairs separated by spaces and followed by a newline. -type TextHandler = slog.TextHandler - -// NewTextHandler creates a TextHandler that writes to w, -// using the given options. -// If opts is nil, the default options are used. -func NewTextHandler(w io.Writer, opts *HandlerOptions) *TextHandler { - return slog.NewTextHandler(w, opts) -} diff --git a/vendor/github.com/sagikazarmark/slog-shim/text_handler_120.go b/vendor/github.com/sagikazarmark/slog-shim/text_handler_120.go deleted file mode 100644 index a69d63ccea..0000000000 --- a/vendor/github.com/sagikazarmark/slog-shim/text_handler_120.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2022 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. - -//go:build !go1.21 - -package slog - -import ( - "io" - - "golang.org/x/exp/slog" -) - -// TextHandler is a Handler that writes Records to an io.Writer as a -// sequence of key=value pairs separated by spaces and followed by a newline. -type TextHandler = slog.TextHandler - -// NewTextHandler creates a TextHandler that writes to w, -// using the given options. -// If opts is nil, the default options are used. -func NewTextHandler(w io.Writer, opts *HandlerOptions) *TextHandler { - return slog.NewTextHandler(w, opts) -} diff --git a/vendor/github.com/sagikazarmark/slog-shim/value.go b/vendor/github.com/sagikazarmark/slog-shim/value.go deleted file mode 100644 index 61173eb946..0000000000 --- a/vendor/github.com/sagikazarmark/slog-shim/value.go +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright 2022 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. - -//go:build go1.21 - -package slog - -import ( - "log/slog" - "time" -) - -// A Value can represent any Go value, but unlike type any, -// it can represent most small values without an allocation. -// The zero Value corresponds to nil. -type Value = slog.Value - -// Kind is the kind of a Value. -type Kind = slog.Kind - -// The following list is sorted alphabetically, but it's also important that -// KindAny is 0 so that a zero Value represents nil. -const ( - KindAny = slog.KindAny - KindBool = slog.KindBool - KindDuration = slog.KindDuration - KindFloat64 = slog.KindFloat64 - KindInt64 = slog.KindInt64 - KindString = slog.KindString - KindTime = slog.KindTime - KindUint64 = slog.KindUint64 - KindGroup = slog.KindGroup - KindLogValuer = slog.KindLogValuer -) - -//////////////// Constructors - -// StringValue returns a new Value for a string. -func StringValue(value string) Value { - return slog.StringValue(value) -} - -// IntValue returns a Value for an int. -func IntValue(v int) Value { - return slog.IntValue(v) -} - -// Int64Value returns a Value for an int64. -func Int64Value(v int64) Value { - return slog.Int64Value(v) -} - -// Uint64Value returns a Value for a uint64. -func Uint64Value(v uint64) Value { - return slog.Uint64Value(v) -} - -// Float64Value returns a Value for a floating-point number. -func Float64Value(v float64) Value { - return slog.Float64Value(v) -} - -// BoolValue returns a Value for a bool. -func BoolValue(v bool) Value { - return slog.BoolValue(v) -} - -// TimeValue returns a Value for a time.Time. -// It discards the monotonic portion. -func TimeValue(v time.Time) Value { - return slog.TimeValue(v) -} - -// DurationValue returns a Value for a time.Duration. -func DurationValue(v time.Duration) Value { - return slog.DurationValue(v) -} - -// GroupValue returns a new Value for a list of Attrs. -// The caller must not subsequently mutate the argument slice. -func GroupValue(as ...Attr) Value { - return slog.GroupValue(as...) -} - -// AnyValue returns a Value for the supplied value. -// -// If the supplied value is of type Value, it is returned -// unmodified. -// -// Given a value of one of Go's predeclared string, bool, or -// (non-complex) numeric types, AnyValue returns a Value of kind -// String, Bool, Uint64, Int64, or Float64. The width of the -// original numeric type is not preserved. -// -// Given a time.Time or time.Duration value, AnyValue returns a Value of kind -// KindTime or KindDuration. The monotonic time is not preserved. -// -// For nil, or values of all other types, including named types whose -// underlying type is numeric, AnyValue returns a value of kind KindAny. -func AnyValue(v any) Value { - return slog.AnyValue(v) -} - -// A LogValuer is any Go value that can convert itself into a Value for logging. -// -// This mechanism may be used to defer expensive operations until they are -// needed, or to expand a single value into a sequence of components. -type LogValuer = slog.LogValuer diff --git a/vendor/github.com/sagikazarmark/slog-shim/value_120.go b/vendor/github.com/sagikazarmark/slog-shim/value_120.go deleted file mode 100644 index 0f9f871eee..0000000000 --- a/vendor/github.com/sagikazarmark/slog-shim/value_120.go +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2022 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. - -//go:build !go1.21 - -package slog - -import ( - "time" - - "golang.org/x/exp/slog" -) - -// A Value can represent any Go value, but unlike type any, -// it can represent most small values without an allocation. -// The zero Value corresponds to nil. -type Value = slog.Value - -// Kind is the kind of a Value. -type Kind = slog.Kind - -// The following list is sorted alphabetically, but it's also important that -// KindAny is 0 so that a zero Value represents nil. -const ( - KindAny = slog.KindAny - KindBool = slog.KindBool - KindDuration = slog.KindDuration - KindFloat64 = slog.KindFloat64 - KindInt64 = slog.KindInt64 - KindString = slog.KindString - KindTime = slog.KindTime - KindUint64 = slog.KindUint64 - KindGroup = slog.KindGroup - KindLogValuer = slog.KindLogValuer -) - -//////////////// Constructors - -// StringValue returns a new Value for a string. -func StringValue(value string) Value { - return slog.StringValue(value) -} - -// IntValue returns a Value for an int. -func IntValue(v int) Value { - return slog.IntValue(v) -} - -// Int64Value returns a Value for an int64. -func Int64Value(v int64) Value { - return slog.Int64Value(v) -} - -// Uint64Value returns a Value for a uint64. -func Uint64Value(v uint64) Value { - return slog.Uint64Value(v) -} - -// Float64Value returns a Value for a floating-point number. -func Float64Value(v float64) Value { - return slog.Float64Value(v) -} - -// BoolValue returns a Value for a bool. -func BoolValue(v bool) Value { - return slog.BoolValue(v) -} - -// TimeValue returns a Value for a time.Time. -// It discards the monotonic portion. -func TimeValue(v time.Time) Value { - return slog.TimeValue(v) -} - -// DurationValue returns a Value for a time.Duration. -func DurationValue(v time.Duration) Value { - return slog.DurationValue(v) -} - -// GroupValue returns a new Value for a list of Attrs. -// The caller must not subsequently mutate the argument slice. -func GroupValue(as ...Attr) Value { - return slog.GroupValue(as...) -} - -// AnyValue returns a Value for the supplied value. -// -// If the supplied value is of type Value, it is returned -// unmodified. -// -// Given a value of one of Go's predeclared string, bool, or -// (non-complex) numeric types, AnyValue returns a Value of kind -// String, Bool, Uint64, Int64, or Float64. The width of the -// original numeric type is not preserved. -// -// Given a time.Time or time.Duration value, AnyValue returns a Value of kind -// KindTime or KindDuration. The monotonic time is not preserved. -// -// For nil, or values of all other types, including named types whose -// underlying type is numeric, AnyValue returns a value of kind KindAny. -func AnyValue(v any) Value { - return slog.AnyValue(v) -} - -// A LogValuer is any Go value that can convert itself into a Value for logging. -// -// This mechanism may be used to defer expensive operations until they are -// needed, or to expand a single value into a sequence of components. -type LogValuer = slog.LogValuer diff --git a/vendor/github.com/spf13/afero/README.md b/vendor/github.com/spf13/afero/README.md index 619af574f3..86f1545543 100644 --- a/vendor/github.com/spf13/afero/README.md +++ b/vendor/github.com/spf13/afero/README.md @@ -2,7 +2,11 @@ A FileSystem Abstraction System for Go -[![Test](https://github.com/spf13/afero/actions/workflows/test.yml/badge.svg)](https://github.com/spf13/afero/actions/workflows/test.yml) [![GoDoc](https://godoc.org/github.com/spf13/afero?status.svg)](https://godoc.org/github.com/spf13/afero) [![Join the chat at https://gitter.im/spf13/afero](https://badges.gitter.im/Dev%20Chat.svg)](https://gitter.im/spf13/afero?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/spf13/afero/ci.yaml?branch=master&style=flat-square)](https://github.com/spf13/afero/actions?query=workflow%3ACI) +[![Join the chat at https://gitter.im/spf13/afero](https://badges.gitter.im/Dev%20Chat.svg)](https://gitter.im/spf13/afero?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +[![Go Report Card](https://goreportcard.com/badge/github.com/spf13/afero?style=flat-square)](https://goreportcard.com/report/github.com/spf13/afero) +![Go Version](https://img.shields.io/badge/go%20version-%3E=1.23-61CFDD.svg?style=flat-square) +[![PkgGoDev](https://pkg.go.dev/badge/mod/github.com/spf13/afero)](https://pkg.go.dev/mod/github.com/spf13/afero) # Overview @@ -427,6 +431,39 @@ See the [Releases Page](https://github.com/spf13/afero/releases). 4. Push to the branch (`git push origin my-new-feature`) 5. Create new Pull Request +## Releasing + +As of version 1.14.0, Afero moved implementations with third-party libraries to +their own submodules. + +Releasing a new version now requires a few steps: + +``` +VERSION=X.Y.Z +git tag -a v$VERSION -m "Release $VERSION" +git push origin v$VERSION + +cd gcsfs +go get github.com/spf13/afero@v$VERSION +go mod tidy +git commit -am "Update afero to v$VERSION" +git tag -a gcsfs/v$VERSION -m "Release gcsfs $VERSION" +git push origin gcsfs/v$VERSION +cd .. + +cd sftpfs +go get github.com/spf13/afero@v$VERSION +go mod tidy +git commit -am "Update afero to v$VERSION" +git tag -a sftpfs/v$VERSION -m "Release sftpfs $VERSION" +git push origin sftpfs/v$VERSION +cd .. + +git push +``` + +TODO: move these instructions to a Makefile or something + ## Contributors Names in no particular order: diff --git a/vendor/github.com/spf13/cobra/README.md b/vendor/github.com/spf13/cobra/README.md index 6444f4b7f6..71757151c3 100644 --- a/vendor/github.com/spf13/cobra/README.md +++ b/vendor/github.com/spf13/cobra/README.md @@ -1,4 +1,5 @@ -![cobra logo](assets/CobraMain.png) + +![cobra logo](https://github.com/user-attachments/assets/cbc3adf8-0dff-46e9-a88d-5e2d971c169e) Cobra is a library for creating powerful modern CLI applications. @@ -105,7 +106,7 @@ go install github.com/spf13/cobra-cli@latest For complete details on using the Cobra-CLI generator, please read [The Cobra Generator README](https://github.com/spf13/cobra-cli/blob/main/README.md) -For complete details on using the Cobra library, please read the [The Cobra User Guide](site/content/user_guide.md). +For complete details on using the Cobra library, please read [The Cobra User Guide](site/content/user_guide.md). # License diff --git a/vendor/github.com/spf13/cobra/active_help.go b/vendor/github.com/spf13/cobra/active_help.go index 25c30e3ccc..b3e2dadfed 100644 --- a/vendor/github.com/spf13/cobra/active_help.go +++ b/vendor/github.com/spf13/cobra/active_help.go @@ -35,7 +35,7 @@ const ( // This function can be called multiple times before and/or after completions are added to // the array. Each time this function is called with the same array, the new // ActiveHelp line will be shown below the previous ones when completion is triggered. -func AppendActiveHelp(compArray []string, activeHelpStr string) []string { +func AppendActiveHelp(compArray []Completion, activeHelpStr string) []Completion { return append(compArray, fmt.Sprintf("%s%s", activeHelpMarker, activeHelpStr)) } diff --git a/vendor/github.com/spf13/cobra/bash_completionsV2.go b/vendor/github.com/spf13/cobra/bash_completionsV2.go index 1cce5c329c..d2397aa366 100644 --- a/vendor/github.com/spf13/cobra/bash_completionsV2.go +++ b/vendor/github.com/spf13/cobra/bash_completionsV2.go @@ -146,7 +146,7 @@ __%[1]s_process_completion_results() { if (((directive & shellCompDirectiveFilterFileExt) != 0)); then # File extension filtering - local fullFilter filter filteringCmd + local fullFilter="" filter filteringCmd # Do not use quotes around the $completions variable or else newline # characters will be kept. @@ -177,20 +177,71 @@ __%[1]s_process_completion_results() { __%[1]s_handle_special_char "$cur" = # Print the activeHelp statements before we finish + __%[1]s_handle_activeHelp +} + +__%[1]s_handle_activeHelp() { + # Print the activeHelp statements if ((${#activeHelp[*]} != 0)); then - printf "\n"; - printf "%%s\n" "${activeHelp[@]}" - printf "\n" - - # The prompt format is only available from bash 4.4. - # We test if it is available before using it. - if (x=${PS1@P}) 2> /dev/null; then - printf "%%s" "${PS1@P}${COMP_LINE[@]}" - else - # Can't print the prompt. Just print the - # text the user had typed, it is workable enough. - printf "%%s" "${COMP_LINE[@]}" + if [ -z $COMP_TYPE ]; then + # Bash v3 does not set the COMP_TYPE variable. + printf "\n"; + printf "%%s\n" "${activeHelp[@]}" + printf "\n" + __%[1]s_reprint_commandLine + return fi + + # Only print ActiveHelp on the second TAB press + if [ $COMP_TYPE -eq 63 ]; then + printf "\n" + printf "%%s\n" "${activeHelp[@]}" + + if ((${#COMPREPLY[*]} == 0)); then + # When there are no completion choices from the program, file completion + # may kick in if the program has not disabled it; in such a case, we want + # to know if any files will match what the user typed, so that we know if + # there will be completions presented, so that we know how to handle ActiveHelp. + # To find out, we actually trigger the file completion ourselves; + # the call to _filedir will fill COMPREPLY if files match. + if (((directive & shellCompDirectiveNoFileComp) == 0)); then + __%[1]s_debug "Listing files" + _filedir + fi + fi + + if ((${#COMPREPLY[*]} != 0)); then + # If there are completion choices to be shown, print a delimiter. + # Re-printing the command-line will automatically be done + # by the shell when it prints the completion choices. + printf -- "--" + else + # When there are no completion choices at all, we need + # to re-print the command-line since the shell will + # not be doing it itself. + __%[1]s_reprint_commandLine + fi + elif [ $COMP_TYPE -eq 37 ] || [ $COMP_TYPE -eq 42 ]; then + # For completion type: menu-complete/menu-complete-backward and insert-completions + # the completions are immediately inserted into the command-line, so we first + # print the activeHelp message and reprint the command-line since the shell won't. + printf "\n" + printf "%%s\n" "${activeHelp[@]}" + + __%[1]s_reprint_commandLine + fi + fi +} + +__%[1]s_reprint_commandLine() { + # The prompt format is only available from bash 4.4. + # We test if it is available before using it. + if (x=${PS1@P}) 2> /dev/null; then + printf "%%s" "${PS1@P}${COMP_LINE[@]}" + else + # Can't print the prompt. Just print the + # text the user had typed, it is workable enough. + printf "%%s" "${COMP_LINE[@]}" fi } @@ -201,6 +252,8 @@ __%[1]s_extract_activeHelp() { local endIndex=${#activeHelpMarker} while IFS='' read -r comp; do + [[ -z $comp ]] && continue + if [[ ${comp:0:endIndex} == $activeHelpMarker ]]; then comp=${comp:endIndex} __%[1]s_debug "ActiveHelp found: $comp" @@ -223,16 +276,21 @@ __%[1]s_handle_completion_types() { # If the user requested inserting one completion at a time, or all # completions at once on the command-line we must remove the descriptions. # https://github.com/spf13/cobra/issues/1508 - local tab=$'\t' comp - while IFS='' read -r comp; do - [[ -z $comp ]] && continue - # Strip any description - comp=${comp%%%%$tab*} - # Only consider the completions that match - if [[ $comp == "$cur"* ]]; then - COMPREPLY+=("$comp") - fi - done < <(printf "%%s\n" "${completions[@]}") + + # If there are no completions, we don't need to do anything + (( ${#completions[@]} == 0 )) && return 0 + + local tab=$'\t' + + # Strip any description and escape the completion to handled special characters + IFS=$'\n' read -ra completions -d '' < <(printf "%%q\n" "${completions[@]%%%%$tab*}") + + # Only consider the completions that match + IFS=$'\n' read -ra COMPREPLY -d '' < <(IFS=$'\n'; compgen -W "${completions[*]}" -- "${cur}") + + # compgen looses the escaping so we need to escape all completions again since they will + # all be inserted on the command-line. + IFS=$'\n' read -ra COMPREPLY -d '' < <(printf "%%q\n" "${COMPREPLY[@]}") ;; *) @@ -243,11 +301,25 @@ __%[1]s_handle_completion_types() { } __%[1]s_handle_standard_completion_case() { - local tab=$'\t' comp + local tab=$'\t' + + # If there are no completions, we don't need to do anything + (( ${#completions[@]} == 0 )) && return 0 # Short circuit to optimize if we don't have descriptions if [[ "${completions[*]}" != *$tab* ]]; then - IFS=$'\n' read -ra COMPREPLY -d '' < <(compgen -W "${completions[*]}" -- "$cur") + # First, escape the completions to handle special characters + IFS=$'\n' read -ra completions -d '' < <(printf "%%q\n" "${completions[@]}") + # Only consider the completions that match what the user typed + IFS=$'\n' read -ra COMPREPLY -d '' < <(IFS=$'\n'; compgen -W "${completions[*]}" -- "${cur}") + + # compgen looses the escaping so, if there is only a single completion, we need to + # escape it again because it will be inserted on the command-line. If there are multiple + # completions, we don't want to escape them because they will be printed in a list + # and we don't want to show escape characters in that list. + if (( ${#COMPREPLY[@]} == 1 )); then + COMPREPLY[0]=$(printf "%%q" "${COMPREPLY[0]}") + fi return 0 fi @@ -256,23 +328,39 @@ __%[1]s_handle_standard_completion_case() { # Look for the longest completion so that we can format things nicely while IFS='' read -r compline; do [[ -z $compline ]] && continue - # Strip any description before checking the length - comp=${compline%%%%$tab*} + + # Before checking if the completion matches what the user typed, + # we need to strip any description and escape the completion to handle special + # characters because those escape characters are part of what the user typed. + # Don't call "printf" in a sub-shell because it will be much slower + # since we are in a loop. + printf -v comp "%%q" "${compline%%%%$tab*}" &>/dev/null || comp=$(printf "%%q" "${compline%%%%$tab*}") + # Only consider the completions that match [[ $comp == "$cur"* ]] || continue + + # The completions matches. Add it to the list of full completions including + # its description. We don't escape the completion because it may get printed + # in a list if there are more than one and we don't want show escape characters + # in that list. COMPREPLY+=("$compline") + + # Strip any description before checking the length, and again, don't escape + # the completion because this length is only used when printing the completions + # in a list and we don't want show escape characters in that list. + comp=${compline%%%%$tab*} if ((${#comp}>longest)); then longest=${#comp} fi done < <(printf "%%s\n" "${completions[@]}") - # If there is a single completion left, remove the description text + # If there is a single completion left, remove the description text and escape any special characters if ((${#COMPREPLY[*]} == 1)); then __%[1]s_debug "COMPREPLY[0]: ${COMPREPLY[0]}" - comp="${COMPREPLY[0]%%%%$tab*}" - __%[1]s_debug "Removed description from single completion, which is now: ${comp}" - COMPREPLY[0]=$comp - else # Format the descriptions + COMPREPLY[0]=$(printf "%%q" "${COMPREPLY[0]%%%%$tab*}") + __%[1]s_debug "Removed description from single completion, which is now: ${COMPREPLY[0]}" + else + # Format the descriptions __%[1]s_format_comp_descriptions $longest fi } diff --git a/vendor/github.com/spf13/cobra/cobra.go b/vendor/github.com/spf13/cobra/cobra.go index e0b0947b04..d9cd2414e2 100644 --- a/vendor/github.com/spf13/cobra/cobra.go +++ b/vendor/github.com/spf13/cobra/cobra.go @@ -176,12 +176,16 @@ func rpad(s string, padding int) string { return fmt.Sprintf(formattedString, s) } -// tmpl executes the given template text on data, writing the result to w. -func tmpl(w io.Writer, text string, data interface{}) error { - t := template.New("top") - t.Funcs(templateFuncs) - template.Must(t.Parse(text)) - return t.Execute(w, data) +func tmpl(text string) *tmplFunc { + return &tmplFunc{ + tmpl: text, + fn: func(w io.Writer, data interface{}) error { + t := template.New("top") + t.Funcs(templateFuncs) + template.Must(t.Parse(text)) + return t.Execute(w, data) + }, + } } // ld compares two strings and returns the levenshtein distance between them. diff --git a/vendor/github.com/spf13/cobra/command.go b/vendor/github.com/spf13/cobra/command.go index 54748fc67e..dbb2c298ba 100644 --- a/vendor/github.com/spf13/cobra/command.go +++ b/vendor/github.com/spf13/cobra/command.go @@ -33,6 +33,9 @@ import ( const ( FlagSetByCobraAnnotation = "cobra_annotation_flag_set_by_cobra" CommandDisplayNameAnnotation = "cobra_annotation_command_display_name" + + helpFlagName = "help" + helpCommandName = "help" ) // FParseErrWhitelist configures Flag parse errors to be ignored @@ -80,11 +83,11 @@ type Command struct { Example string // ValidArgs is list of all valid non-flag arguments that are accepted in shell completions - ValidArgs []string + ValidArgs []Completion // ValidArgsFunction is an optional function that provides valid non-flag arguments for shell completion. // It is a dynamic version of using ValidArgs. // Only one of ValidArgs and ValidArgsFunction can be used for a command. - ValidArgsFunction func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) + ValidArgsFunction CompletionFunc // Expected arguments Args PositionalArgs @@ -168,12 +171,12 @@ type Command struct { // usageFunc is usage func defined by user. usageFunc func(*Command) error // usageTemplate is usage template defined by user. - usageTemplate string + usageTemplate *tmplFunc // flagErrorFunc is func defined by user and it's called when the parsing of // flags returns an error. flagErrorFunc func(*Command, error) error // helpTemplate is help template defined by user. - helpTemplate string + helpTemplate *tmplFunc // helpFunc is help func defined by user. helpFunc func(*Command, []string) // helpCommand is command with usage 'help'. If it's not defined by user, @@ -186,7 +189,7 @@ type Command struct { completionCommandGroupID string // versionTemplate is the version template defined by user. - versionTemplate string + versionTemplate *tmplFunc // errPrefix is the error message prefix defined by user. errPrefix string @@ -281,6 +284,7 @@ func (c *Command) SetArgs(a []string) { // SetOutput sets the destination for usage and error messages. // If output is nil, os.Stderr is used. +// // Deprecated: Use SetOut and/or SetErr instead func (c *Command) SetOutput(output io.Writer) { c.outWriter = output @@ -312,7 +316,11 @@ func (c *Command) SetUsageFunc(f func(*Command) error) { // SetUsageTemplate sets usage template. Can be defined by Application. func (c *Command) SetUsageTemplate(s string) { - c.usageTemplate = s + if s == "" { + c.usageTemplate = nil + return + } + c.usageTemplate = tmpl(s) } // SetFlagErrorFunc sets a function to generate an error when flag parsing @@ -348,12 +356,20 @@ func (c *Command) SetCompletionCommandGroupID(groupID string) { // SetHelpTemplate sets help template to be used. Application can use it to set custom template. func (c *Command) SetHelpTemplate(s string) { - c.helpTemplate = s + if s == "" { + c.helpTemplate = nil + return + } + c.helpTemplate = tmpl(s) } // SetVersionTemplate sets version template to be used. Application can use it to set custom template. func (c *Command) SetVersionTemplate(s string) { - c.versionTemplate = s + if s == "" { + c.versionTemplate = nil + return + } + c.versionTemplate = tmpl(s) } // SetErrPrefix sets error message prefix to be used. Application can use it to set custom prefix. @@ -434,7 +450,8 @@ func (c *Command) UsageFunc() (f func(*Command) error) { } return func(c *Command) error { c.mergePersistentFlags() - err := tmpl(c.OutOrStderr(), c.UsageTemplate(), c) + fn := c.getUsageTemplateFunc() + err := fn(c.OutOrStderr(), c) if err != nil { c.PrintErrln(err) } @@ -442,6 +459,19 @@ func (c *Command) UsageFunc() (f func(*Command) error) { } } +// getUsageTemplateFunc returns the usage template function for the command +// going up the command tree if necessary. +func (c *Command) getUsageTemplateFunc() func(w io.Writer, data interface{}) error { + if c.usageTemplate != nil { + return c.usageTemplate.fn + } + + if c.HasParent() { + return c.parent.getUsageTemplateFunc() + } + return defaultUsageFunc +} + // Usage puts out the usage for the command. // Used when a user provides invalid input. // Can be defined by user by overriding UsageFunc. @@ -460,15 +490,30 @@ func (c *Command) HelpFunc() func(*Command, []string) { } return func(c *Command, a []string) { c.mergePersistentFlags() + fn := c.getHelpTemplateFunc() // The help should be sent to stdout // See https://github.com/spf13/cobra/issues/1002 - err := tmpl(c.OutOrStdout(), c.HelpTemplate(), c) + err := fn(c.OutOrStdout(), c) if err != nil { c.PrintErrln(err) } } } +// getHelpTemplateFunc returns the help template function for the command +// going up the command tree if necessary. +func (c *Command) getHelpTemplateFunc() func(w io.Writer, data interface{}) error { + if c.helpTemplate != nil { + return c.helpTemplate.fn + } + + if c.HasParent() { + return c.parent.getHelpTemplateFunc() + } + + return defaultHelpFunc +} + // Help puts out the help for the command. // Used when a user calls help [command]. // Can be defined by user by overriding HelpFunc. @@ -543,71 +588,55 @@ func (c *Command) NamePadding() int { } // UsageTemplate returns usage template for the command. +// This function is kept for backwards-compatibility reasons. func (c *Command) UsageTemplate() string { - if c.usageTemplate != "" { - return c.usageTemplate + if c.usageTemplate != nil { + return c.usageTemplate.tmpl } if c.HasParent() { return c.parent.UsageTemplate() } - return `Usage:{{if .Runnable}} - {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}} - {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}} - -Aliases: - {{.NameAndAliases}}{{end}}{{if .HasExample}} - -Examples: -{{.Example}}{{end}}{{if .HasAvailableSubCommands}}{{$cmds := .Commands}}{{if eq (len .Groups) 0}} - -Available Commands:{{range $cmds}}{{if (or .IsAvailableCommand (eq .Name "help"))}} - {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{else}}{{range $group := .Groups}} - -{{.Title}}{{range $cmds}}{{if (and (eq .GroupID $group.ID) (or .IsAvailableCommand (eq .Name "help")))}} - {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if not .AllChildCommandsHaveGroup}} - -Additional Commands:{{range $cmds}}{{if (and (eq .GroupID "") (or .IsAvailableCommand (eq .Name "help")))}} - {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} - -Flags: -{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} - -Global Flags: -{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}} - -Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}} - {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}} - -Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}} -` + return defaultUsageTemplate } // HelpTemplate return help template for the command. +// This function is kept for backwards-compatibility reasons. func (c *Command) HelpTemplate() string { - if c.helpTemplate != "" { - return c.helpTemplate + if c.helpTemplate != nil { + return c.helpTemplate.tmpl } if c.HasParent() { return c.parent.HelpTemplate() } - return `{{with (or .Long .Short)}}{{. | trimTrailingWhitespaces}} - -{{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}` + return defaultHelpTemplate } // VersionTemplate return version template for the command. +// This function is kept for backwards-compatibility reasons. func (c *Command) VersionTemplate() string { - if c.versionTemplate != "" { - return c.versionTemplate + if c.versionTemplate != nil { + return c.versionTemplate.tmpl } if c.HasParent() { return c.parent.VersionTemplate() } - return `{{with .Name}}{{printf "%s " .}}{{end}}{{printf "version %s" .Version}} -` + return defaultVersionTemplate +} + +// getVersionTemplateFunc returns the version template function for the command +// going up the command tree if necessary. +func (c *Command) getVersionTemplateFunc() func(w io.Writer, data interface{}) error { + if c.versionTemplate != nil { + return c.versionTemplate.fn + } + + if c.HasParent() { + return c.parent.getVersionTemplateFunc() + } + return defaultVersionFunc } // ErrPrefix return error message prefix for the command @@ -894,7 +923,7 @@ func (c *Command) execute(a []string) (err error) { // If help is called, regardless of other flags, return we want help. // Also say we need help if the command isn't runnable. - helpVal, err := c.Flags().GetBool("help") + helpVal, err := c.Flags().GetBool(helpFlagName) if err != nil { // should be impossible to get here as we always declare a help // flag in InitDefaultHelpFlag() @@ -914,7 +943,8 @@ func (c *Command) execute(a []string) (err error) { return err } if versionVal { - err := tmpl(c.OutOrStdout(), c.VersionTemplate(), c) + fn := c.getVersionTemplateFunc() + err := fn(c.OutOrStdout(), c) if err != nil { c.Println(err) } @@ -1068,12 +1098,6 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { // initialize help at the last point to allow for user overriding c.InitDefaultHelpCmd() - // initialize completion at the last point to allow for user overriding - c.InitDefaultCompletionCmd() - - // Now that all commands have been created, let's make sure all groups - // are properly created also - c.checkCommandGroups() args := c.args @@ -1082,9 +1106,16 @@ func (c *Command) ExecuteC() (cmd *Command, err error) { args = os.Args[1:] } - // initialize the hidden command to be used for shell completion + // initialize the __complete command to be used for shell completion c.initCompleteCmd(args) + // initialize the default completion command + c.InitDefaultCompletionCmd(args...) + + // Now that all commands have been created, let's make sure all groups + // are properly created also + c.checkCommandGroups() + var flags []string if c.TraverseChildren { cmd, flags, err = c.Traverse(args) @@ -1187,16 +1218,16 @@ func (c *Command) checkCommandGroups() { // If c already has help flag, it will do nothing. func (c *Command) InitDefaultHelpFlag() { c.mergePersistentFlags() - if c.Flags().Lookup("help") == nil { + if c.Flags().Lookup(helpFlagName) == nil { usage := "help for " - name := c.displayName() + name := c.DisplayName() if name == "" { usage += "this command" } else { usage += name } - c.Flags().BoolP("help", "h", false, usage) - _ = c.Flags().SetAnnotation("help", FlagSetByCobraAnnotation, []string{"true"}) + c.Flags().BoolP(helpFlagName, "h", false, usage) + _ = c.Flags().SetAnnotation(helpFlagName, FlagSetByCobraAnnotation, []string{"true"}) } } @@ -1215,7 +1246,7 @@ func (c *Command) InitDefaultVersionFlag() { if c.Name() == "" { usage += "this command" } else { - usage += c.Name() + usage += c.DisplayName() } if c.Flags().ShorthandLookup("v") == nil { c.Flags().BoolP("version", "v", false, usage) @@ -1239,9 +1270,9 @@ func (c *Command) InitDefaultHelpCmd() { Use: "help [command]", Short: "Help about any command", Long: `Help provides help for any command in the application. -Simply type ` + c.displayName() + ` help [path to command] for full details.`, - ValidArgsFunction: func(c *Command, args []string, toComplete string) ([]string, ShellCompDirective) { - var completions []string +Simply type ` + c.DisplayName() + ` help [path to command] for full details.`, + ValidArgsFunction: func(c *Command, args []string, toComplete string) ([]Completion, ShellCompDirective) { + var completions []Completion cmd, _, e := c.Root().Find(args) if e != nil { return nil, ShellCompDirectiveNoFileComp @@ -1253,7 +1284,7 @@ Simply type ` + c.displayName() + ` help [path to command] for full details.`, for _, subCmd := range cmd.Commands() { if subCmd.IsAvailableCommand() || subCmd == cmd.helpCommand { if strings.HasPrefix(subCmd.Name(), toComplete) { - completions = append(completions, fmt.Sprintf("%s\t%s", subCmd.Name(), subCmd.Short)) + completions = append(completions, CompletionWithDesc(subCmd.Name(), subCmd.Short)) } } } @@ -1430,10 +1461,12 @@ func (c *Command) CommandPath() string { if c.HasParent() { return c.Parent().CommandPath() + " " + c.Name() } - return c.displayName() + return c.DisplayName() } -func (c *Command) displayName() string { +// DisplayName returns the name to display in help text. Returns command Name() +// If CommandDisplayNameAnnoation is not set +func (c *Command) DisplayName() string { if displayName, ok := c.Annotations[CommandDisplayNameAnnotation]; ok { return displayName } @@ -1443,7 +1476,7 @@ func (c *Command) displayName() string { // UseLine puts out the full usage for a given command (including parents). func (c *Command) UseLine() string { var useline string - use := strings.Replace(c.Use, c.Name(), c.displayName(), 1) + use := strings.Replace(c.Use, c.Name(), c.DisplayName(), 1) if c.HasParent() { useline = c.parent.CommandPath() + " " + use } else { @@ -1649,7 +1682,7 @@ func (c *Command) GlobalNormalizationFunc() func(f *flag.FlagSet, name string) f // to this command (local and persistent declared here and by all parents). func (c *Command) Flags() *flag.FlagSet { if c.flags == nil { - c.flags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError) + c.flags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError) if c.flagErrorBuf == nil { c.flagErrorBuf = new(bytes.Buffer) } @@ -1664,7 +1697,7 @@ func (c *Command) Flags() *flag.FlagSet { func (c *Command) LocalNonPersistentFlags() *flag.FlagSet { persistentFlags := c.PersistentFlags() - out := flag.NewFlagSet(c.displayName(), flag.ContinueOnError) + out := flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError) c.LocalFlags().VisitAll(func(f *flag.Flag) { if persistentFlags.Lookup(f.Name) == nil { out.AddFlag(f) @@ -1679,7 +1712,7 @@ func (c *Command) LocalFlags() *flag.FlagSet { c.mergePersistentFlags() if c.lflags == nil { - c.lflags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError) + c.lflags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError) if c.flagErrorBuf == nil { c.flagErrorBuf = new(bytes.Buffer) } @@ -1707,7 +1740,7 @@ func (c *Command) InheritedFlags() *flag.FlagSet { c.mergePersistentFlags() if c.iflags == nil { - c.iflags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError) + c.iflags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError) if c.flagErrorBuf == nil { c.flagErrorBuf = new(bytes.Buffer) } @@ -1736,7 +1769,7 @@ func (c *Command) NonInheritedFlags() *flag.FlagSet { // PersistentFlags returns the persistent FlagSet specifically set in the current command. func (c *Command) PersistentFlags() *flag.FlagSet { if c.pflags == nil { - c.pflags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError) + c.pflags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError) if c.flagErrorBuf == nil { c.flagErrorBuf = new(bytes.Buffer) } @@ -1749,9 +1782,9 @@ func (c *Command) PersistentFlags() *flag.FlagSet { func (c *Command) ResetFlags() { c.flagErrorBuf = new(bytes.Buffer) c.flagErrorBuf.Reset() - c.flags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError) + c.flags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError) c.flags.SetOutput(c.flagErrorBuf) - c.pflags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError) + c.pflags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError) c.pflags.SetOutput(c.flagErrorBuf) c.lflags = nil @@ -1868,7 +1901,7 @@ func (c *Command) mergePersistentFlags() { // If c.parentsPflags == nil, it makes new. func (c *Command) updateParentsPflags() { if c.parentsPflags == nil { - c.parentsPflags = flag.NewFlagSet(c.displayName(), flag.ContinueOnError) + c.parentsPflags = flag.NewFlagSet(c.DisplayName(), flag.ContinueOnError) c.parentsPflags.SetOutput(c.flagErrorBuf) c.parentsPflags.SortFlags = false } @@ -1894,3 +1927,141 @@ func commandNameMatches(s string, t string) bool { return s == t } + +// tmplFunc holds a template and a function that will execute said template. +type tmplFunc struct { + tmpl string + fn func(io.Writer, interface{}) error +} + +var defaultUsageTemplate = `Usage:{{if .Runnable}} + {{.UseLine}}{{end}}{{if .HasAvailableSubCommands}} + {{.CommandPath}} [command]{{end}}{{if gt (len .Aliases) 0}} + +Aliases: + {{.NameAndAliases}}{{end}}{{if .HasExample}} + +Examples: +{{.Example}}{{end}}{{if .HasAvailableSubCommands}}{{$cmds := .Commands}}{{if eq (len .Groups) 0}} + +Available Commands:{{range $cmds}}{{if (or .IsAvailableCommand (eq .Name "help"))}} + {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{else}}{{range $group := .Groups}} + +{{.Title}}{{range $cmds}}{{if (and (eq .GroupID $group.ID) (or .IsAvailableCommand (eq .Name "help")))}} + {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if not .AllChildCommandsHaveGroup}} + +Additional Commands:{{range $cmds}}{{if (and (eq .GroupID "") (or .IsAvailableCommand (eq .Name "help")))}} + {{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}} + +Flags: +{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasAvailableInheritedFlags}} + +Global Flags: +{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}{{end}}{{if .HasHelpSubCommands}} + +Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}} + {{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}} + +Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}} +` + +// defaultUsageFunc is equivalent to executing defaultUsageTemplate. The two should be changed in sync. +func defaultUsageFunc(w io.Writer, in interface{}) error { + c := in.(*Command) + fmt.Fprint(w, "Usage:") + if c.Runnable() { + fmt.Fprintf(w, "\n %s", c.UseLine()) + } + if c.HasAvailableSubCommands() { + fmt.Fprintf(w, "\n %s [command]", c.CommandPath()) + } + if len(c.Aliases) > 0 { + fmt.Fprintf(w, "\n\nAliases:\n") + fmt.Fprintf(w, " %s", c.NameAndAliases()) + } + if c.HasExample() { + fmt.Fprintf(w, "\n\nExamples:\n") + fmt.Fprintf(w, "%s", c.Example) + } + if c.HasAvailableSubCommands() { + cmds := c.Commands() + if len(c.Groups()) == 0 { + fmt.Fprintf(w, "\n\nAvailable Commands:") + for _, subcmd := range cmds { + if subcmd.IsAvailableCommand() || subcmd.Name() == helpCommandName { + fmt.Fprintf(w, "\n %s %s", rpad(subcmd.Name(), subcmd.NamePadding()), subcmd.Short) + } + } + } else { + for _, group := range c.Groups() { + fmt.Fprintf(w, "\n\n%s", group.Title) + for _, subcmd := range cmds { + if subcmd.GroupID == group.ID && (subcmd.IsAvailableCommand() || subcmd.Name() == helpCommandName) { + fmt.Fprintf(w, "\n %s %s", rpad(subcmd.Name(), subcmd.NamePadding()), subcmd.Short) + } + } + } + if !c.AllChildCommandsHaveGroup() { + fmt.Fprintf(w, "\n\nAdditional Commands:") + for _, subcmd := range cmds { + if subcmd.GroupID == "" && (subcmd.IsAvailableCommand() || subcmd.Name() == helpCommandName) { + fmt.Fprintf(w, "\n %s %s", rpad(subcmd.Name(), subcmd.NamePadding()), subcmd.Short) + } + } + } + } + } + if c.HasAvailableLocalFlags() { + fmt.Fprintf(w, "\n\nFlags:\n") + fmt.Fprint(w, trimRightSpace(c.LocalFlags().FlagUsages())) + } + if c.HasAvailableInheritedFlags() { + fmt.Fprintf(w, "\n\nGlobal Flags:\n") + fmt.Fprint(w, trimRightSpace(c.InheritedFlags().FlagUsages())) + } + if c.HasHelpSubCommands() { + fmt.Fprintf(w, "\n\nAdditional help topcis:") + for _, subcmd := range c.Commands() { + if subcmd.IsAdditionalHelpTopicCommand() { + fmt.Fprintf(w, "\n %s %s", rpad(subcmd.CommandPath(), subcmd.CommandPathPadding()), subcmd.Short) + } + } + } + if c.HasAvailableSubCommands() { + fmt.Fprintf(w, "\n\nUse \"%s [command] --help\" for more information about a command.", c.CommandPath()) + } + fmt.Fprintln(w) + return nil +} + +var defaultHelpTemplate = `{{with (or .Long .Short)}}{{. | trimTrailingWhitespaces}} + +{{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}` + +// defaultHelpFunc is equivalent to executing defaultHelpTemplate. The two should be changed in sync. +func defaultHelpFunc(w io.Writer, in interface{}) error { + c := in.(*Command) + usage := c.Long + if usage == "" { + usage = c.Short + } + usage = trimRightSpace(usage) + if usage != "" { + fmt.Fprintln(w, usage) + fmt.Fprintln(w) + } + if c.Runnable() || c.HasSubCommands() { + fmt.Fprint(w, c.UsageString()) + } + return nil +} + +var defaultVersionTemplate = `{{with .DisplayName}}{{printf "%s " .}}{{end}}{{printf "version %s" .Version}} +` + +// defaultVersionFunc is equivalent to executing defaultVersionTemplate. The two should be changed in sync. +func defaultVersionFunc(w io.Writer, in interface{}) error { + c := in.(*Command) + _, err := fmt.Fprintf(w, "%s version %s\n", c.DisplayName(), c.Version) + return err +} diff --git a/vendor/github.com/spf13/cobra/completions.go b/vendor/github.com/spf13/cobra/completions.go index c0c08b0572..a1752f7631 100644 --- a/vendor/github.com/spf13/cobra/completions.go +++ b/vendor/github.com/spf13/cobra/completions.go @@ -35,7 +35,7 @@ const ( ) // Global map of flag completion functions. Make sure to use flagCompletionMutex before you try to read and write from it. -var flagCompletionFunctions = map[*pflag.Flag]func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective){} +var flagCompletionFunctions = map[*pflag.Flag]CompletionFunc{} // lock for reading and writing from flagCompletionFunctions var flagCompletionMutex = &sync.RWMutex{} @@ -117,22 +117,50 @@ type CompletionOptions struct { HiddenDefaultCmd bool } +// Completion is a string that can be used for completions +// +// two formats are supported: +// - the completion choice +// - the completion choice with a textual description (separated by a TAB). +// +// [CompletionWithDesc] can be used to create a completion string with a textual description. +// +// Note: Go type alias is used to provide a more descriptive name in the documentation, but any string can be used. +type Completion = string + +// CompletionFunc is a function that provides completion results. +type CompletionFunc = func(cmd *Command, args []string, toComplete string) ([]Completion, ShellCompDirective) + +// CompletionWithDesc returns a [Completion] with a description by using the TAB delimited format. +func CompletionWithDesc(choice string, description string) Completion { + return choice + "\t" + description +} + // NoFileCompletions can be used to disable file completion for commands that should // not trigger file completions. -func NoFileCompletions(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { +// +// This method satisfies [CompletionFunc]. +// It can be used with [Command.RegisterFlagCompletionFunc] and for [Command.ValidArgsFunction]. +func NoFileCompletions(cmd *Command, args []string, toComplete string) ([]Completion, ShellCompDirective) { return nil, ShellCompDirectiveNoFileComp } // FixedCompletions can be used to create a completion function which always // returns the same results. -func FixedCompletions(choices []string, directive ShellCompDirective) func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { - return func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) { +// +// This method returns a function that satisfies [CompletionFunc] +// It can be used with [Command.RegisterFlagCompletionFunc] and for [Command.ValidArgsFunction]. +func FixedCompletions(choices []Completion, directive ShellCompDirective) CompletionFunc { + return func(cmd *Command, args []string, toComplete string) ([]Completion, ShellCompDirective) { return choices, directive } } // RegisterFlagCompletionFunc should be called to register a function to provide completion for a flag. -func (c *Command) RegisterFlagCompletionFunc(flagName string, f func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective)) error { +// +// You can use pre-defined completion functions such as [FixedCompletions] or [NoFileCompletions], +// or you can define your own. +func (c *Command) RegisterFlagCompletionFunc(flagName string, f CompletionFunc) error { flag := c.Flag(flagName) if flag == nil { return fmt.Errorf("RegisterFlagCompletionFunc: flag '%s' does not exist", flagName) @@ -148,7 +176,7 @@ func (c *Command) RegisterFlagCompletionFunc(flagName string, f func(cmd *Comman } // GetFlagCompletionFunc returns the completion function for the given flag of the command, if available. -func (c *Command) GetFlagCompletionFunc(flagName string) (func(*Command, []string, string) ([]string, ShellCompDirective), bool) { +func (c *Command) GetFlagCompletionFunc(flagName string) (CompletionFunc, bool) { flag := c.Flag(flagName) if flag == nil { return nil, false @@ -270,7 +298,15 @@ func (c *Command) initCompleteCmd(args []string) { } } -func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDirective, error) { +// SliceValue is a reduced version of [pflag.SliceValue]. It is used to detect +// flags that accept multiple values and therefore can provide completion +// multiple times. +type SliceValue interface { + // GetSlice returns the flag value list as an array of strings. + GetSlice() []string +} + +func (c *Command) getCompletions(args []string) (*Command, []Completion, ShellCompDirective, error) { // The last argument, which is not completely typed by the user, // should not be part of the list of arguments toComplete := args[len(args)-1] @@ -298,7 +334,7 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi } if err != nil { // Unable to find the real command. E.g., someInvalidCmd - return c, []string{}, ShellCompDirectiveDefault, fmt.Errorf("unable to find a command for arguments: %v", trimmedArgs) + return c, []Completion{}, ShellCompDirectiveDefault, fmt.Errorf("unable to find a command for arguments: %v", trimmedArgs) } finalCmd.ctx = c.ctx @@ -328,7 +364,7 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi // Parse the flags early so we can check if required flags are set if err = finalCmd.ParseFlags(finalArgs); err != nil { - return finalCmd, []string{}, ShellCompDirectiveDefault, fmt.Errorf("Error while parsing flags from args %v: %s", finalArgs, err.Error()) + return finalCmd, []Completion{}, ShellCompDirectiveDefault, fmt.Errorf("Error while parsing flags from args %v: %s", finalArgs, err.Error()) } realArgCount := finalCmd.Flags().NArg() @@ -340,14 +376,14 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi if flagErr != nil { // If error type is flagCompError and we don't want flagCompletion we should ignore the error if _, ok := flagErr.(*flagCompError); !(ok && !flagCompletion) { - return finalCmd, []string{}, ShellCompDirectiveDefault, flagErr + return finalCmd, []Completion{}, ShellCompDirectiveDefault, flagErr } } // Look for the --help or --version flags. If they are present, // there should be no further completions. if helpOrVersionFlagPresent(finalCmd) { - return finalCmd, []string{}, ShellCompDirectiveNoFileComp, nil + return finalCmd, []Completion{}, ShellCompDirectiveNoFileComp, nil } // We only remove the flags from the arguments if DisableFlagParsing is not set. @@ -376,11 +412,11 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi return finalCmd, subDir, ShellCompDirectiveFilterDirs, nil } // Directory completion - return finalCmd, []string{}, ShellCompDirectiveFilterDirs, nil + return finalCmd, []Completion{}, ShellCompDirectiveFilterDirs, nil } } - var completions []string + var completions []Completion var directive ShellCompDirective // Enforce flag groups before doing flag completions @@ -399,10 +435,14 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi // If we have not found any required flags, only then can we show regular flags if len(completions) == 0 { doCompleteFlags := func(flag *pflag.Flag) { - if !flag.Changed || + _, acceptsMultiple := flag.Value.(SliceValue) + acceptsMultiple = acceptsMultiple || strings.Contains(flag.Value.Type(), "Slice") || - strings.Contains(flag.Value.Type(), "Array") { - // If the flag is not already present, or if it can be specified multiple times (Array or Slice) + strings.Contains(flag.Value.Type(), "Array") || + strings.HasPrefix(flag.Value.Type(), "stringTo") + + if !flag.Changed || acceptsMultiple { + // If the flag is not already present, or if it can be specified multiple times (Array, Slice, or stringTo) // we suggest it as a completion completions = append(completions, getFlagNameCompletions(flag, toComplete)...) } @@ -462,7 +502,7 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi for _, subCmd := range finalCmd.Commands() { if subCmd.IsAvailableCommand() || subCmd == finalCmd.helpCommand { if strings.HasPrefix(subCmd.Name(), toComplete) { - completions = append(completions, fmt.Sprintf("%s\t%s", subCmd.Name(), subCmd.Short)) + completions = append(completions, CompletionWithDesc(subCmd.Name(), subCmd.Short)) } directive = ShellCompDirectiveNoFileComp } @@ -507,7 +547,7 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi } // Find the completion function for the flag or command - var completionFn func(cmd *Command, args []string, toComplete string) ([]string, ShellCompDirective) + var completionFn CompletionFunc if flag != nil && flagCompletion { flagCompletionMutex.RLock() completionFn = flagCompletionFunctions[flag] @@ -518,7 +558,7 @@ func (c *Command) getCompletions(args []string) (*Command, []string, ShellCompDi if completionFn != nil { // Go custom completion defined for this flag or command. // Call the registered completion function to get the completions. - var comps []string + var comps []Completion comps, directive = completionFn(finalCmd, finalArgs, toComplete) completions = append(completions, comps...) } @@ -531,23 +571,23 @@ func helpOrVersionFlagPresent(cmd *Command) bool { len(versionFlag.Annotations[FlagSetByCobraAnnotation]) > 0 && versionFlag.Changed { return true } - if helpFlag := cmd.Flags().Lookup("help"); helpFlag != nil && + if helpFlag := cmd.Flags().Lookup(helpFlagName); helpFlag != nil && len(helpFlag.Annotations[FlagSetByCobraAnnotation]) > 0 && helpFlag.Changed { return true } return false } -func getFlagNameCompletions(flag *pflag.Flag, toComplete string) []string { +func getFlagNameCompletions(flag *pflag.Flag, toComplete string) []Completion { if nonCompletableFlag(flag) { - return []string{} + return []Completion{} } - var completions []string + var completions []Completion flagName := "--" + flag.Name if strings.HasPrefix(flagName, toComplete) { // Flag without the = - completions = append(completions, fmt.Sprintf("%s\t%s", flagName, flag.Usage)) + completions = append(completions, CompletionWithDesc(flagName, flag.Usage)) // Why suggest both long forms: --flag and --flag= ? // This forces the user to *always* have to type either an = or a space after the flag name. @@ -559,20 +599,20 @@ func getFlagNameCompletions(flag *pflag.Flag, toComplete string) []string { // if len(flag.NoOptDefVal) == 0 { // // Flag requires a value, so it can be suffixed with = // flagName += "=" - // completions = append(completions, fmt.Sprintf("%s\t%s", flagName, flag.Usage)) + // completions = append(completions, CompletionWithDesc(flagName, flag.Usage)) // } } flagName = "-" + flag.Shorthand if len(flag.Shorthand) > 0 && strings.HasPrefix(flagName, toComplete) { - completions = append(completions, fmt.Sprintf("%s\t%s", flagName, flag.Usage)) + completions = append(completions, CompletionWithDesc(flagName, flag.Usage)) } return completions } -func completeRequireFlags(finalCmd *Command, toComplete string) []string { - var completions []string +func completeRequireFlags(finalCmd *Command, toComplete string) []Completion { + var completions []Completion doCompleteRequiredFlags := func(flag *pflag.Flag) { if _, present := flag.Annotations[BashCompOneRequiredFlag]; present { @@ -687,8 +727,8 @@ func checkIfFlagCompletion(finalCmd *Command, args []string, lastArg string) (*p // 1- the feature has been explicitly disabled by the program, // 2- c has no subcommands (to avoid creating one), // 3- c already has a 'completion' command provided by the program. -func (c *Command) InitDefaultCompletionCmd() { - if c.CompletionOptions.DisableDefaultCmd || !c.HasSubCommands() { +func (c *Command) InitDefaultCompletionCmd(args ...string) { + if c.CompletionOptions.DisableDefaultCmd { return } @@ -701,6 +741,16 @@ func (c *Command) InitDefaultCompletionCmd() { haveNoDescFlag := !c.CompletionOptions.DisableNoDescFlag && !c.CompletionOptions.DisableDescriptions + // Special case to know if there are sub-commands or not. + hasSubCommands := false + for _, cmd := range c.commands { + if cmd.Name() != ShellCompRequestCmd && cmd.Name() != helpCommandName { + // We found a real sub-command (not 'help' or '__complete') + hasSubCommands = true + break + } + } + completionCmd := &Command{ Use: compCmdName, Short: "Generate the autocompletion script for the specified shell", @@ -714,6 +764,22 @@ See each sub-command's help for details on how to use the generated script. } c.AddCommand(completionCmd) + if !hasSubCommands { + // If the 'completion' command will be the only sub-command, + // we only create it if it is actually being called. + // This avoids breaking programs that would suddenly find themselves with + // a subcommand, which would prevent them from accepting arguments. + // We also create the 'completion' command if the user is triggering + // shell completion for it (prog __complete completion '') + subCmd, cmdArgs, err := c.Find(args) + if err != nil || subCmd.Name() != compCmdName && + !(subCmd.Name() == ShellCompRequestCmd && len(cmdArgs) > 1 && cmdArgs[0] == compCmdName) { + // The completion command is not being called or being completed so we remove it. + c.RemoveCommand(completionCmd) + return + } + } + out := c.OutOrStdout() noDesc := c.CompletionOptions.DisableDescriptions shortDesc := "Generate the autocompletion script for %s" diff --git a/vendor/github.com/spf13/cobra/powershell_completions.go b/vendor/github.com/spf13/cobra/powershell_completions.go index a830b7bcad..746dcb92e3 100644 --- a/vendor/github.com/spf13/cobra/powershell_completions.go +++ b/vendor/github.com/spf13/cobra/powershell_completions.go @@ -162,7 +162,10 @@ filter __%[1]s_escapeStringWithSpecialChars { if (-Not $Description) { $Description = " " } - @{Name="$Name";Description="$Description"} + New-Object -TypeName PSCustomObject -Property @{ + Name = "$Name" + Description = "$Description" + } } @@ -240,7 +243,12 @@ filter __%[1]s_escapeStringWithSpecialChars { __%[1]s_debug "Only one completion left" # insert space after value - [System.Management.Automation.CompletionResult]::new($($comp.Name | __%[1]s_escapeStringWithSpecialChars) + $Space, "$($comp.Name)", 'ParameterValue', "$($comp.Description)") + $CompletionText = $($comp.Name | __%[1]s_escapeStringWithSpecialChars) + $Space + if ($ExecutionContext.SessionState.LanguageMode -eq "FullLanguage"){ + [System.Management.Automation.CompletionResult]::new($CompletionText, "$($comp.Name)", 'ParameterValue', "$($comp.Description)") + } else { + $CompletionText + } } else { # Add the proper number of spaces to align the descriptions @@ -255,7 +263,12 @@ filter __%[1]s_escapeStringWithSpecialChars { $Description = " ($($comp.Description))" } - [System.Management.Automation.CompletionResult]::new("$($comp.Name)$Description", "$($comp.Name)$Description", 'ParameterValue', "$($comp.Description)") + $CompletionText = "$($comp.Name)$Description" + if ($ExecutionContext.SessionState.LanguageMode -eq "FullLanguage"){ + [System.Management.Automation.CompletionResult]::new($CompletionText, "$($comp.Name)$Description", 'ParameterValue', "$($comp.Description)") + } else { + $CompletionText + } } } @@ -264,7 +277,13 @@ filter __%[1]s_escapeStringWithSpecialChars { # insert space after value # MenuComplete will automatically show the ToolTip of # the highlighted value at the bottom of the suggestions. - [System.Management.Automation.CompletionResult]::new($($comp.Name | __%[1]s_escapeStringWithSpecialChars) + $Space, "$($comp.Name)", 'ParameterValue', "$($comp.Description)") + + $CompletionText = $($comp.Name | __%[1]s_escapeStringWithSpecialChars) + $Space + if ($ExecutionContext.SessionState.LanguageMode -eq "FullLanguage"){ + [System.Management.Automation.CompletionResult]::new($CompletionText, "$($comp.Name)", 'ParameterValue', "$($comp.Description)") + } else { + $CompletionText + } } # TabCompleteNext and in case we get something unknown @@ -272,7 +291,13 @@ filter __%[1]s_escapeStringWithSpecialChars { # Like MenuComplete but we don't want to add a space here because # the user need to press space anyway to get the completion. # Description will not be shown because that's not possible with TabCompleteNext - [System.Management.Automation.CompletionResult]::new($($comp.Name | __%[1]s_escapeStringWithSpecialChars), "$($comp.Name)", 'ParameterValue', "$($comp.Description)") + + $CompletionText = $($comp.Name | __%[1]s_escapeStringWithSpecialChars) + if ($ExecutionContext.SessionState.LanguageMode -eq "FullLanguage"){ + [System.Management.Automation.CompletionResult]::new($CompletionText, "$($comp.Name)", 'ParameterValue', "$($comp.Description)") + } else { + $CompletionText + } } } diff --git a/vendor/github.com/spf13/viper/.golangci.yaml b/vendor/github.com/spf13/viper/.golangci.yaml index 1faeae42c7..474f41633c 100644 --- a/vendor/github.com/spf13/viper/.golangci.yaml +++ b/vendor/github.com/spf13/viper/.golangci.yaml @@ -17,8 +17,6 @@ linters-settings: disabled-checks: - importShadow - unnamedResult - golint: - min-confidence: 0 goimports: local-prefixes: github.com/spf13/viper @@ -30,7 +28,6 @@ linters: - dupl - durationcheck - exhaustive - - exportloopref - gci - gocritic - godot diff --git a/vendor/github.com/spf13/viper/README.md b/vendor/github.com/spf13/viper/README.md index 3fc7d84f16..769a5d900d 100644 --- a/vendor/github.com/spf13/viper/README.md +++ b/vendor/github.com/spf13/viper/README.md @@ -3,7 +3,8 @@ > > **Thank you!** -![Viper](.github/logo.png?raw=true) +![viper logo](https://github.com/user-attachments/assets/acae9193-2974-41f3-808d-2d433f5ada5e) + [![Mentioned in Awesome Go](https://awesome.re/mentioned-badge-flat.svg)](https://github.com/avelino/awesome-go#configuration) [![run on repl.it](https://repl.it/badge/github/sagikazarmark/Viper-example)](https://repl.it/@sagikazarmark/Viper-example#main.go) @@ -11,7 +12,7 @@ [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/spf13/viper/ci.yaml?branch=master&style=flat-square)](https://github.com/spf13/viper/actions?query=workflow%3ACI) [![Join the chat at https://gitter.im/spf13/viper](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Go Report Card](https://goreportcard.com/badge/github.com/spf13/viper?style=flat-square)](https://goreportcard.com/report/github.com/spf13/viper) -![Go Version](https://img.shields.io/badge/go%20version-%3E=1.20-61CFDD.svg?style=flat-square) +![Go Version](https://img.shields.io/badge/go%20version-%3E=1.21-61CFDD.svg?style=flat-square) [![PkgGoDev](https://pkg.go.dev/badge/mod/github.com/spf13/viper)](https://pkg.go.dev/mod/github.com/spf13/viper) **Go configuration with fangs!** @@ -802,7 +803,7 @@ if err != nil { } ``` -Viper uses [github.com/mitchellh/mapstructure](https://github.com/mitchellh/mapstructure) under the hood for unmarshaling values which uses `mapstructure` tags by default. +Viper uses [github.com/go-viper/mapstructure](https://github.com/go-viper/mapstructure) under the hood for unmarshaling values which uses `mapstructure` tags by default. ### Decoding custom formats @@ -836,13 +837,15 @@ func yamlStringSettings() string { ## Viper or Vipers? -Viper comes ready to use out of the box. There is no configuration or -initialization needed to begin using Viper. Since most applications will want -to use a single central repository for their configuration, the viper package -provides this. It is similar to a singleton. +Viper comes with a global instance (singleton) out of the box. + +Although it makes setting up configuration easy, +using it is generally discouraged as it makes testing harder and can lead to unexpected behavior. + +The best practice is to initialize a Viper instance and pass that around when necessary. -In all of the examples above, they demonstrate using viper in its singleton -style approach. +The global instance _MAY_ be deprecated in the future. +See [#1855](https://github.com/spf13/viper/issues/1855) for more details. ### Working with multiple vipers diff --git a/vendor/github.com/spf13/viper/UPDATES.md b/vendor/github.com/spf13/viper/UPDATES.md new file mode 100644 index 0000000000..ccf413ed7e --- /dev/null +++ b/vendor/github.com/spf13/viper/UPDATES.md @@ -0,0 +1,126 @@ +# Update Log + +**This document details any major updates required to use new features or improvements in Viper.** + +## v1.20.x + +### New file searching API + +Viper now includes a new file searching API that allows users to customize how Viper looks for config files. + +Viper accepts a custom [`Finder`](https://pkg.go.dev/github.com/spf13/viper#Finder) interface implementation: + +```go +// Finder looks for files and directories in an [afero.Fs] filesystem. +type Finder interface { + Find(fsys afero.Fs) ([]string, error) +} +``` + +It is supposed to return a list of paths to config files. + +The default implementation uses [github.com/sagikazarmark/locafero](https://github.com/sagikazarmark/locafero) under the hood. + +You can supply your own implementation using `WithFinder`: + +```go +v := viper.NewWithOptions( + viper.WithFinder(&MyFinder{}), +) +``` + +For more information, check out the [Finder examples](https://pkg.go.dev/github.com/spf13/viper#Finder) +and the [documentation](https://pkg.go.dev/github.com/sagikazarmark/locafero) for the locafero package. + +### New encoding API + +Viper now allows customizing the encoding layer by providing an API for encoding and decoding configuration data: + +```go +// Encoder encodes Viper's internal data structures into a byte representation. +// It's primarily used for encoding a map[string]any into a file format. +type Encoder interface { + Encode(v map[string]any) ([]byte, error) +} + +// Decoder decodes the contents of a byte slice into Viper's internal data structures. +// It's primarily used for decoding contents of a file into a map[string]any. +type Decoder interface { + Decode(b []byte, v map[string]any) error +} + +// Codec combines [Encoder] and [Decoder] interfaces. +type Codec interface { + Encoder + Decoder +} +``` + +By default, Viper includes the following codecs: + +- JSON +- TOML +- YAML +- Dotenv + +The rest of the codecs are moved to [github.com/go-viper/encoding](https://github.com/go-viper/encoding) + +Customizing the encoding layer is possible by providing a custom registry of codecs: + +- [Encoder](https://pkg.go.dev/github.com/spf13/viper#Encoder) -> [EncoderRegistry](https://pkg.go.dev/github.com/spf13/viper#EncoderRegistry) +- [Decoder](https://pkg.go.dev/github.com/spf13/viper#Decoder) -> [DecoderRegistry](https://pkg.go.dev/github.com/spf13/viper#DecoderRegistry) +- [Codec](https://pkg.go.dev/github.com/spf13/viper#Codec) -> [CodecRegistry](https://pkg.go.dev/github.com/spf13/viper#CodecRegistry) + +You can supply the registry of codecs to Viper using the appropriate `With*Registry` function: + +```go +codecRegistry := viper.NewCodecRegistry() + +codecRegistry.RegisterCodec("myformat", &MyCodec{}) + +v := viper.NewWithOptions( + viper.WithCodecRegistry(codecRegistry), +) +``` + +### BREAKING: HCL, Java properties, INI removed from core + +In order to reduce third-party dependencies, Viper dropped support for the following formats from the core: + +- HCL +- Java properties +- INI + +You can still use these formats though by importing them from [github.com/go-viper/encoding](https://github.com/go-viper/encoding): + +```go +import ( + "github.com/go-viper/encoding/hcl" + "github.com/go-viper/encoding/javaproperties" + "github.com/go-viper/encoding/ini" +) + +codecRegistry := viper.NewCodecRegistry() + +{ + codec := hcl.Codec{} + + codecRegistry.RegisterCodec("hcl", codec) + codecRegistry.RegisterCodec("tfvars", codec) + +} + +{ + codec := &javaproperties.Codec{} + + codecRegistry.RegisterCodec("properties", codec) + codecRegistry.RegisterCodec("props", codec) + codecRegistry.RegisterCodec("prop", codec) +} + +codecRegistry.RegisterCodec("ini", ini.Codec{}) + +v := viper.NewWithOptions( + viper.WithCodecRegistry(codecRegistry), +) +``` diff --git a/vendor/github.com/spf13/viper/encoding.go b/vendor/github.com/spf13/viper/encoding.go new file mode 100644 index 0000000000..a7da55860e --- /dev/null +++ b/vendor/github.com/spf13/viper/encoding.go @@ -0,0 +1,181 @@ +package viper + +import ( + "errors" + "strings" + "sync" + + "github.com/spf13/viper/internal/encoding/dotenv" + "github.com/spf13/viper/internal/encoding/json" + "github.com/spf13/viper/internal/encoding/toml" + "github.com/spf13/viper/internal/encoding/yaml" +) + +// Encoder encodes Viper's internal data structures into a byte representation. +// It's primarily used for encoding a map[string]any into a file format. +type Encoder interface { + Encode(v map[string]any) ([]byte, error) +} + +// Decoder decodes the contents of a byte slice into Viper's internal data structures. +// It's primarily used for decoding contents of a file into a map[string]any. +type Decoder interface { + Decode(b []byte, v map[string]any) error +} + +// Codec combines [Encoder] and [Decoder] interfaces. +type Codec interface { + Encoder + Decoder +} + +// TODO: consider adding specific errors for not found scenarios + +// EncoderRegistry returns an [Encoder] for a given format. +// +// Format is case-insensitive. +// +// [EncoderRegistry] returns an error if no [Encoder] is registered for the format. +type EncoderRegistry interface { + Encoder(format string) (Encoder, error) +} + +// DecoderRegistry returns an [Decoder] for a given format. +// +// Format is case-insensitive. +// +// [DecoderRegistry] returns an error if no [Decoder] is registered for the format. +type DecoderRegistry interface { + Decoder(format string) (Decoder, error) +} + +// [CodecRegistry] combines [EncoderRegistry] and [DecoderRegistry] interfaces. +type CodecRegistry interface { + EncoderRegistry + DecoderRegistry +} + +// WithEncoderRegistry sets a custom [EncoderRegistry]. +func WithEncoderRegistry(r EncoderRegistry) Option { + return optionFunc(func(v *Viper) { + if r == nil { + return + } + + v.encoderRegistry = r + }) +} + +// WithDecoderRegistry sets a custom [DecoderRegistry]. +func WithDecoderRegistry(r DecoderRegistry) Option { + return optionFunc(func(v *Viper) { + if r == nil { + return + } + + v.decoderRegistry = r + }) +} + +// WithCodecRegistry sets a custom [EncoderRegistry] and [DecoderRegistry]. +func WithCodecRegistry(r CodecRegistry) Option { + return optionFunc(func(v *Viper) { + if r == nil { + return + } + + v.encoderRegistry = r + v.decoderRegistry = r + }) +} + +// DefaultCodecRegistry is a simple implementation of [CodecRegistry] that allows registering custom [Codec]s. +type DefaultCodecRegistry struct { + codecs map[string]Codec + + mu sync.RWMutex + once sync.Once +} + +// NewCodecRegistry returns a new [CodecRegistry], ready to accept custom [Codec]s. +func NewCodecRegistry() *DefaultCodecRegistry { + r := &DefaultCodecRegistry{} + + r.init() + + return r +} + +func (r *DefaultCodecRegistry) init() { + r.once.Do(func() { + r.codecs = map[string]Codec{} + }) +} + +// RegisterCodec registers a custom [Codec]. +// +// Format is case-insensitive. +func (r *DefaultCodecRegistry) RegisterCodec(format string, codec Codec) error { + r.init() + + r.mu.Lock() + defer r.mu.Unlock() + + r.codecs[strings.ToLower(format)] = codec + + return nil +} + +// Encoder implements the [EncoderRegistry] interface. +// +// Format is case-insensitive. +func (r *DefaultCodecRegistry) Encoder(format string) (Encoder, error) { + encoder, ok := r.codec(format) + if !ok { + return nil, errors.New("encoder not found for this format") + } + + return encoder, nil +} + +// Decoder implements the [DecoderRegistry] interface. +// +// Format is case-insensitive. +func (r *DefaultCodecRegistry) Decoder(format string) (Decoder, error) { + decoder, ok := r.codec(format) + if !ok { + return nil, errors.New("decoder not found for this format") + } + + return decoder, nil +} + +func (r *DefaultCodecRegistry) codec(format string) (Codec, bool) { + r.mu.Lock() + defer r.mu.Unlock() + + format = strings.ToLower(format) + + if r.codecs != nil { + codec, ok := r.codecs[format] + if ok { + return codec, true + } + } + + switch format { + case "yaml", "yml": + return yaml.Codec{}, true + + case "json": + return json.Codec{}, true + + case "toml": + return toml.Codec{}, true + + case "dotenv", "env": + return &dotenv.Codec{}, true + } + + return nil, false +} diff --git a/vendor/github.com/spf13/viper/experimental.go b/vendor/github.com/spf13/viper/experimental.go new file mode 100644 index 0000000000..6e19e8a100 --- /dev/null +++ b/vendor/github.com/spf13/viper/experimental.go @@ -0,0 +1,8 @@ +package viper + +// ExperimentalBindStruct tells Viper to use the new bind struct feature. +func ExperimentalBindStruct() Option { + return optionFunc(func(v *Viper) { + v.experimentalBindStruct = true + }) +} diff --git a/vendor/github.com/spf13/viper/file.go b/vendor/github.com/spf13/viper/file.go index a54fe5a7a8..50a40581d0 100644 --- a/vendor/github.com/spf13/viper/file.go +++ b/vendor/github.com/spf13/viper/file.go @@ -1,5 +1,3 @@ -//go:build !finder - package viper import ( @@ -7,12 +5,62 @@ import ( "os" "path/filepath" + "github.com/sagikazarmark/locafero" "github.com/spf13/afero" ) +// ExperimentalFinder tells Viper to use the new Finder interface for finding configuration files. +func ExperimentalFinder() Option { + return optionFunc(func(v *Viper) { + v.experimentalFinder = true + }) +} + +// Search for a config file. +func (v *Viper) findConfigFile() (string, error) { + finder := v.finder + + if finder == nil && v.experimentalFinder { + var names []string + + if v.configType != "" { + names = locafero.NameWithOptionalExtensions(v.configName, SupportedExts...) + } else { + names = locafero.NameWithExtensions(v.configName, SupportedExts...) + } + + finder = locafero.Finder{ + Paths: v.configPaths, + Names: names, + Type: locafero.FileTypeFile, + } + } + + if finder != nil { + return v.findConfigFileWithFinder(finder) + } + + return v.findConfigFileOld() +} + +func (v *Viper) findConfigFileWithFinder(finder Finder) (string, error) { + results, err := finder.Find(v.fs) + if err != nil { + return "", err + } + + if len(results) == 0 { + return "", ConfigFileNotFoundError{v.configName, fmt.Sprintf("%s", v.configPaths)} + } + + // We call clean on the final result to ensure that the path is in its canonical form. + // This is mostly for consistent path handling and to make sure tests pass. + return results[0], nil +} + // Search all configPaths for any config file. // Returns the first path that exists (and is a config file). -func (v *Viper) findConfigFile() (string, error) { +func (v *Viper) findConfigFileOld() (string, error) { v.logger.Info("searching for config in paths", "paths", v.configPaths) for _, cp := range v.configPaths { diff --git a/vendor/github.com/spf13/viper/file_finder.go b/vendor/github.com/spf13/viper/file_finder.go deleted file mode 100644 index d96a1bd223..0000000000 --- a/vendor/github.com/spf13/viper/file_finder.go +++ /dev/null @@ -1,38 +0,0 @@ -//go:build finder - -package viper - -import ( - "fmt" - - "github.com/sagikazarmark/locafero" -) - -// Search all configPaths for any config file. -// Returns the first path that exists (and is a config file). -func (v *Viper) findConfigFile() (string, error) { - var names []string - - if v.configType != "" { - names = locafero.NameWithOptionalExtensions(v.configName, SupportedExts...) - } else { - names = locafero.NameWithExtensions(v.configName, SupportedExts...) - } - - finder := locafero.Finder{ - Paths: v.configPaths, - Names: names, - Type: locafero.FileTypeFile, - } - - results, err := finder.Find(v.fs) - if err != nil { - return "", err - } - - if len(results) == 0 { - return "", ConfigFileNotFoundError{v.configName, fmt.Sprintf("%s", v.configPaths)} - } - - return results[0], nil -} diff --git a/vendor/github.com/spf13/viper/finder.go b/vendor/github.com/spf13/viper/finder.go new file mode 100644 index 0000000000..9b203ea69a --- /dev/null +++ b/vendor/github.com/spf13/viper/finder.go @@ -0,0 +1,55 @@ +package viper + +import ( + "errors" + + "github.com/spf13/afero" +) + +// WithFinder sets a custom [Finder]. +func WithFinder(f Finder) Option { + return optionFunc(func(v *Viper) { + if f == nil { + return + } + + v.finder = f + }) +} + +// Finder looks for files and directories in an [afero.Fs] filesystem. +type Finder interface { + Find(fsys afero.Fs) ([]string, error) +} + +// Finders combines multiple finders into one. +func Finders(finders ...Finder) Finder { + return &combinedFinder{finders: finders} +} + +// combinedFinder is a Finder that combines multiple finders. +type combinedFinder struct { + finders []Finder +} + +// Find implements the [Finder] interface. +func (c *combinedFinder) Find(fsys afero.Fs) ([]string, error) { + var results []string + var errs []error + + for _, finder := range c.finders { + if finder == nil { + continue + } + + r, err := finder.Find(fsys) + if err != nil { + errs = append(errs, err) + continue + } + + results = append(results, r...) + } + + return results, errors.Join(errs...) +} diff --git a/vendor/github.com/spf13/viper/flake.lock b/vendor/github.com/spf13/viper/flake.lock index 3840614fa2..d76dfbddd1 100644 --- a/vendor/github.com/spf13/viper/flake.lock +++ b/vendor/github.com/spf13/viper/flake.lock @@ -1,22 +1,84 @@ { "nodes": { + "cachix": { + "inputs": { + "devenv": "devenv_2", + "flake-compat": [ + "devenv", + "flake-compat" + ], + "nixpkgs": [ + "devenv", + "nixpkgs" + ], + "pre-commit-hooks": [ + "devenv", + "pre-commit-hooks" + ] + }, + "locked": { + "lastModified": 1712055811, + "narHash": "sha256-7FcfMm5A/f02yyzuavJe06zLa9hcMHsagE28ADcmQvk=", + "owner": "cachix", + "repo": "cachix", + "rev": "02e38da89851ec7fec3356a5c04bc8349cae0e30", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "cachix", + "type": "github" + } + }, "devenv": { "inputs": { - "flake-compat": "flake-compat", + "cachix": "cachix", + "flake-compat": "flake-compat_2", + "nix": "nix_2", + "nixpkgs": "nixpkgs_2", + "pre-commit-hooks": "pre-commit-hooks" + }, + "locked": { + "lastModified": 1724763216, + "narHash": "sha256-oW2bwCrJpIzibCNK6zfIDaIQw765yMAuMSG2gyZfGv0=", + "owner": "cachix", + "repo": "devenv", + "rev": "1e4ef61205b9aa20fe04bf1c468b6a316281c4f1", + "type": "github" + }, + "original": { + "owner": "cachix", + "repo": "devenv", + "type": "github" + } + }, + "devenv_2": { + "inputs": { + "flake-compat": [ + "devenv", + "cachix", + "flake-compat" + ], "nix": "nix", "nixpkgs": "nixpkgs", - "pre-commit-hooks": "pre-commit-hooks" + "poetry2nix": "poetry2nix", + "pre-commit-hooks": [ + "devenv", + "cachix", + "pre-commit-hooks" + ] }, "locked": { - "lastModified": 1707817777, - "narHash": "sha256-vHyIs1OULQ3/91wD6xOiuayfI71JXALGA5KLnDKAcy0=", + "lastModified": 1708704632, + "narHash": "sha256-w+dOIW60FKMaHI1q5714CSibk99JfYxm0CzTinYWr+Q=", "owner": "cachix", "repo": "devenv", - "rev": "5a30b9e5ac7c6167e61b1f4193d5130bb9f8defa", + "rev": "2ee4450b0f4b95a1b90f2eb5ffea98b90e48c196", "type": "github" }, "original": { "owner": "cachix", + "ref": "python-rewrite", "repo": "devenv", "type": "github" } @@ -37,16 +99,32 @@ "type": "github" } }, + "flake-compat_2": { + "flake": false, + "locked": { + "lastModified": 1696426674, + "narHash": "sha256-kvjfFW7WAETZlt09AgDn1MrtKzP7t90Vf7vypd3OL1U=", + "owner": "edolstra", + "repo": "flake-compat", + "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33", + "type": "github" + }, + "original": { + "owner": "edolstra", + "repo": "flake-compat", + "type": "github" + } + }, "flake-parts": { "inputs": { "nixpkgs-lib": "nixpkgs-lib" }, "locked": { - "lastModified": 1706830856, - "narHash": "sha256-a0NYyp+h9hlb7ddVz4LUn1vT/PLwqfrWYcHMvFB1xYg=", + "lastModified": 1722555600, + "narHash": "sha256-XOQkdLafnb/p9ij77byFQjDf5m5QYl9b2REiVClC+x4=", "owner": "hercules-ci", "repo": "flake-parts", - "rev": "b253292d9c0a5ead9bc98c4e9a26c6312e27d69f", + "rev": "8471fe90ad337a8074e957b69ca4d0089218391d", "type": "github" }, "original": { @@ -60,11 +138,29 @@ "systems": "systems" }, "locked": { - "lastModified": 1685518550, - "narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=", + "lastModified": 1689068808, + "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "inputs": { + "systems": "systems_2" + }, + "locked": { + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", "owner": "numtide", "repo": "flake-utils", - "rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", "type": "github" }, "original": { @@ -82,11 +178,11 @@ ] }, "locked": { - "lastModified": 1660459072, - "narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=", + "lastModified": 1709087332, + "narHash": "sha256-HG2cCnktfHsKV0s4XW83gU3F57gaTljL9KNSuG6bnQs=", "owner": "hercules-ci", "repo": "gitignore.nix", - "rev": "a20de23b925fd8264fd7fad6454652e142fd7f73", + "rev": "637db329424fd7e46cf4185293b9cc8c88c95394", "type": "github" }, "original": { @@ -95,53 +191,90 @@ "type": "github" } }, - "lowdown-src": { - "flake": false, + "nix": { + "inputs": { + "flake-compat": "flake-compat", + "nixpkgs": [ + "devenv", + "cachix", + "devenv", + "nixpkgs" + ], + "nixpkgs-regression": "nixpkgs-regression" + }, "locked": { - "lastModified": 1633514407, - "narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=", - "owner": "kristapsdz", - "repo": "lowdown", - "rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8", + "lastModified": 1712911606, + "narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=", + "owner": "domenkozar", + "repo": "nix", + "rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12", "type": "github" }, "original": { - "owner": "kristapsdz", - "repo": "lowdown", + "owner": "domenkozar", + "ref": "devenv-2.21", + "repo": "nix", "type": "github" } }, - "nix": { + "nix-github-actions": { + "inputs": { + "nixpkgs": [ + "devenv", + "cachix", + "devenv", + "poetry2nix", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1688870561, + "narHash": "sha256-4UYkifnPEw1nAzqqPOTL2MvWtm3sNGw1UTYTalkTcGY=", + "owner": "nix-community", + "repo": "nix-github-actions", + "rev": "165b1650b753316aa7f1787f3005a8d2da0f5301", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "nix-github-actions", + "type": "github" + } + }, + "nix_2": { "inputs": { - "lowdown-src": "lowdown-src", + "flake-compat": [ + "devenv", + "flake-compat" + ], "nixpkgs": [ "devenv", "nixpkgs" ], - "nixpkgs-regression": "nixpkgs-regression" + "nixpkgs-regression": "nixpkgs-regression_2" }, "locked": { - "lastModified": 1676545802, - "narHash": "sha256-EK4rZ+Hd5hsvXnzSzk2ikhStJnD63odF7SzsQ8CuSPU=", + "lastModified": 1712911606, + "narHash": "sha256-BGvBhepCufsjcUkXnEEXhEVjwdJAwPglCC2+bInc794=", "owner": "domenkozar", "repo": "nix", - "rev": "7c91803598ffbcfe4a55c44ac6d49b2cf07a527f", + "rev": "b24a9318ea3f3600c1e24b4a00691ee912d4de12", "type": "github" }, "original": { "owner": "domenkozar", - "ref": "relaxed-flakes", + "ref": "devenv-2.21", "repo": "nix", "type": "github" } }, "nixpkgs": { "locked": { - "lastModified": 1678875422, - "narHash": "sha256-T3o6NcQPwXjxJMn2shz86Chch4ljXgZn746c2caGxd8=", + "lastModified": 1692808169, + "narHash": "sha256-x9Opq06rIiwdwGeK2Ykj69dNc2IvUH1fY55Wm7atwrE=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "126f49a01de5b7e35a43fd43f891ecf6d3a51459", + "rev": "9201b5ff357e781bf014d0330d18555695df7ba8", "type": "github" }, "original": { @@ -153,23 +286,33 @@ }, "nixpkgs-lib": { "locked": { - "dir": "lib", - "lastModified": 1706550542, - "narHash": "sha256-UcsnCG6wx++23yeER4Hg18CXWbgNpqNXcHIo5/1Y+hc=", + "lastModified": 1722555339, + "narHash": "sha256-uFf2QeW7eAHlYXuDktm9c25OxOyCoUOQmh5SZ9amE5Q=", + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/a5d394176e64ab29c852d03346c1fc9b0b7d33eb.tar.gz" + }, + "original": { + "type": "tarball", + "url": "https://github.com/NixOS/nixpkgs/archive/a5d394176e64ab29c852d03346c1fc9b0b7d33eb.tar.gz" + } + }, + "nixpkgs-regression": { + "locked": { + "lastModified": 1643052045, + "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "97b17f32362e475016f942bbdfda4a4a72a8a652", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", "type": "github" }, "original": { - "dir": "lib", "owner": "NixOS", - "ref": "nixos-unstable", "repo": "nixpkgs", + "rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2", "type": "github" } }, - "nixpkgs-regression": { + "nixpkgs-regression_2": { "locked": { "lastModified": 1643052045, "narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=", @@ -187,27 +330,43 @@ }, "nixpkgs-stable": { "locked": { - "lastModified": 1685801374, - "narHash": "sha256-otaSUoFEMM+LjBI1XL/xGB5ao6IwnZOXc47qhIgJe8U=", + "lastModified": 1710695816, + "narHash": "sha256-3Eh7fhEID17pv9ZxrPwCLfqXnYP006RKzSs0JptsN84=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "c37ca420157f4abc31e26f436c1145f8951ff373", + "rev": "614b4613980a522ba49f0d194531beddbb7220d3", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-23.05", + "ref": "nixos-23.11", "repo": "nixpkgs", "type": "github" } }, "nixpkgs_2": { "locked": { - "lastModified": 1707939175, - "narHash": "sha256-D1xan0lgxbmXDyzVqXTiSYHLmAMrMRdD+alKzEO/p3w=", + "lastModified": 1713361204, + "narHash": "sha256-TA6EDunWTkc5FvDCqU3W2T3SFn0gRZqh6D/hJnM02MM=", + "owner": "cachix", + "repo": "devenv-nixpkgs", + "rev": "285676e87ad9f0ca23d8714a6ab61e7e027020c6", + "type": "github" + }, + "original": { + "owner": "cachix", + "ref": "rolling", + "repo": "devenv-nixpkgs", + "type": "github" + } + }, + "nixpkgs_3": { + "locked": { + "lastModified": 1724748588, + "narHash": "sha256-NlpGA4+AIf1dKNq76ps90rxowlFXUsV9x7vK/mN37JM=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "f7e8132daca31b1e3859ac0fb49741754375ac3d", + "rev": "a6292e34000dc93d43bccf78338770c1c5ec8a99", "type": "github" }, "original": { @@ -217,13 +376,38 @@ "type": "github" } }, + "poetry2nix": { + "inputs": { + "flake-utils": "flake-utils", + "nix-github-actions": "nix-github-actions", + "nixpkgs": [ + "devenv", + "cachix", + "devenv", + "nixpkgs" + ] + }, + "locked": { + "lastModified": 1692876271, + "narHash": "sha256-IXfZEkI0Mal5y1jr6IRWMqK8GW2/f28xJenZIPQqkY0=", + "owner": "nix-community", + "repo": "poetry2nix", + "rev": "d5006be9c2c2417dafb2e2e5034d83fabd207ee3", + "type": "github" + }, + "original": { + "owner": "nix-community", + "repo": "poetry2nix", + "type": "github" + } + }, "pre-commit-hooks": { "inputs": { "flake-compat": [ "devenv", "flake-compat" ], - "flake-utils": "flake-utils", + "flake-utils": "flake-utils_2", "gitignore": "gitignore", "nixpkgs": [ "devenv", @@ -232,11 +416,11 @@ "nixpkgs-stable": "nixpkgs-stable" }, "locked": { - "lastModified": 1704725188, - "narHash": "sha256-qq8NbkhRZF1vVYQFt1s8Mbgo8knj+83+QlL5LBnYGpI=", + "lastModified": 1713775815, + "narHash": "sha256-Wu9cdYTnGQQwtT20QQMg7jzkANKQjwBD9iccfGKkfls=", "owner": "cachix", "repo": "pre-commit-hooks.nix", - "rev": "ea96f0c05924341c551a797aaba8126334c505d2", + "rev": "2ac4dcbf55ed43f3be0bae15e181f08a57af24a4", "type": "github" }, "original": { @@ -249,7 +433,7 @@ "inputs": { "devenv": "devenv", "flake-parts": "flake-parts", - "nixpkgs": "nixpkgs_2" + "nixpkgs": "nixpkgs_3" } }, "systems": { @@ -266,6 +450,21 @@ "repo": "default", "type": "github" } + }, + "systems_2": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } } }, "root": "root", diff --git a/vendor/github.com/spf13/viper/flake.nix b/vendor/github.com/spf13/viper/flake.nix index 0230668cff..52ad7d5814 100644 --- a/vendor/github.com/spf13/viper/flake.nix +++ b/vendor/github.com/spf13/viper/flake.nix @@ -20,7 +20,7 @@ default = { languages = { go.enable = true; - go.package = pkgs.go_1_22; + go.package = pkgs.go_1_23; }; pre-commit.hooks = { diff --git a/vendor/github.com/spf13/viper/internal/encoding/decoder.go b/vendor/github.com/spf13/viper/internal/encoding/decoder.go deleted file mode 100644 index 8a7b1dbc91..0000000000 --- a/vendor/github.com/spf13/viper/internal/encoding/decoder.go +++ /dev/null @@ -1,61 +0,0 @@ -package encoding - -import ( - "sync" -) - -// Decoder decodes the contents of b into v. -// It's primarily used for decoding contents of a file into a map[string]any. -type Decoder interface { - Decode(b []byte, v map[string]any) error -} - -const ( - // ErrDecoderNotFound is returned when there is no decoder registered for a format. - ErrDecoderNotFound = encodingError("decoder not found for this format") - - // ErrDecoderFormatAlreadyRegistered is returned when an decoder is already registered for a format. - ErrDecoderFormatAlreadyRegistered = encodingError("decoder already registered for this format") -) - -// DecoderRegistry can choose an appropriate Decoder based on the provided format. -type DecoderRegistry struct { - decoders map[string]Decoder - - mu sync.RWMutex -} - -// NewDecoderRegistry returns a new, initialized DecoderRegistry. -func NewDecoderRegistry() *DecoderRegistry { - return &DecoderRegistry{ - decoders: make(map[string]Decoder), - } -} - -// RegisterDecoder registers a Decoder for a format. -// Registering a Decoder for an already existing format is not supported. -func (e *DecoderRegistry) RegisterDecoder(format string, enc Decoder) error { - e.mu.Lock() - defer e.mu.Unlock() - - if _, ok := e.decoders[format]; ok { - return ErrDecoderFormatAlreadyRegistered - } - - e.decoders[format] = enc - - return nil -} - -// Decode calls the underlying Decoder based on the format. -func (e *DecoderRegistry) Decode(format string, b []byte, v map[string]any) error { - e.mu.RLock() - decoder, ok := e.decoders[format] - e.mu.RUnlock() - - if !ok { - return ErrDecoderNotFound - } - - return decoder.Decode(b, v) -} diff --git a/vendor/github.com/spf13/viper/internal/encoding/encoder.go b/vendor/github.com/spf13/viper/internal/encoding/encoder.go deleted file mode 100644 index 659585962c..0000000000 --- a/vendor/github.com/spf13/viper/internal/encoding/encoder.go +++ /dev/null @@ -1,60 +0,0 @@ -package encoding - -import ( - "sync" -) - -// Encoder encodes the contents of v into a byte representation. -// It's primarily used for encoding a map[string]any into a file format. -type Encoder interface { - Encode(v map[string]any) ([]byte, error) -} - -const ( - // ErrEncoderNotFound is returned when there is no encoder registered for a format. - ErrEncoderNotFound = encodingError("encoder not found for this format") - - // ErrEncoderFormatAlreadyRegistered is returned when an encoder is already registered for a format. - ErrEncoderFormatAlreadyRegistered = encodingError("encoder already registered for this format") -) - -// EncoderRegistry can choose an appropriate Encoder based on the provided format. -type EncoderRegistry struct { - encoders map[string]Encoder - - mu sync.RWMutex -} - -// NewEncoderRegistry returns a new, initialized EncoderRegistry. -func NewEncoderRegistry() *EncoderRegistry { - return &EncoderRegistry{ - encoders: make(map[string]Encoder), - } -} - -// RegisterEncoder registers an Encoder for a format. -// Registering a Encoder for an already existing format is not supported. -func (e *EncoderRegistry) RegisterEncoder(format string, enc Encoder) error { - e.mu.Lock() - defer e.mu.Unlock() - - if _, ok := e.encoders[format]; ok { - return ErrEncoderFormatAlreadyRegistered - } - - e.encoders[format] = enc - - return nil -} - -func (e *EncoderRegistry) Encode(format string, v map[string]any) ([]byte, error) { - e.mu.RLock() - encoder, ok := e.encoders[format] - e.mu.RUnlock() - - if !ok { - return nil, ErrEncoderNotFound - } - - return encoder.Encode(v) -} diff --git a/vendor/github.com/spf13/viper/internal/encoding/error.go b/vendor/github.com/spf13/viper/internal/encoding/error.go deleted file mode 100644 index e4cde02d7b..0000000000 --- a/vendor/github.com/spf13/viper/internal/encoding/error.go +++ /dev/null @@ -1,7 +0,0 @@ -package encoding - -type encodingError string - -func (e encodingError) Error() string { - return string(e) -} diff --git a/vendor/github.com/spf13/viper/internal/encoding/hcl/codec.go b/vendor/github.com/spf13/viper/internal/encoding/hcl/codec.go deleted file mode 100644 index d7fa8a1b7a..0000000000 --- a/vendor/github.com/spf13/viper/internal/encoding/hcl/codec.go +++ /dev/null @@ -1,40 +0,0 @@ -package hcl - -import ( - "bytes" - "encoding/json" - - "github.com/hashicorp/hcl" - "github.com/hashicorp/hcl/hcl/printer" -) - -// Codec implements the encoding.Encoder and encoding.Decoder interfaces for HCL encoding. -// TODO: add printer config to the codec? -type Codec struct{} - -func (Codec) Encode(v map[string]any) ([]byte, error) { - b, err := json.Marshal(v) - if err != nil { - return nil, err - } - - // TODO: use printer.Format? Is the trailing newline an issue? - - ast, err := hcl.Parse(string(b)) - if err != nil { - return nil, err - } - - var buf bytes.Buffer - - err = printer.Fprint(&buf, ast.Node) - if err != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -func (Codec) Decode(b []byte, v map[string]any) error { - return hcl.Unmarshal(b, &v) -} diff --git a/vendor/github.com/spf13/viper/internal/encoding/ini/codec.go b/vendor/github.com/spf13/viper/internal/encoding/ini/codec.go deleted file mode 100644 index d91cf59d2b..0000000000 --- a/vendor/github.com/spf13/viper/internal/encoding/ini/codec.go +++ /dev/null @@ -1,99 +0,0 @@ -package ini - -import ( - "bytes" - "sort" - "strings" - - "github.com/spf13/cast" - "gopkg.in/ini.v1" -) - -// LoadOptions contains all customized options used for load data source(s). -// This type is added here for convenience: this way consumers can import a single package called "ini". -type LoadOptions = ini.LoadOptions - -// Codec implements the encoding.Encoder and encoding.Decoder interfaces for INI encoding. -type Codec struct { - KeyDelimiter string - LoadOptions LoadOptions -} - -func (c Codec) Encode(v map[string]any) ([]byte, error) { - cfg := ini.Empty() - ini.PrettyFormat = false - - flattened := map[string]any{} - - flattened = flattenAndMergeMap(flattened, v, "", c.keyDelimiter()) - - keys := make([]string, 0, len(flattened)) - - for key := range flattened { - keys = append(keys, key) - } - - sort.Strings(keys) - - for _, key := range keys { - sectionName, keyName := "", key - - lastSep := strings.LastIndex(key, ".") - if lastSep != -1 { - sectionName = key[:(lastSep)] - keyName = key[(lastSep + 1):] - } - - // TODO: is this a good idea? - if sectionName == "default" { - sectionName = "" - } - - cfg.Section(sectionName).Key(keyName).SetValue(cast.ToString(flattened[key])) - } - - var buf bytes.Buffer - - _, err := cfg.WriteTo(&buf) - if err != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -func (c Codec) Decode(b []byte, v map[string]any) error { - cfg := ini.Empty(c.LoadOptions) - - err := cfg.Append(b) - if err != nil { - return err - } - - sections := cfg.Sections() - - for i := 0; i < len(sections); i++ { - section := sections[i] - keys := section.Keys() - - for j := 0; j < len(keys); j++ { - key := keys[j] - value := cfg.Section(section.Name()).Key(key.Name()).String() - - deepestMap := deepSearch(v, strings.Split(section.Name(), c.keyDelimiter())) - - // set innermost value - deepestMap[key.Name()] = value - } - } - - return nil -} - -func (c Codec) keyDelimiter() string { - if c.KeyDelimiter == "" { - return "." - } - - return c.KeyDelimiter -} diff --git a/vendor/github.com/spf13/viper/internal/encoding/ini/map_utils.go b/vendor/github.com/spf13/viper/internal/encoding/ini/map_utils.go deleted file mode 100644 index 490ab594ec..0000000000 --- a/vendor/github.com/spf13/viper/internal/encoding/ini/map_utils.go +++ /dev/null @@ -1,74 +0,0 @@ -package ini - -import ( - "strings" - - "github.com/spf13/cast" -) - -// THIS CODE IS COPIED HERE: IT SHOULD NOT BE MODIFIED -// AT SOME POINT IT WILL BE MOVED TO A COMMON PLACE -// deepSearch scans deep maps, following the key indexes listed in the -// sequence "path". -// The last value is expected to be another map, and is returned. -// -// In case intermediate keys do not exist, or map to a non-map value, -// a new map is created and inserted, and the search continues from there: -// the initial map "m" may be modified! -func deepSearch(m map[string]any, path []string) map[string]any { - for _, k := range path { - m2, ok := m[k] - if !ok { - // intermediate key does not exist - // => create it and continue from there - m3 := make(map[string]any) - m[k] = m3 - m = m3 - continue - } - m3, ok := m2.(map[string]any) - if !ok { - // intermediate key is a value - // => replace with a new map - m3 = make(map[string]any) - m[k] = m3 - } - // continue search from here - m = m3 - } - return m -} - -// flattenAndMergeMap recursively flattens the given map into a new map -// Code is based on the function with the same name in the main package. -// TODO: move it to a common place. -func flattenAndMergeMap(shadow, m map[string]any, prefix, delimiter string) map[string]any { - if shadow != nil && prefix != "" && shadow[prefix] != nil { - // prefix is shadowed => nothing more to flatten - return shadow - } - if shadow == nil { - shadow = make(map[string]any) - } - - var m2 map[string]any - if prefix != "" { - prefix += delimiter - } - for k, val := range m { - fullKey := prefix + k - switch val := val.(type) { - case map[string]any: - m2 = val - case map[any]any: - m2 = cast.ToStringMap(val) - default: - // immediate value - shadow[strings.ToLower(fullKey)] = val - continue - } - // recursively merge to shadow map - shadow = flattenAndMergeMap(shadow, m2, fullKey, delimiter) - } - return shadow -} diff --git a/vendor/github.com/spf13/viper/internal/encoding/javaproperties/codec.go b/vendor/github.com/spf13/viper/internal/encoding/javaproperties/codec.go deleted file mode 100644 index e92e5172c1..0000000000 --- a/vendor/github.com/spf13/viper/internal/encoding/javaproperties/codec.go +++ /dev/null @@ -1,86 +0,0 @@ -package javaproperties - -import ( - "bytes" - "sort" - "strings" - - "github.com/magiconair/properties" - "github.com/spf13/cast" -) - -// Codec implements the encoding.Encoder and encoding.Decoder interfaces for Java properties encoding. -type Codec struct { - KeyDelimiter string - - // Store read properties on the object so that we can write back in order with comments. - // This will only be used if the configuration read is a properties file. - // TODO: drop this feature in v2 - // TODO: make use of the global properties object optional - Properties *properties.Properties -} - -func (c *Codec) Encode(v map[string]any) ([]byte, error) { - if c.Properties == nil { - c.Properties = properties.NewProperties() - } - - flattened := map[string]any{} - - flattened = flattenAndMergeMap(flattened, v, "", c.keyDelimiter()) - - keys := make([]string, 0, len(flattened)) - - for key := range flattened { - keys = append(keys, key) - } - - sort.Strings(keys) - - for _, key := range keys { - _, _, err := c.Properties.Set(key, cast.ToString(flattened[key])) - if err != nil { - return nil, err - } - } - - var buf bytes.Buffer - - _, err := c.Properties.WriteComment(&buf, "#", properties.UTF8) - if err != nil { - return nil, err - } - - return buf.Bytes(), nil -} - -func (c *Codec) Decode(b []byte, v map[string]any) error { - var err error - c.Properties, err = properties.Load(b, properties.UTF8) - if err != nil { - return err - } - - for _, key := range c.Properties.Keys() { - // ignore existence check: we know it's there - value, _ := c.Properties.Get(key) - - // recursively build nested maps - path := strings.Split(key, c.keyDelimiter()) - lastKey := strings.ToLower(path[len(path)-1]) - deepestMap := deepSearch(v, path[0:len(path)-1]) - - // set innermost value - deepestMap[lastKey] = value - } - - return nil -} - -func (c Codec) keyDelimiter() string { - if c.KeyDelimiter == "" { - return "." - } - - return c.KeyDelimiter -} diff --git a/vendor/github.com/spf13/viper/internal/encoding/javaproperties/map_utils.go b/vendor/github.com/spf13/viper/internal/encoding/javaproperties/map_utils.go deleted file mode 100644 index 6e1aff2236..0000000000 --- a/vendor/github.com/spf13/viper/internal/encoding/javaproperties/map_utils.go +++ /dev/null @@ -1,74 +0,0 @@ -package javaproperties - -import ( - "strings" - - "github.com/spf13/cast" -) - -// THIS CODE IS COPIED HERE: IT SHOULD NOT BE MODIFIED -// AT SOME POINT IT WILL BE MOVED TO A COMMON PLACE -// deepSearch scans deep maps, following the key indexes listed in the -// sequence "path". -// The last value is expected to be another map, and is returned. -// -// In case intermediate keys do not exist, or map to a non-map value, -// a new map is created and inserted, and the search continues from there: -// the initial map "m" may be modified! -func deepSearch(m map[string]any, path []string) map[string]any { - for _, k := range path { - m2, ok := m[k] - if !ok { - // intermediate key does not exist - // => create it and continue from there - m3 := make(map[string]any) - m[k] = m3 - m = m3 - continue - } - m3, ok := m2.(map[string]any) - if !ok { - // intermediate key is a value - // => replace with a new map - m3 = make(map[string]any) - m[k] = m3 - } - // continue search from here - m = m3 - } - return m -} - -// flattenAndMergeMap recursively flattens the given map into a new map -// Code is based on the function with the same name in the main package. -// TODO: move it to a common place. -func flattenAndMergeMap(shadow, m map[string]any, prefix, delimiter string) map[string]any { - if shadow != nil && prefix != "" && shadow[prefix] != nil { - // prefix is shadowed => nothing more to flatten - return shadow - } - if shadow == nil { - shadow = make(map[string]any) - } - - var m2 map[string]any - if prefix != "" { - prefix += delimiter - } - for k, val := range m { - fullKey := prefix + k - switch val := val.(type) { - case map[string]any: - m2 = val - case map[any]any: - m2 = cast.ToStringMap(val) - default: - // immediate value - shadow[strings.ToLower(fullKey)] = val - continue - } - // recursively merge to shadow map - shadow = flattenAndMergeMap(shadow, m2, fullKey, delimiter) - } - return shadow -} diff --git a/vendor/github.com/spf13/viper/internal/features/finder.go b/vendor/github.com/spf13/viper/internal/features/finder.go new file mode 100644 index 0000000000..983ea3a9d7 --- /dev/null +++ b/vendor/github.com/spf13/viper/internal/features/finder.go @@ -0,0 +1,5 @@ +//go:build viper_finder + +package features + +const Finder = true diff --git a/vendor/github.com/spf13/viper/internal/features/finder_default.go b/vendor/github.com/spf13/viper/internal/features/finder_default.go new file mode 100644 index 0000000000..89bcb06ee1 --- /dev/null +++ b/vendor/github.com/spf13/viper/internal/features/finder_default.go @@ -0,0 +1,5 @@ +//go:build !viper_finder + +package features + +const Finder = false diff --git a/vendor/github.com/spf13/viper/logger.go b/vendor/github.com/spf13/viper/logger.go index 8938053b31..828042f29a 100644 --- a/vendor/github.com/spf13/viper/logger.go +++ b/vendor/github.com/spf13/viper/logger.go @@ -2,46 +2,9 @@ package viper import ( "context" - - slog "github.com/sagikazarmark/slog-shim" + "log/slog" ) -// Logger is a unified interface for various logging use cases and practices, including: -// - leveled logging -// - structured logging -// -// Deprecated: use `log/slog` instead. -type Logger interface { - // Trace logs a Trace event. - // - // Even more fine-grained information than Debug events. - // Loggers not supporting this level should fall back to Debug. - Trace(msg string, keyvals ...any) - - // Debug logs a Debug event. - // - // A verbose series of information events. - // They are useful when debugging the system. - Debug(msg string, keyvals ...any) - - // Info logs an Info event. - // - // General information about what's happening inside the system. - Info(msg string, keyvals ...any) - - // Warn logs a Warn(ing) event. - // - // Non-critical events that should be looked at. - Warn(msg string, keyvals ...any) - - // Error logs an Error event. - // - // Critical events that require immediate attention. - // Loggers commonly provide Fatal and Panic levels above Error level, - // but exiting and panicking is out of scope for a logging library. - Error(msg string, keyvals ...any) -} - // WithLogger sets a custom logger. func WithLogger(l *slog.Logger) Option { return optionFunc(func(v *Viper) { diff --git a/vendor/github.com/spf13/viper/remote.go b/vendor/github.com/spf13/viper/remote.go new file mode 100644 index 0000000000..bdde7de267 --- /dev/null +++ b/vendor/github.com/spf13/viper/remote.go @@ -0,0 +1,256 @@ +package viper + +import ( + "bytes" + "fmt" + "io" + "reflect" + "slices" +) + +// SupportedRemoteProviders are universally supported remote providers. +var SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore", "nats"} + +func resetRemote() { + SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore", "nats"} +} + +type remoteConfigFactory interface { + Get(rp RemoteProvider) (io.Reader, error) + Watch(rp RemoteProvider) (io.Reader, error) + WatchChannel(rp RemoteProvider) (<-chan *RemoteResponse, chan bool) +} + +type RemoteResponse struct { + Value []byte + Error error +} + +// RemoteConfig is optional, see the remote package. +var RemoteConfig remoteConfigFactory + +// UnsupportedRemoteProviderError denotes encountering an unsupported remote +// provider. Currently only etcd and Consul are supported. +type UnsupportedRemoteProviderError string + +// Error returns the formatted remote provider error. +func (str UnsupportedRemoteProviderError) Error() string { + return fmt.Sprintf("Unsupported Remote Provider Type %q", string(str)) +} + +// RemoteConfigError denotes encountering an error while trying to +// pull the configuration from the remote provider. +type RemoteConfigError string + +// Error returns the formatted remote provider error. +func (rce RemoteConfigError) Error() string { + return fmt.Sprintf("Remote Configurations Error: %s", string(rce)) +} + +type defaultRemoteProvider struct { + provider string + endpoint string + path string + secretKeyring string +} + +func (rp defaultRemoteProvider) Provider() string { + return rp.provider +} + +func (rp defaultRemoteProvider) Endpoint() string { + return rp.endpoint +} + +func (rp defaultRemoteProvider) Path() string { + return rp.path +} + +func (rp defaultRemoteProvider) SecretKeyring() string { + return rp.secretKeyring +} + +// RemoteProvider stores the configuration necessary +// to connect to a remote key/value store. +// Optional secretKeyring to unencrypt encrypted values +// can be provided. +type RemoteProvider interface { + Provider() string + Endpoint() string + Path() string + SecretKeyring() string +} + +// AddRemoteProvider adds a remote configuration source. +// Remote Providers are searched in the order they are added. +// provider is a string value: "etcd", "etcd3", "consul", "firestore" or "nats" are currently supported. +// endpoint is the url. etcd requires http://ip:port, consul requires ip:port, nats requires nats://ip:port +// path is the path in the k/v store to retrieve configuration +// To retrieve a config file called myapp.json from /configs/myapp.json +// you should set path to /configs and set config name (SetConfigName()) to +// "myapp". +func AddRemoteProvider(provider, endpoint, path string) error { + return v.AddRemoteProvider(provider, endpoint, path) +} + +func (v *Viper) AddRemoteProvider(provider, endpoint, path string) error { + if !slices.Contains(SupportedRemoteProviders, provider) { + return UnsupportedRemoteProviderError(provider) + } + if provider != "" && endpoint != "" { + v.logger.Info("adding remote provider", "provider", provider, "endpoint", endpoint) + + rp := &defaultRemoteProvider{ + endpoint: endpoint, + provider: provider, + path: path, + } + if !v.providerPathExists(rp) { + v.remoteProviders = append(v.remoteProviders, rp) + } + } + return nil +} + +// AddSecureRemoteProvider adds a remote configuration source. +// Secure Remote Providers are searched in the order they are added. +// provider is a string value: "etcd", "etcd3", "consul", "firestore" or "nats" are currently supported. +// endpoint is the url. etcd requires http://ip:port consul requires ip:port +// secretkeyring is the filepath to your openpgp secret keyring. e.g. /etc/secrets/myring.gpg +// path is the path in the k/v store to retrieve configuration +// To retrieve a config file called myapp.json from /configs/myapp.json +// you should set path to /configs and set config name (SetConfigName()) to +// "myapp". +// Secure Remote Providers are implemented with github.com/sagikazarmark/crypt. +func AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error { + return v.AddSecureRemoteProvider(provider, endpoint, path, secretkeyring) +} + +func (v *Viper) AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error { + if !slices.Contains(SupportedRemoteProviders, provider) { + return UnsupportedRemoteProviderError(provider) + } + if provider != "" && endpoint != "" { + v.logger.Info("adding remote provider", "provider", provider, "endpoint", endpoint) + + rp := &defaultRemoteProvider{ + endpoint: endpoint, + provider: provider, + path: path, + secretKeyring: secretkeyring, + } + if !v.providerPathExists(rp) { + v.remoteProviders = append(v.remoteProviders, rp) + } + } + return nil +} + +func (v *Viper) providerPathExists(p *defaultRemoteProvider) bool { + for _, y := range v.remoteProviders { + if reflect.DeepEqual(y, p) { + return true + } + } + return false +} + +// ReadRemoteConfig attempts to get configuration from a remote source +// and read it in the remote configuration registry. +func ReadRemoteConfig() error { return v.ReadRemoteConfig() } + +func (v *Viper) ReadRemoteConfig() error { + return v.getKeyValueConfig() +} + +func WatchRemoteConfig() error { return v.WatchRemoteConfig() } +func (v *Viper) WatchRemoteConfig() error { + return v.watchKeyValueConfig() +} + +func (v *Viper) WatchRemoteConfigOnChannel() error { + return v.watchKeyValueConfigOnChannel() +} + +// Retrieve the first found remote configuration. +func (v *Viper) getKeyValueConfig() error { + if RemoteConfig == nil { + return RemoteConfigError("Enable the remote features by doing a blank import of the viper/remote package: '_ github.com/spf13/viper/remote'") + } + + if len(v.remoteProviders) == 0 { + return RemoteConfigError("No Remote Providers") + } + + for _, rp := range v.remoteProviders { + val, err := v.getRemoteConfig(rp) + if err != nil { + v.logger.Error(fmt.Errorf("get remote config: %w", err).Error()) + + continue + } + + v.kvstore = val + + return nil + } + return RemoteConfigError("No Files Found") +} + +func (v *Viper) getRemoteConfig(provider RemoteProvider) (map[string]any, error) { + reader, err := RemoteConfig.Get(provider) + if err != nil { + return nil, err + } + err = v.unmarshalReader(reader, v.kvstore) + return v.kvstore, err +} + +// Retrieve the first found remote configuration. +func (v *Viper) watchKeyValueConfigOnChannel() error { + if len(v.remoteProviders) == 0 { + return RemoteConfigError("No Remote Providers") + } + + for _, rp := range v.remoteProviders { + respc, _ := RemoteConfig.WatchChannel(rp) + // Todo: Add quit channel + go func(rc <-chan *RemoteResponse) { + for { + b := <-rc + reader := bytes.NewReader(b.Value) + v.unmarshalReader(reader, v.kvstore) + } + }(respc) + return nil + } + return RemoteConfigError("No Files Found") +} + +// Retrieve the first found remote configuration. +func (v *Viper) watchKeyValueConfig() error { + if len(v.remoteProviders) == 0 { + return RemoteConfigError("No Remote Providers") + } + + for _, rp := range v.remoteProviders { + val, err := v.watchRemoteConfig(rp) + if err != nil { + v.logger.Error(fmt.Errorf("watch remote config: %w", err).Error()) + + continue + } + v.kvstore = val + return nil + } + return RemoteConfigError("No Files Found") +} + +func (v *Viper) watchRemoteConfig(provider RemoteProvider) (map[string]any, error) { + reader, err := RemoteConfig.Watch(provider) + if err != nil { + return nil, err + } + err = v.unmarshalReader(reader, v.kvstore) + return v.kvstore, err +} diff --git a/vendor/github.com/spf13/viper/util.go b/vendor/github.com/spf13/viper/util.go index 117c6ac312..2a08074bc7 100644 --- a/vendor/github.com/spf13/viper/util.go +++ b/vendor/github.com/spf13/viper/util.go @@ -12,13 +12,13 @@ package viper import ( "fmt" + "log/slog" "os" "path/filepath" "runtime" "strings" "unicode" - slog "github.com/sagikazarmark/slog-shim" "github.com/spf13/cast" ) @@ -128,15 +128,6 @@ func absPathify(logger *slog.Logger, inPath string) string { return "" } -func stringInSlice(a string, list []string) bool { - for _, b := range list { - if b == a { - return true - } - } - return false -} - func userHomeDir() string { if runtime.GOOS == "windows" { home := os.Getenv("HOMEDRIVE") + os.Getenv("HOMEPATH") diff --git a/vendor/github.com/spf13/viper/viper.go b/vendor/github.com/spf13/viper/viper.go index da68d9944c..f900e58b1a 100644 --- a/vendor/github.com/spf13/viper/viper.go +++ b/vendor/github.com/spf13/viper/viper.go @@ -25,29 +25,22 @@ import ( "errors" "fmt" "io" + "log/slog" "os" "path/filepath" "reflect" + "slices" "strconv" "strings" "sync" "time" "github.com/fsnotify/fsnotify" - "github.com/mitchellh/mapstructure" - slog "github.com/sagikazarmark/slog-shim" + "github.com/go-viper/mapstructure/v2" "github.com/spf13/afero" "github.com/spf13/cast" "github.com/spf13/pflag" - "github.com/spf13/viper/internal/encoding" - "github.com/spf13/viper/internal/encoding/dotenv" - "github.com/spf13/viper/internal/encoding/hcl" - "github.com/spf13/viper/internal/encoding/ini" - "github.com/spf13/viper/internal/encoding/javaproperties" - "github.com/spf13/viper/internal/encoding/json" - "github.com/spf13/viper/internal/encoding/toml" - "github.com/spf13/viper/internal/encoding/yaml" "github.com/spf13/viper/internal/features" ) @@ -63,24 +56,10 @@ func (e ConfigMarshalError) Error() string { var v *Viper -type RemoteResponse struct { - Value []byte - Error error -} - func init() { v = New() } -type remoteConfigFactory interface { - Get(rp RemoteProvider) (io.Reader, error) - Watch(rp RemoteProvider) (io.Reader, error) - WatchChannel(rp RemoteProvider) (<-chan *RemoteResponse, chan bool) -} - -// RemoteConfig is optional, see the remote package. -var RemoteConfig remoteConfigFactory - // UnsupportedConfigError denotes encountering an unsupported // configuration filetype. type UnsupportedConfigError string @@ -90,24 +69,6 @@ func (str UnsupportedConfigError) Error() string { return fmt.Sprintf("Unsupported Config Type %q", string(str)) } -// UnsupportedRemoteProviderError denotes encountering an unsupported remote -// provider. Currently only etcd and Consul are supported. -type UnsupportedRemoteProviderError string - -// Error returns the formatted remote provider error. -func (str UnsupportedRemoteProviderError) Error() string { - return fmt.Sprintf("Unsupported Remote Provider Type %q", string(str)) -} - -// RemoteConfigError denotes encountering an error while trying to -// pull the configuration from the remote provider. -type RemoteConfigError string - -// Error returns the formatted remote provider error. -func (rce RemoteConfigError) Error() string { - return fmt.Sprintf("Remote Configurations Error: %s", string(rce)) -} - // ConfigFileNotFoundError denotes failing to find configuration file. type ConfigFileNotFoundError struct { name, locations string @@ -190,6 +151,8 @@ type Viper struct { // The filesystem to read config from. fs afero.Fs + finder Finder + // A set of remote providers to search for the configuration remoteProviders []*defaultRemoteProvider @@ -200,9 +163,6 @@ type Viper struct { configPermissions os.FileMode envPrefix string - // Specific commands for ini parsing - iniLoadOptions ini.LoadOptions - automaticEnvApplied bool envKeyReplacer StringReplacer allowEmptyEnv bool @@ -221,9 +181,13 @@ type Viper struct { logger *slog.Logger - // TODO: should probably be protected with a mutex - encoderRegistry *encoding.EncoderRegistry - decoderRegistry *encoding.DecoderRegistry + encoderRegistry EncoderRegistry + decoderRegistry DecoderRegistry + + decodeHook mapstructure.DecodeHookFunc + + experimentalFinder bool + experimentalBindStruct bool } // New returns an initialized Viper instance. @@ -244,7 +208,13 @@ func New() *Viper { v.typeByDefValue = false v.logger = slog.New(&discardHandler{}) - v.resetEncoding() + codecRegistry := NewCodecRegistry() + + v.encoderRegistry = codecRegistry + v.decoderRegistry = codecRegistry + + v.experimentalFinder = features.Finder + v.experimentalBindStruct = features.BindStruct return v } @@ -280,10 +250,25 @@ type StringReplacer interface { // EnvKeyReplacer sets a replacer used for mapping environment variables to internal keys. func EnvKeyReplacer(r StringReplacer) Option { return optionFunc(func(v *Viper) { + if r == nil { + return + } + v.envKeyReplacer = r }) } +// WithDecodeHook sets a default decode hook for mapstructure. +func WithDecodeHook(h mapstructure.DecodeHookFunc) Option { + return optionFunc(func(v *Viper) { + if h == nil { + return + } + + v.decodeHook = h + }) +} + // NewWithOptions creates a new Viper instance. func NewWithOptions(opts ...Option) *Viper { v := New() @@ -292,138 +277,32 @@ func NewWithOptions(opts ...Option) *Viper { opt.apply(v) } - v.resetEncoding() - return v } +// SetOptions sets the options on the global Viper instance. +// +// Be careful when using this function: subsequent calls may override options you set. +// It's always better to use a local Viper instance. +func SetOptions(opts ...Option) { + for _, opt := range opts { + opt.apply(v) + } +} + // Reset is intended for testing, will reset all to default settings. // In the public interface for the viper package so applications // can use it in their testing as well. func Reset() { v = New() SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"} - SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore", "nats"} -} - -// TODO: make this lazy initialization instead. -func (v *Viper) resetEncoding() { - encoderRegistry := encoding.NewEncoderRegistry() - decoderRegistry := encoding.NewDecoderRegistry() - - { - codec := yaml.Codec{} - - encoderRegistry.RegisterEncoder("yaml", codec) - decoderRegistry.RegisterDecoder("yaml", codec) - - encoderRegistry.RegisterEncoder("yml", codec) - decoderRegistry.RegisterDecoder("yml", codec) - } - - { - codec := json.Codec{} - - encoderRegistry.RegisterEncoder("json", codec) - decoderRegistry.RegisterDecoder("json", codec) - } - - { - codec := toml.Codec{} - - encoderRegistry.RegisterEncoder("toml", codec) - decoderRegistry.RegisterDecoder("toml", codec) - } - - { - codec := hcl.Codec{} - - encoderRegistry.RegisterEncoder("hcl", codec) - decoderRegistry.RegisterDecoder("hcl", codec) - - encoderRegistry.RegisterEncoder("tfvars", codec) - decoderRegistry.RegisterDecoder("tfvars", codec) - } - - { - codec := ini.Codec{ - KeyDelimiter: v.keyDelim, - LoadOptions: v.iniLoadOptions, - } - - encoderRegistry.RegisterEncoder("ini", codec) - decoderRegistry.RegisterDecoder("ini", codec) - } - - { - codec := &javaproperties.Codec{ - KeyDelimiter: v.keyDelim, - } - - encoderRegistry.RegisterEncoder("properties", codec) - decoderRegistry.RegisterDecoder("properties", codec) - - encoderRegistry.RegisterEncoder("props", codec) - decoderRegistry.RegisterDecoder("props", codec) - - encoderRegistry.RegisterEncoder("prop", codec) - decoderRegistry.RegisterDecoder("prop", codec) - } - - { - codec := &dotenv.Codec{} - - encoderRegistry.RegisterEncoder("dotenv", codec) - decoderRegistry.RegisterDecoder("dotenv", codec) - - encoderRegistry.RegisterEncoder("env", codec) - decoderRegistry.RegisterDecoder("env", codec) - } - v.encoderRegistry = encoderRegistry - v.decoderRegistry = decoderRegistry -} - -type defaultRemoteProvider struct { - provider string - endpoint string - path string - secretKeyring string -} - -func (rp defaultRemoteProvider) Provider() string { - return rp.provider -} - -func (rp defaultRemoteProvider) Endpoint() string { - return rp.endpoint -} - -func (rp defaultRemoteProvider) Path() string { - return rp.path -} - -func (rp defaultRemoteProvider) SecretKeyring() string { - return rp.secretKeyring -} - -// RemoteProvider stores the configuration necessary -// to connect to a remote key/value store. -// Optional secretKeyring to unencrypt encrypted values -// can be provided. -type RemoteProvider interface { - Provider() string - Endpoint() string - Path() string - SecretKeyring() string + resetRemote() } // SupportedExts are universally supported extensions. var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"} -// SupportedRemoteProviders are universally supported remote providers. -var SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore", "nats"} - // OnConfigChange sets the event handler that is called when a config file changes. func OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) } @@ -574,90 +453,20 @@ func (v *Viper) ConfigFileUsed() string { return v.configFile } func AddConfigPath(in string) { v.AddConfigPath(in) } func (v *Viper) AddConfigPath(in string) { + if v.finder != nil { + v.logger.Warn("ineffective call to function: custom finder takes precedence", slog.String("function", "AddConfigPath")) + } + if in != "" { absin := absPathify(v.logger, in) v.logger.Info("adding path to search paths", "path", absin) - if !stringInSlice(absin, v.configPaths) { + if !slices.Contains(v.configPaths, absin) { v.configPaths = append(v.configPaths, absin) } } } -// AddRemoteProvider adds a remote configuration source. -// Remote Providers are searched in the order they are added. -// provider is a string value: "etcd", "etcd3", "consul", "firestore" or "nats" are currently supported. -// endpoint is the url. etcd requires http://ip:port, consul requires ip:port, nats requires nats://ip:port -// path is the path in the k/v store to retrieve configuration -// To retrieve a config file called myapp.json from /configs/myapp.json -// you should set path to /configs and set config name (SetConfigName()) to -// "myapp". -func AddRemoteProvider(provider, endpoint, path string) error { - return v.AddRemoteProvider(provider, endpoint, path) -} - -func (v *Viper) AddRemoteProvider(provider, endpoint, path string) error { - if !stringInSlice(provider, SupportedRemoteProviders) { - return UnsupportedRemoteProviderError(provider) - } - if provider != "" && endpoint != "" { - v.logger.Info("adding remote provider", "provider", provider, "endpoint", endpoint) - - rp := &defaultRemoteProvider{ - endpoint: endpoint, - provider: provider, - path: path, - } - if !v.providerPathExists(rp) { - v.remoteProviders = append(v.remoteProviders, rp) - } - } - return nil -} - -// AddSecureRemoteProvider adds a remote configuration source. -// Secure Remote Providers are searched in the order they are added. -// provider is a string value: "etcd", "etcd3", "consul", "firestore" or "nats" are currently supported. -// endpoint is the url. etcd requires http://ip:port consul requires ip:port -// secretkeyring is the filepath to your openpgp secret keyring. e.g. /etc/secrets/myring.gpg -// path is the path in the k/v store to retrieve configuration -// To retrieve a config file called myapp.json from /configs/myapp.json -// you should set path to /configs and set config name (SetConfigName()) to -// "myapp". -// Secure Remote Providers are implemented with github.com/sagikazarmark/crypt. -func AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error { - return v.AddSecureRemoteProvider(provider, endpoint, path, secretkeyring) -} - -func (v *Viper) AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error { - if !stringInSlice(provider, SupportedRemoteProviders) { - return UnsupportedRemoteProviderError(provider) - } - if provider != "" && endpoint != "" { - v.logger.Info("adding remote provider", "provider", provider, "endpoint", endpoint) - - rp := &defaultRemoteProvider{ - endpoint: endpoint, - provider: provider, - path: path, - secretKeyring: secretkeyring, - } - if !v.providerPathExists(rp) { - v.remoteProviders = append(v.remoteProviders, rp) - } - } - return nil -} - -func (v *Viper) providerPathExists(p *defaultRemoteProvider) bool { - for _, y := range v.remoteProviders { - if reflect.DeepEqual(y, p) { - return true - } - } - return false -} - // searchMap recursively searches for a value for path in source map. // Returns nil if not found. // Note: This assumes that the path entries and map keys are lower cased. @@ -965,6 +774,7 @@ func (v *Viper) Sub(key string) *Viper { subv.automaticEnvApplied = v.automaticEnvApplied subv.envPrefix = v.envPrefix subv.envKeyReplacer = v.envKeyReplacer + subv.keyDelim = v.keyDelim subv.config = cast.ToStringMap(data) return subv } @@ -1006,6 +816,13 @@ func (v *Viper) GetInt64(key string) int64 { return cast.ToInt64(v.Get(key)) } +// GetUint8 returns the value associated with the key as an unsigned integer. +func GetUint8(key string) uint8 { return v.GetUint8(key) } + +func (v *Viper) GetUint8(key string) uint8 { + return cast.ToUint8(v.Get(key)) +} + // GetUint returns the value associated with the key as an unsigned integer. func GetUint(key string) uint { return v.GetUint(key) } @@ -1105,7 +922,7 @@ func UnmarshalKey(key string, rawVal any, opts ...DecoderConfigOption) error { } func (v *Viper) UnmarshalKey(key string, rawVal any, opts ...DecoderConfigOption) error { - return decode(v.Get(key), defaultDecoderConfig(rawVal, opts...)) + return decode(v.Get(key), v.defaultDecoderConfig(rawVal, opts...)) } // Unmarshal unmarshals the config into a Struct. Make sure that the tags @@ -1117,7 +934,7 @@ func Unmarshal(rawVal any, opts ...DecoderConfigOption) error { func (v *Viper) Unmarshal(rawVal any, opts ...DecoderConfigOption) error { keys := v.AllKeys() - if features.BindStruct { + if v.experimentalBindStruct { // TODO: make this optional? structKeys, err := v.decodeStructKeys(rawVal, opts...) if err != nil { @@ -1128,13 +945,13 @@ func (v *Viper) Unmarshal(rawVal any, opts ...DecoderConfigOption) error { } // TODO: struct keys should be enough? - return decode(v.getSettings(keys), defaultDecoderConfig(rawVal, opts...)) + return decode(v.getSettings(keys), v.defaultDecoderConfig(rawVal, opts...)) } func (v *Viper) decodeStructKeys(input any, opts ...DecoderConfigOption) ([]string, error) { var structKeyMap map[string]any - err := decode(input, defaultDecoderConfig(&structKeyMap, opts...)) + err := decode(input, v.defaultDecoderConfig(&structKeyMap, opts...)) if err != nil { return nil, err } @@ -1151,22 +968,54 @@ func (v *Viper) decodeStructKeys(input any, opts ...DecoderConfigOption) ([]stri // defaultDecoderConfig returns default mapstructure.DecoderConfig with support // of time.Duration values & string slices. -func defaultDecoderConfig(output any, opts ...DecoderConfigOption) *mapstructure.DecoderConfig { +func (v *Viper) defaultDecoderConfig(output any, opts ...DecoderConfigOption) *mapstructure.DecoderConfig { + decodeHook := v.decodeHook + if decodeHook == nil { + decodeHook = mapstructure.ComposeDecodeHookFunc( + mapstructure.StringToTimeDurationHookFunc(), + // mapstructure.StringToSliceHookFunc(","), + stringToWeakSliceHookFunc(","), + ) + } + c := &mapstructure.DecoderConfig{ Metadata: nil, - Result: output, WeaklyTypedInput: true, - DecodeHook: mapstructure.ComposeDecodeHookFunc( - mapstructure.StringToTimeDurationHookFunc(), - mapstructure.StringToSliceHookFunc(","), - ), + DecodeHook: decodeHook, } + for _, opt := range opts { opt(c) } + + // Do not allow overwriting the output + c.Result = output + return c } +// As of mapstructure v2.0.0 StringToSliceHookFunc checks if the return type is a string slice. +// This function removes that check. +// TODO: implement a function that checks if the value can be converted to the return type and use it instead. +func stringToWeakSliceHookFunc(sep string) mapstructure.DecodeHookFunc { + return func( + f reflect.Type, + t reflect.Type, + data interface{}, + ) (interface{}, error) { + if f.Kind() != reflect.String || t.Kind() != reflect.Slice { + return data, nil + } + + raw := data.(string) + if raw == "" { + return []string{}, nil + } + + return strings.Split(raw, sep), nil + } +} + // decode is a wrapper around mapstructure.Decode that mimics the WeakDecode functionality. func decode(input any, config *mapstructure.DecoderConfig) error { decoder, err := mapstructure.NewDecoder(config) @@ -1183,12 +1032,12 @@ func UnmarshalExact(rawVal any, opts ...DecoderConfigOption) error { } func (v *Viper) UnmarshalExact(rawVal any, opts ...DecoderConfigOption) error { - config := defaultDecoderConfig(rawVal, opts...) + config := v.defaultDecoderConfig(rawVal, opts...) config.ErrorUnused = true keys := v.AllKeys() - if features.BindStruct { + if v.experimentalBindStruct { // TODO: make this optional? structKeys, err := v.decodeStructKeys(rawVal, opts...) if err != nil { @@ -1638,7 +1487,7 @@ func (v *Viper) ReadInConfig() error { return err } - if !stringInSlice(v.getConfigType(), SupportedExts) { + if !slices.Contains(SupportedExts, v.getConfigType()) { return UnsupportedConfigError(v.getConfigType()) } @@ -1669,7 +1518,7 @@ func (v *Viper) MergeInConfig() error { return err } - if !stringInSlice(v.getConfigType(), SupportedExts) { + if !slices.Contains(SupportedExts, v.getConfigType()) { return UnsupportedConfigError(v.getConfigType()) } @@ -1686,6 +1535,10 @@ func (v *Viper) MergeInConfig() error { func ReadConfig(in io.Reader) error { return v.ReadConfig(in) } func (v *Viper) ReadConfig(in io.Reader) error { + if v.configType == "" { + return errors.New("cannot decode configuration: config type is not set") + } + v.config = make(map[string]any) return v.unmarshalReader(in, v.config) } @@ -1694,6 +1547,10 @@ func (v *Viper) ReadConfig(in io.Reader) error { func MergeConfig(in io.Reader) error { return v.MergeConfig(in) } func (v *Viper) MergeConfig(in io.Reader) error { + if v.configType == "" { + return errors.New("cannot decode configuration: config type is not set") + } + cfg := make(map[string]any) if err := v.unmarshalReader(in, cfg); err != nil { return err @@ -1742,6 +1599,19 @@ func (v *Viper) WriteConfigAs(filename string) error { return v.writeConfig(filename, true) } +// WriteConfigTo writes current configuration to an [io.Writer]. +func WriteConfigTo(w io.Writer) error { return v.WriteConfigTo(w) } + +func (v *Viper) WriteConfigTo(w io.Writer) error { + format := strings.ToLower(v.getConfigType()) + + if !slices.Contains(SupportedExts, format) { + return UnsupportedConfigError(format) + } + + return v.marshalWriter(w, format) +} + // SafeWriteConfigAs writes current configuration to a given filename if it does not exist. func SafeWriteConfigAs(filename string) error { return v.SafeWriteConfigAs(filename) } @@ -1768,7 +1638,7 @@ func (v *Viper) writeConfig(filename string, force bool) error { return fmt.Errorf("config type could not be determined for %s", filename) } - if !stringInSlice(configType, SupportedExts) { + if !slices.Contains(SupportedExts, configType) { return UnsupportedConfigError(configType) } if v.config == nil { @@ -1795,12 +1665,20 @@ func (v *Viper) unmarshalReader(in io.Reader, c map[string]any) error { buf := new(bytes.Buffer) buf.ReadFrom(in) - switch format := strings.ToLower(v.getConfigType()); format { - case "yaml", "yml", "json", "toml", "hcl", "tfvars", "ini", "properties", "props", "prop", "dotenv", "env": - err := v.decoderRegistry.Decode(format, buf.Bytes(), c) - if err != nil { - return ConfigParseError{err} - } + format := strings.ToLower(v.getConfigType()) + + if !slices.Contains(SupportedExts, format) { + return UnsupportedConfigError(format) + } + + decoder, err := v.decoderRegistry.Decoder(format) + if err != nil { + return ConfigParseError{err} + } + + err = decoder.Decode(buf.Bytes(), c) + if err != nil { + return ConfigParseError{err} } insensitiviseMap(c) @@ -1808,20 +1686,24 @@ func (v *Viper) unmarshalReader(in io.Reader, c map[string]any) error { } // Marshal a map into Writer. -func (v *Viper) marshalWriter(f afero.File, configType string) error { +func (v *Viper) marshalWriter(w io.Writer, configType string) error { c := v.AllSettings() - switch configType { - case "yaml", "yml", "json", "toml", "hcl", "tfvars", "ini", "prop", "props", "properties", "dotenv", "env": - b, err := v.encoderRegistry.Encode(configType, c) - if err != nil { - return ConfigMarshalError{err} - } - _, err = f.WriteString(string(b)) - if err != nil { - return ConfigMarshalError{err} - } + encoder, err := v.encoderRegistry.Encoder(configType) + if err != nil { + return ConfigMarshalError{err} } + + b, err := encoder.Encode(c) + if err != nil { + return ConfigMarshalError{err} + } + + _, err = w.Write(b) + if err != nil { + return ConfigMarshalError{err} + } + return nil } @@ -1953,106 +1835,6 @@ func mergeMaps(src, tgt map[string]any, itgt map[any]any) { } } -// ReadRemoteConfig attempts to get configuration from a remote source -// and read it in the remote configuration registry. -func ReadRemoteConfig() error { return v.ReadRemoteConfig() } - -func (v *Viper) ReadRemoteConfig() error { - return v.getKeyValueConfig() -} - -func WatchRemoteConfig() error { return v.WatchRemoteConfig() } -func (v *Viper) WatchRemoteConfig() error { - return v.watchKeyValueConfig() -} - -func (v *Viper) WatchRemoteConfigOnChannel() error { - return v.watchKeyValueConfigOnChannel() -} - -// Retrieve the first found remote configuration. -func (v *Viper) getKeyValueConfig() error { - if RemoteConfig == nil { - return RemoteConfigError("Enable the remote features by doing a blank import of the viper/remote package: '_ github.com/spf13/viper/remote'") - } - - if len(v.remoteProviders) == 0 { - return RemoteConfigError("No Remote Providers") - } - - for _, rp := range v.remoteProviders { - val, err := v.getRemoteConfig(rp) - if err != nil { - v.logger.Error(fmt.Errorf("get remote config: %w", err).Error()) - - continue - } - - v.kvstore = val - - return nil - } - return RemoteConfigError("No Files Found") -} - -func (v *Viper) getRemoteConfig(provider RemoteProvider) (map[string]any, error) { - reader, err := RemoteConfig.Get(provider) - if err != nil { - return nil, err - } - err = v.unmarshalReader(reader, v.kvstore) - return v.kvstore, err -} - -// Retrieve the first found remote configuration. -func (v *Viper) watchKeyValueConfigOnChannel() error { - if len(v.remoteProviders) == 0 { - return RemoteConfigError("No Remote Providers") - } - - for _, rp := range v.remoteProviders { - respc, _ := RemoteConfig.WatchChannel(rp) - // Todo: Add quit channel - go func(rc <-chan *RemoteResponse) { - for { - b := <-rc - reader := bytes.NewReader(b.Value) - v.unmarshalReader(reader, v.kvstore) - } - }(respc) - return nil - } - return RemoteConfigError("No Files Found") -} - -// Retrieve the first found remote configuration. -func (v *Viper) watchKeyValueConfig() error { - if len(v.remoteProviders) == 0 { - return RemoteConfigError("No Remote Providers") - } - - for _, rp := range v.remoteProviders { - val, err := v.watchRemoteConfig(rp) - if err != nil { - v.logger.Error(fmt.Errorf("watch remote config: %w", err).Error()) - - continue - } - v.kvstore = val - return nil - } - return RemoteConfigError("No Files Found") -} - -func (v *Viper) watchRemoteConfig(provider RemoteProvider) (map[string]any, error) { - reader, err := RemoteConfig.Watch(provider) - if err != nil { - return nil, err - } - err = v.unmarshalReader(reader, v.kvstore) - return v.kvstore, err -} - // AllKeys returns all keys holding a value, regardless of where they are set. // Nested keys are returned with a v.keyDelim separator. func AllKeys() []string { return v.AllKeys() } @@ -2174,6 +1956,10 @@ func (v *Viper) SetFs(fs afero.Fs) { func SetConfigName(in string) { v.SetConfigName(in) } func (v *Viper) SetConfigName(in string) { + if v.finder != nil { + v.logger.Warn("ineffective call to function: custom finder takes precedence", slog.String("function", "SetConfigName")) + } + if in != "" { v.configName = in v.configFile = "" @@ -2197,13 +1983,6 @@ func (v *Viper) SetConfigPermissions(perm os.FileMode) { v.configPermissions = perm.Perm() } -// IniLoadOptions sets the load options for ini parsing. -func IniLoadOptions(in ini.LoadOptions) Option { - return optionFunc(func(v *Viper) { - v.iniLoadOptions = in - }) -} - func (v *Viper) getConfigType() string { if v.configType != "" { return v.configType diff --git a/vendor/go.etcd.io/etcd/api/v3/version/version.go b/vendor/go.etcd.io/etcd/api/v3/version/version.go index 15c99a2c28..e614915822 100644 --- a/vendor/go.etcd.io/etcd/api/v3/version/version.go +++ b/vendor/go.etcd.io/etcd/api/v3/version/version.go @@ -26,7 +26,7 @@ import ( var ( // MinClusterVersion is the min cluster version this etcd binary is compatible with. MinClusterVersion = "3.0.0" - Version = "3.5.18" + Version = "3.5.19" APIVersion = "unknown" // Git SHA Value will be set during build diff --git a/vendor/go.opentelemetry.io/otel/.gitignore b/vendor/go.opentelemetry.io/otel/.gitignore index ae8577ef36..749e8e881b 100644 --- a/vendor/go.opentelemetry.io/otel/.gitignore +++ b/vendor/go.opentelemetry.io/otel/.gitignore @@ -1,6 +1,7 @@ .DS_Store Thumbs.db +.cache/ .tools/ venv/ .idea/ diff --git a/vendor/go.opentelemetry.io/otel/.golangci.yml b/vendor/go.opentelemetry.io/otel/.golangci.yml index ce3f40b609..c58e48ab0c 100644 --- a/vendor/go.opentelemetry.io/otel/.golangci.yml +++ b/vendor/go.opentelemetry.io/otel/.golangci.yml @@ -25,13 +25,13 @@ linters: - perfsprint - revive - staticcheck - - tenv - testifylint - typecheck - unconvert - unused - unparam - usestdlibvars + - usetesting issues: # Maximum issues count per one linter. @@ -175,132 +175,60 @@ linters-settings: # This means that linting errors with less than 0.8 confidence will be ignored. # Default: 0.8 confidence: 0.01 + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md rules: - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#blank-imports - name: blank-imports - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#bool-literal-in-expr - name: bool-literal-in-expr - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#constant-logical-expr - name: constant-logical-expr - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#context-as-argument - # TODO (#3372) re-enable linter when it is compatible. https://github.com/golangci/golangci-lint/issues/3280 - name: context-as-argument disabled: true arguments: - allowTypesBefore: "*testing.T" - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#context-keys-type + - allowTypesBefore: "*testing.T" - name: context-keys-type - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#deep-exit - name: deep-exit - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#defer - name: defer - disabled: false arguments: - ["call-chain", "loop"] - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#dot-imports - name: dot-imports - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#duplicated-imports - name: duplicated-imports - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#early-return - name: early-return - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#empty-block + arguments: + - "preserveScope" - name: empty-block - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#empty-lines - name: empty-lines - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#error-naming - name: error-naming - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#error-return - name: error-return - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#error-strings - name: error-strings - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#errorf - name: errorf - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#exported - name: exported - disabled: false arguments: - "sayRepetitiveInsteadOfStutters" - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#flag-parameter - name: flag-parameter - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#identical-branches - name: identical-branches - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#if-return - name: if-return - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#increment-decrement + - name: import-shadowing - name: increment-decrement - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#indent-error-flow - name: indent-error-flow - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#import-shadowing - - name: import-shadowing - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#package-comments + arguments: + - "preserveScope" - name: package-comments - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#range - name: range - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#range-val-in-closure - name: range-val-in-closure - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#range-val-address - name: range-val-address - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#redefines-builtin-id - name: redefines-builtin-id - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#string-format - name: string-format - disabled: false arguments: - - panic - '/^[^\n]*$/' - must not contain line breaks - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#struct-tag - name: struct-tag - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#superfluous-else - name: superfluous-else - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#time-equal - - name: time-equal - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#var-naming - - name: var-naming - disabled: false arguments: - - ["ID"] # AllowList - - ["Otel", "Aws", "Gcp"] # DenyList - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#var-declaration - - name: var-declaration - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unconditional-recursion + - "preserveScope" + - name: time-equal - name: unconditional-recursion - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unexported-return - name: unexported-return - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unhandled-error - name: unhandled-error - disabled: false arguments: - "fmt.Fprint" - "fmt.Fprintf" @@ -308,15 +236,14 @@ linters-settings: - "fmt.Print" - "fmt.Printf" - "fmt.Println" - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unnecessary-stmt - name: unnecessary-stmt - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#useless-break - name: useless-break - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#waitgroup-by-value + - name: var-declaration + - name: var-naming + arguments: + - ["ID"] # AllowList + - ["Otel", "Aws", "Gcp"] # DenyList - name: waitgroup-by-value - disabled: false testifylint: enable-all: true disable: diff --git a/vendor/go.opentelemetry.io/otel/CHANGELOG.md b/vendor/go.opentelemetry.io/otel/CHANGELOG.md index 599d59cd13..c076db2823 100644 --- a/vendor/go.opentelemetry.io/otel/CHANGELOG.md +++ b/vendor/go.opentelemetry.io/otel/CHANGELOG.md @@ -11,6 +11,46 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm +## [1.35.0/0.57.0/0.11.0] 2025-03-05 + +This release is the last to support [Go 1.22]. +The next release will require at least [Go 1.23]. + +### Added + +- Add `ValueFromAttribute` and `KeyValueFromAttribute` in `go.opentelemetry.io/otel/log`. (#6180) +- Add `EventName` and `SetEventName` to `Record` in `go.opentelemetry.io/otel/log`. (#6187) +- Add `EventName` to `RecordFactory` in `go.opentelemetry.io/otel/log/logtest`. (#6187) +- `AssertRecordEqual` in `go.opentelemetry.io/otel/log/logtest` checks `Record.EventName`. (#6187) +- Add `EventName` and `SetEventName` to `Record` in `go.opentelemetry.io/otel/sdk/log`. (#6193) +- Add `EventName` to `RecordFactory` in `go.opentelemetry.io/otel/sdk/log/logtest`. (#6193) +- Emit `Record.EventName` field in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc`. (#6211) +- Emit `Record.EventName` field in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#6211) +- Emit `Record.EventName` field in `go.opentelemetry.io/otel/exporters/stdout/stdoutlog` (#6210) +- The `go.opentelemetry.io/otel/semconv/v1.28.0` package. + The package contains semantic conventions from the `v1.28.0` version of the OpenTelemetry Semantic Conventions. + See the [migration documentation](./semconv/v1.28.0/MIGRATION.md) for information on how to upgrade from `go.opentelemetry.io/otel/semconv/v1.27.0`(#6236) +- The `go.opentelemetry.io/otel/semconv/v1.30.0` package. + The package contains semantic conventions from the `v1.30.0` version of the OpenTelemetry Semantic Conventions. + See the [migration documentation](./semconv/v1.30.0/MIGRATION.md) for information on how to upgrade from `go.opentelemetry.io/otel/semconv/v1.28.0`(#6240) +- Document the pitfalls of using `Resource` as a comparable type. + `Resource.Equal` and `Resource.Equivalent` should be used instead. (#6272) +- Support [Go 1.24]. (#6304) +- Add `FilterProcessor` and `EnabledParameters` in `go.opentelemetry.io/otel/sdk/log`. + It replaces `go.opentelemetry.io/otel/sdk/log/internal/x.FilterProcessor`. + Compared to previous version it additionally gives the possibility to filter by resource and instrumentation scope. (#6317) + +### Changed + +- Update `github.com/prometheus/common` to `v0.62.0`, which changes the `NameValidationScheme` to `NoEscaping`. + This allows metrics names to keep original delimiters (e.g. `.`), rather than replacing with underscores. + This is controlled by the `Content-Type` header, or can be reverted by setting `NameValidationScheme` to `LegacyValidation` in `github.com/prometheus/common/model`. (#6198) + +### Fixes + +- Eliminate goroutine leak for the processor returned by `NewSimpleSpanProcessor` in `go.opentelemetry.io/otel/sdk/trace` when `Shutdown` is called and the passed `ctx` is canceled and `SpanExporter.Shutdown` has not returned. (#6368) +- Eliminate goroutine leak for the processor returned by `NewBatchSpanProcessor` in `go.opentelemetry.io/otel/sdk/trace` when `ForceFlush` is called and the passed `ctx` is canceled and `SpanExporter.Export` has not returned. (#6369) + ## [1.34.0/0.56.0/0.10.0] 2025-01-17 ### Changed @@ -3197,7 +3237,8 @@ It contains api and sdk for trace and meter. - CircleCI build CI manifest files. - CODEOWNERS file to track owners of this project. -[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.34.0...HEAD +[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.35.0...HEAD +[1.35.0/0.57.0/0.11.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.35.0 [1.34.0/0.56.0/0.10.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.34.0 [1.33.0/0.55.0/0.9.0/0.0.12]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.33.0 [1.32.0/0.54.0/0.8.0/0.0.11]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.32.0 @@ -3288,6 +3329,7 @@ It contains api and sdk for trace and meter. +[Go 1.24]: https://go.dev/doc/go1.24 [Go 1.23]: https://go.dev/doc/go1.23 [Go 1.22]: https://go.dev/doc/go1.22 [Go 1.21]: https://go.dev/doc/go1.21 diff --git a/vendor/go.opentelemetry.io/otel/CONTRIBUTING.md b/vendor/go.opentelemetry.io/otel/CONTRIBUTING.md index 22a2e9dbd4..7b8af585aa 100644 --- a/vendor/go.opentelemetry.io/otel/CONTRIBUTING.md +++ b/vendor/go.opentelemetry.io/otel/CONTRIBUTING.md @@ -181,6 +181,18 @@ patterns in the spec. For a deeper discussion, see [this](https://github.com/open-telemetry/opentelemetry-specification/issues/165). +## Tests + +Each functionality should be covered by tests. + +Performance-critical functionality should also be covered by benchmarks. + +- Pull requests adding a performance-critical functionality +should have `go test -bench` output in their description. +- Pull requests changing a performance-critical functionality +should have [`benchstat`](https://pkg.go.dev/golang.org/x/perf/cmd/benchstat) +output in their description. + ## Documentation Each (non-internal, non-test) package must be documented using diff --git a/vendor/go.opentelemetry.io/otel/Makefile b/vendor/go.opentelemetry.io/otel/Makefile index a7f6d8cc68..226410d742 100644 --- a/vendor/go.opentelemetry.io/otel/Makefile +++ b/vendor/go.opentelemetry.io/otel/Makefile @@ -11,6 +11,10 @@ ALL_COVERAGE_MOD_DIRS := $(shell find . -type f -name 'go.mod' -exec dirname {} GO = go TIMEOUT = 60 +# User to run as in docker images. +DOCKER_USER=$(shell id -u):$(shell id -g) +DEPENDENCIES_DOCKERFILE=./dependencies.Dockerfile + .DEFAULT_GOAL := precommit .PHONY: precommit ci @@ -81,20 +85,20 @@ PIP := $(PYTOOLS)/pip WORKDIR := /workdir # The python image to use for the virtual environment. -PYTHONIMAGE := python:3.11.3-slim-bullseye +PYTHONIMAGE := $(shell awk '$$4=="python" {print $$2}' $(DEPENDENCIES_DOCKERFILE)) # Run the python image with the current directory mounted. -DOCKERPY := docker run --rm -v "$(CURDIR):$(WORKDIR)" -w $(WORKDIR) $(PYTHONIMAGE) +DOCKERPY := docker run --rm -u $(DOCKER_USER) -v "$(CURDIR):$(WORKDIR)" -w $(WORKDIR) $(PYTHONIMAGE) # Create a virtual environment for Python tools. $(PYTOOLS): # The `--upgrade` flag is needed to ensure that the virtual environment is # created with the latest pip version. - @$(DOCKERPY) bash -c "python3 -m venv $(VENVDIR) && $(PIP) install --upgrade pip" + @$(DOCKERPY) bash -c "python3 -m venv $(VENVDIR) && $(PIP) install --upgrade --cache-dir=$(WORKDIR)/.cache/pip pip" # Install python packages into the virtual environment. $(PYTOOLS)/%: $(PYTOOLS) - @$(DOCKERPY) $(PIP) install -r requirements.txt + @$(DOCKERPY) $(PIP) install --cache-dir=$(WORKDIR)/.cache/pip -r requirements.txt CODESPELL = $(PYTOOLS)/codespell $(CODESPELL): PACKAGE=codespell @@ -119,7 +123,7 @@ vanity-import-fix: $(PORTO) # Generate go.work file for local development. .PHONY: go-work go-work: $(CROSSLINK) - $(CROSSLINK) work --root=$(shell pwd) + $(CROSSLINK) work --root=$(shell pwd) --go=1.22.7 # Build @@ -265,13 +269,30 @@ check-clean-work-tree: exit 1; \ fi +# The weaver docker image to use for semconv-generate. +WEAVER_IMAGE := $(shell awk '$$4=="weaver" {print $$2}' $(DEPENDENCIES_DOCKERFILE)) + SEMCONVPKG ?= "semconv/" .PHONY: semconv-generate -semconv-generate: $(SEMCONVGEN) $(SEMCONVKIT) +semconv-generate: $(SEMCONVKIT) [ "$(TAG)" ] || ( echo "TAG unset: missing opentelemetry semantic-conventions tag"; exit 1 ) - [ "$(OTEL_SEMCONV_REPO)" ] || ( echo "OTEL_SEMCONV_REPO unset: missing path to opentelemetry semantic-conventions repo"; exit 1 ) - $(SEMCONVGEN) -i "$(OTEL_SEMCONV_REPO)/model/." --only=attribute_group -p conventionType=trace -f attribute_group.go -z "$(SEMCONVPKG)/capitalizations.txt" -t "$(SEMCONVPKG)/template.j2" -s "$(TAG)" - $(SEMCONVGEN) -i "$(OTEL_SEMCONV_REPO)/model/." --only=metric -f metric.go -t "$(SEMCONVPKG)/metric_template.j2" -s "$(TAG)" + # Ensure the target directory for source code is available. + mkdir -p $(PWD)/$(SEMCONVPKG)/${TAG} + # Note: We mount a home directory for downloading/storing the semconv repository. + # Weaver will automatically clean the cache when finished, but the directories will remain. + mkdir -p ~/.weaver + docker run --rm \ + -u $(DOCKER_USER) \ + --env HOME=/tmp/weaver \ + --mount 'type=bind,source=$(PWD)/semconv,target=/home/weaver/templates/registry/go,readonly' \ + --mount 'type=bind,source=$(PWD)/semconv/${TAG},target=/home/weaver/target' \ + --mount 'type=bind,source=$(HOME)/.weaver,target=/tmp/weaver/.weaver' \ + $(WEAVER_IMAGE) registry generate \ + --registry=https://github.com/open-telemetry/semantic-conventions/archive/refs/tags/$(TAG).zip[model] \ + --templates=/home/weaver/templates \ + --param tag=$(TAG) \ + go \ + /home/weaver/target $(SEMCONVKIT) -output "$(SEMCONVPKG)/$(TAG)" -tag "$(TAG)" .PHONY: gorelease diff --git a/vendor/go.opentelemetry.io/otel/README.md b/vendor/go.opentelemetry.io/otel/README.md index d9a1920762..8421cd7e59 100644 --- a/vendor/go.opentelemetry.io/otel/README.md +++ b/vendor/go.opentelemetry.io/otel/README.md @@ -4,6 +4,8 @@ [![codecov.io](https://codecov.io/gh/open-telemetry/opentelemetry-go/coverage.svg?branch=main)](https://app.codecov.io/gh/open-telemetry/opentelemetry-go?branch=main) [![PkgGoDev](https://pkg.go.dev/badge/go.opentelemetry.io/otel)](https://pkg.go.dev/go.opentelemetry.io/otel) [![Go Report Card](https://goreportcard.com/badge/go.opentelemetry.io/otel)](https://goreportcard.com/report/go.opentelemetry.io/otel) +[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/open-telemetry/opentelemetry-go/badge)](https://scorecard.dev/viewer/?uri=github.com/open-telemetry/opentelemetry-go) +[![OpenSSF Best Practices](https://www.bestpractices.dev/projects/9996/badge)](https://www.bestpractices.dev/projects/9996) [![Slack](https://img.shields.io/badge/slack-@cncf/otel--go-brightgreen.svg?logo=slack)](https://cloud-native.slack.com/archives/C01NPAXACKT) OpenTelemetry-Go is the [Go](https://golang.org/) implementation of [OpenTelemetry](https://opentelemetry.io/). @@ -49,18 +51,25 @@ Currently, this project supports the following environments. | OS | Go Version | Architecture | |----------|------------|--------------| +| Ubuntu | 1.24 | amd64 | | Ubuntu | 1.23 | amd64 | | Ubuntu | 1.22 | amd64 | +| Ubuntu | 1.24 | 386 | | Ubuntu | 1.23 | 386 | | Ubuntu | 1.22 | 386 | -| Linux | 1.23 | arm64 | -| Linux | 1.22 | arm64 | +| Ubuntu | 1.24 | arm64 | +| Ubuntu | 1.23 | arm64 | +| Ubuntu | 1.22 | arm64 | +| macOS 13 | 1.24 | amd64 | | macOS 13 | 1.23 | amd64 | | macOS 13 | 1.22 | amd64 | +| macOS | 1.24 | arm64 | | macOS | 1.23 | arm64 | | macOS | 1.22 | arm64 | +| Windows | 1.24 | amd64 | | Windows | 1.23 | amd64 | | Windows | 1.22 | amd64 | +| Windows | 1.24 | 386 | | Windows | 1.23 | 386 | | Windows | 1.22 | 386 | diff --git a/vendor/go.opentelemetry.io/otel/RELEASING.md b/vendor/go.opentelemetry.io/otel/RELEASING.md index 4ebef4f9dd..1e13ae54f7 100644 --- a/vendor/go.opentelemetry.io/otel/RELEASING.md +++ b/vendor/go.opentelemetry.io/otel/RELEASING.md @@ -5,17 +5,14 @@ New versions of the [OpenTelemetry Semantic Conventions] mean new versions of the `semconv` package need to be generated. The `semconv-generate` make target is used for this. -1. Checkout a local copy of the [OpenTelemetry Semantic Conventions] to the desired release tag. -2. Pull the latest `otel/semconvgen` image: `docker pull otel/semconvgen:latest` -3. Run the `make semconv-generate ...` target from this repository. +1. Set the `TAG` environment variable to the semantic convention tag you want to generate. +2. Run the `make semconv-generate ...` target from this repository. For example, ```sh -export TAG="v1.21.0" # Change to the release version you are generating. -export OTEL_SEMCONV_REPO="/absolute/path/to/opentelemetry/semantic-conventions" -docker pull otel/semconvgen:latest -make semconv-generate # Uses the exported TAG and OTEL_SEMCONV_REPO. +export TAG="v1.30.0" # Change to the release version you are generating. +make semconv-generate # Uses the exported TAG. ``` This should create a new sub-package of [`semconv`](./semconv). diff --git a/vendor/go.opentelemetry.io/otel/dependencies.Dockerfile b/vendor/go.opentelemetry.io/otel/dependencies.Dockerfile new file mode 100644 index 0000000000..e4c4a753c8 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/dependencies.Dockerfile @@ -0,0 +1,3 @@ +# This is a renovate-friendly source of Docker images. +FROM python:3.13.2-slim-bullseye@sha256:31b581c8218e1f3c58672481b3b7dba8e898852866b408c6a984c22832523935 AS python +FROM otel/weaver:v0.13.2@sha256:ae7346b992e477f629ea327e0979e8a416a97f7956ab1f7e95ac1f44edf1a893 AS weaver diff --git a/vendor/go.opentelemetry.io/otel/renovate.json b/vendor/go.opentelemetry.io/otel/renovate.json index 4f80c898a1..a6fa353f95 100644 --- a/vendor/go.opentelemetry.io/otel/renovate.json +++ b/vendor/go.opentelemetry.io/otel/renovate.json @@ -1,7 +1,7 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ - "config:recommended" + "config:best-practices" ], "ignorePaths": [], "labels": ["Skip Changelog", "dependencies"], @@ -14,6 +14,10 @@ "matchDepTypes": ["indirect"], "enabled": true }, + { + "matchPackageNames": ["go.opentelemetry.io/build-tools/**"], + "groupName": "build-tools" + }, { "matchPackageNames": ["google.golang.org/genproto/googleapis/**"], "groupName": "googleapis" diff --git a/vendor/go.opentelemetry.io/otel/requirements.txt b/vendor/go.opentelemetry.io/otel/requirements.txt index ab09daf9d5..1bb55fb1cc 100644 --- a/vendor/go.opentelemetry.io/otel/requirements.txt +++ b/vendor/go.opentelemetry.io/otel/requirements.txt @@ -1 +1 @@ -codespell==2.3.0 +codespell==2.4.1 diff --git a/vendor/go.opentelemetry.io/otel/trace/auto.go b/vendor/go.opentelemetry.io/otel/trace/auto.go new file mode 100644 index 0000000000..7e2910025a --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/trace/auto.go @@ -0,0 +1,661 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package trace // import "go.opentelemetry.io/otel/trace" + +import ( + "context" + "encoding/json" + "fmt" + "math" + "os" + "reflect" + "runtime" + "strconv" + "strings" + "sync" + "sync/atomic" + "time" + "unicode/utf8" + + "go.opentelemetry.io/otel/attribute" + "go.opentelemetry.io/otel/codes" + semconv "go.opentelemetry.io/otel/semconv/v1.26.0" + "go.opentelemetry.io/otel/trace/embedded" + "go.opentelemetry.io/otel/trace/internal/telemetry" +) + +// newAutoTracerProvider returns an auto-instrumentable [trace.TracerProvider]. +// If an [go.opentelemetry.io/auto.Instrumentation] is configured to instrument +// the process using the returned TracerProvider, all of the telemetry it +// produces will be processed and handled by that Instrumentation. By default, +// if no Instrumentation instruments the TracerProvider it will not generate +// any trace telemetry. +func newAutoTracerProvider() TracerProvider { return tracerProviderInstance } + +var tracerProviderInstance = new(autoTracerProvider) + +type autoTracerProvider struct{ embedded.TracerProvider } + +var _ TracerProvider = autoTracerProvider{} + +func (p autoTracerProvider) Tracer(name string, opts ...TracerOption) Tracer { + cfg := NewTracerConfig(opts...) + return autoTracer{ + name: name, + version: cfg.InstrumentationVersion(), + schemaURL: cfg.SchemaURL(), + } +} + +type autoTracer struct { + embedded.Tracer + + name, schemaURL, version string +} + +var _ Tracer = autoTracer{} + +func (t autoTracer) Start(ctx context.Context, name string, opts ...SpanStartOption) (context.Context, Span) { + var psc SpanContext + sampled := true + span := new(autoSpan) + + // Ask eBPF for sampling decision and span context info. + t.start(ctx, span, &psc, &sampled, &span.spanContext) + + span.sampled.Store(sampled) + + ctx = ContextWithSpan(ctx, span) + + if sampled { + // Only build traces if sampled. + cfg := NewSpanStartConfig(opts...) + span.traces, span.span = t.traces(name, cfg, span.spanContext, psc) + } + + return ctx, span +} + +// Expected to be implemented in eBPF. +// +//go:noinline +func (t *autoTracer) start( + ctx context.Context, + spanPtr *autoSpan, + psc *SpanContext, + sampled *bool, + sc *SpanContext, +) { + start(ctx, spanPtr, psc, sampled, sc) +} + +// start is used for testing. +var start = func(context.Context, *autoSpan, *SpanContext, *bool, *SpanContext) {} + +func (t autoTracer) traces(name string, cfg SpanConfig, sc, psc SpanContext) (*telemetry.Traces, *telemetry.Span) { + span := &telemetry.Span{ + TraceID: telemetry.TraceID(sc.TraceID()), + SpanID: telemetry.SpanID(sc.SpanID()), + Flags: uint32(sc.TraceFlags()), + TraceState: sc.TraceState().String(), + ParentSpanID: telemetry.SpanID(psc.SpanID()), + Name: name, + Kind: spanKind(cfg.SpanKind()), + } + + span.Attrs, span.DroppedAttrs = convCappedAttrs(maxSpan.Attrs, cfg.Attributes()) + + links := cfg.Links() + if limit := maxSpan.Links; limit == 0 { + n := int64(len(links)) + if n > 0 { + span.DroppedLinks = uint32(min(n, math.MaxUint32)) // nolint: gosec // Bounds checked. + } + } else { + if limit > 0 { + n := int64(max(len(links)-limit, 0)) + span.DroppedLinks = uint32(min(n, math.MaxUint32)) // nolint: gosec // Bounds checked. + links = links[n:] + } + span.Links = convLinks(links) + } + + if t := cfg.Timestamp(); !t.IsZero() { + span.StartTime = cfg.Timestamp() + } else { + span.StartTime = time.Now() + } + + return &telemetry.Traces{ + ResourceSpans: []*telemetry.ResourceSpans{ + { + ScopeSpans: []*telemetry.ScopeSpans{ + { + Scope: &telemetry.Scope{ + Name: t.name, + Version: t.version, + }, + Spans: []*telemetry.Span{span}, + SchemaURL: t.schemaURL, + }, + }, + }, + }, + }, span +} + +func spanKind(kind SpanKind) telemetry.SpanKind { + switch kind { + case SpanKindInternal: + return telemetry.SpanKindInternal + case SpanKindServer: + return telemetry.SpanKindServer + case SpanKindClient: + return telemetry.SpanKindClient + case SpanKindProducer: + return telemetry.SpanKindProducer + case SpanKindConsumer: + return telemetry.SpanKindConsumer + } + return telemetry.SpanKind(0) // undefined. +} + +type autoSpan struct { + embedded.Span + + spanContext SpanContext + sampled atomic.Bool + + mu sync.Mutex + traces *telemetry.Traces + span *telemetry.Span +} + +func (s *autoSpan) SpanContext() SpanContext { + if s == nil { + return SpanContext{} + } + // s.spanContext is immutable, do not acquire lock s.mu. + return s.spanContext +} + +func (s *autoSpan) IsRecording() bool { + if s == nil { + return false + } + + return s.sampled.Load() +} + +func (s *autoSpan) SetStatus(c codes.Code, msg string) { + if s == nil || !s.sampled.Load() { + return + } + + s.mu.Lock() + defer s.mu.Unlock() + + if s.span.Status == nil { + s.span.Status = new(telemetry.Status) + } + + s.span.Status.Message = msg + + switch c { + case codes.Unset: + s.span.Status.Code = telemetry.StatusCodeUnset + case codes.Error: + s.span.Status.Code = telemetry.StatusCodeError + case codes.Ok: + s.span.Status.Code = telemetry.StatusCodeOK + } +} + +func (s *autoSpan) SetAttributes(attrs ...attribute.KeyValue) { + if s == nil || !s.sampled.Load() { + return + } + + s.mu.Lock() + defer s.mu.Unlock() + + limit := maxSpan.Attrs + if limit == 0 { + // No attributes allowed. + n := int64(len(attrs)) + if n > 0 { + s.span.DroppedAttrs += uint32(min(n, math.MaxUint32)) // nolint: gosec // Bounds checked. + } + return + } + + m := make(map[string]int) + for i, a := range s.span.Attrs { + m[a.Key] = i + } + + for _, a := range attrs { + val := convAttrValue(a.Value) + if val.Empty() { + s.span.DroppedAttrs++ + continue + } + + if idx, ok := m[string(a.Key)]; ok { + s.span.Attrs[idx] = telemetry.Attr{ + Key: string(a.Key), + Value: val, + } + } else if limit < 0 || len(s.span.Attrs) < limit { + s.span.Attrs = append(s.span.Attrs, telemetry.Attr{ + Key: string(a.Key), + Value: val, + }) + m[string(a.Key)] = len(s.span.Attrs) - 1 + } else { + s.span.DroppedAttrs++ + } + } +} + +// convCappedAttrs converts up to limit attrs into a []telemetry.Attr. The +// number of dropped attributes is also returned. +func convCappedAttrs(limit int, attrs []attribute.KeyValue) ([]telemetry.Attr, uint32) { + n := len(attrs) + if limit == 0 { + var out uint32 + if n > 0 { + out = uint32(min(int64(n), math.MaxUint32)) // nolint: gosec // Bounds checked. + } + return nil, out + } + + if limit < 0 { + // Unlimited. + return convAttrs(attrs), 0 + } + + if n < 0 { + n = 0 + } + + limit = min(n, limit) + return convAttrs(attrs[:limit]), uint32(n - limit) // nolint: gosec // Bounds checked. +} + +func convAttrs(attrs []attribute.KeyValue) []telemetry.Attr { + if len(attrs) == 0 { + // Avoid allocations if not necessary. + return nil + } + + out := make([]telemetry.Attr, 0, len(attrs)) + for _, attr := range attrs { + key := string(attr.Key) + val := convAttrValue(attr.Value) + if val.Empty() { + continue + } + out = append(out, telemetry.Attr{Key: key, Value: val}) + } + return out +} + +func convAttrValue(value attribute.Value) telemetry.Value { + switch value.Type() { + case attribute.BOOL: + return telemetry.BoolValue(value.AsBool()) + case attribute.INT64: + return telemetry.Int64Value(value.AsInt64()) + case attribute.FLOAT64: + return telemetry.Float64Value(value.AsFloat64()) + case attribute.STRING: + v := truncate(maxSpan.AttrValueLen, value.AsString()) + return telemetry.StringValue(v) + case attribute.BOOLSLICE: + slice := value.AsBoolSlice() + out := make([]telemetry.Value, 0, len(slice)) + for _, v := range slice { + out = append(out, telemetry.BoolValue(v)) + } + return telemetry.SliceValue(out...) + case attribute.INT64SLICE: + slice := value.AsInt64Slice() + out := make([]telemetry.Value, 0, len(slice)) + for _, v := range slice { + out = append(out, telemetry.Int64Value(v)) + } + return telemetry.SliceValue(out...) + case attribute.FLOAT64SLICE: + slice := value.AsFloat64Slice() + out := make([]telemetry.Value, 0, len(slice)) + for _, v := range slice { + out = append(out, telemetry.Float64Value(v)) + } + return telemetry.SliceValue(out...) + case attribute.STRINGSLICE: + slice := value.AsStringSlice() + out := make([]telemetry.Value, 0, len(slice)) + for _, v := range slice { + v = truncate(maxSpan.AttrValueLen, v) + out = append(out, telemetry.StringValue(v)) + } + return telemetry.SliceValue(out...) + } + return telemetry.Value{} +} + +// truncate returns a truncated version of s such that it contains less than +// the limit number of characters. Truncation is applied by returning the limit +// number of valid characters contained in s. +// +// If limit is negative, it returns the original string. +// +// UTF-8 is supported. When truncating, all invalid characters are dropped +// before applying truncation. +// +// If s already contains less than the limit number of bytes, it is returned +// unchanged. No invalid characters are removed. +func truncate(limit int, s string) string { + // This prioritize performance in the following order based on the most + // common expected use-cases. + // + // - Short values less than the default limit (128). + // - Strings with valid encodings that exceed the limit. + // - No limit. + // - Strings with invalid encodings that exceed the limit. + if limit < 0 || len(s) <= limit { + return s + } + + // Optimistically, assume all valid UTF-8. + var b strings.Builder + count := 0 + for i, c := range s { + if c != utf8.RuneError { + count++ + if count > limit { + return s[:i] + } + continue + } + + _, size := utf8.DecodeRuneInString(s[i:]) + if size == 1 { + // Invalid encoding. + b.Grow(len(s) - 1) + _, _ = b.WriteString(s[:i]) + s = s[i:] + break + } + } + + // Fast-path, no invalid input. + if b.Cap() == 0 { + return s + } + + // Truncate while validating UTF-8. + for i := 0; i < len(s) && count < limit; { + c := s[i] + if c < utf8.RuneSelf { + // Optimization for single byte runes (common case). + _ = b.WriteByte(c) + i++ + count++ + continue + } + + _, size := utf8.DecodeRuneInString(s[i:]) + if size == 1 { + // We checked for all 1-byte runes above, this is a RuneError. + i++ + continue + } + + _, _ = b.WriteString(s[i : i+size]) + i += size + count++ + } + + return b.String() +} + +func (s *autoSpan) End(opts ...SpanEndOption) { + if s == nil || !s.sampled.Swap(false) { + return + } + + // s.end exists so the lock (s.mu) is not held while s.ended is called. + s.ended(s.end(opts)) +} + +func (s *autoSpan) end(opts []SpanEndOption) []byte { + s.mu.Lock() + defer s.mu.Unlock() + + cfg := NewSpanEndConfig(opts...) + if t := cfg.Timestamp(); !t.IsZero() { + s.span.EndTime = cfg.Timestamp() + } else { + s.span.EndTime = time.Now() + } + + b, _ := json.Marshal(s.traces) // TODO: do not ignore this error. + return b +} + +// Expected to be implemented in eBPF. +// +//go:noinline +func (*autoSpan) ended(buf []byte) { ended(buf) } + +// ended is used for testing. +var ended = func([]byte) {} + +func (s *autoSpan) RecordError(err error, opts ...EventOption) { + if s == nil || err == nil || !s.sampled.Load() { + return + } + + cfg := NewEventConfig(opts...) + + attrs := cfg.Attributes() + attrs = append(attrs, + semconv.ExceptionType(typeStr(err)), + semconv.ExceptionMessage(err.Error()), + ) + if cfg.StackTrace() { + buf := make([]byte, 2048) + n := runtime.Stack(buf, false) + attrs = append(attrs, semconv.ExceptionStacktrace(string(buf[0:n]))) + } + + s.mu.Lock() + defer s.mu.Unlock() + + s.addEvent(semconv.ExceptionEventName, cfg.Timestamp(), attrs) +} + +func typeStr(i any) string { + t := reflect.TypeOf(i) + if t.PkgPath() == "" && t.Name() == "" { + // Likely a builtin type. + return t.String() + } + return fmt.Sprintf("%s.%s", t.PkgPath(), t.Name()) +} + +func (s *autoSpan) AddEvent(name string, opts ...EventOption) { + if s == nil || !s.sampled.Load() { + return + } + + cfg := NewEventConfig(opts...) + + s.mu.Lock() + defer s.mu.Unlock() + + s.addEvent(name, cfg.Timestamp(), cfg.Attributes()) +} + +// addEvent adds an event with name and attrs at tStamp to the span. The span +// lock (s.mu) needs to be held by the caller. +func (s *autoSpan) addEvent(name string, tStamp time.Time, attrs []attribute.KeyValue) { + limit := maxSpan.Events + + if limit == 0 { + s.span.DroppedEvents++ + return + } + + if limit > 0 && len(s.span.Events) == limit { + // Drop head while avoiding allocation of more capacity. + copy(s.span.Events[:limit-1], s.span.Events[1:]) + s.span.Events = s.span.Events[:limit-1] + s.span.DroppedEvents++ + } + + e := &telemetry.SpanEvent{Time: tStamp, Name: name} + e.Attrs, e.DroppedAttrs = convCappedAttrs(maxSpan.EventAttrs, attrs) + + s.span.Events = append(s.span.Events, e) +} + +func (s *autoSpan) AddLink(link Link) { + if s == nil || !s.sampled.Load() { + return + } + + l := maxSpan.Links + + s.mu.Lock() + defer s.mu.Unlock() + + if l == 0 { + s.span.DroppedLinks++ + return + } + + if l > 0 && len(s.span.Links) == l { + // Drop head while avoiding allocation of more capacity. + copy(s.span.Links[:l-1], s.span.Links[1:]) + s.span.Links = s.span.Links[:l-1] + s.span.DroppedLinks++ + } + + s.span.Links = append(s.span.Links, convLink(link)) +} + +func convLinks(links []Link) []*telemetry.SpanLink { + out := make([]*telemetry.SpanLink, 0, len(links)) + for _, link := range links { + out = append(out, convLink(link)) + } + return out +} + +func convLink(link Link) *telemetry.SpanLink { + l := &telemetry.SpanLink{ + TraceID: telemetry.TraceID(link.SpanContext.TraceID()), + SpanID: telemetry.SpanID(link.SpanContext.SpanID()), + TraceState: link.SpanContext.TraceState().String(), + Flags: uint32(link.SpanContext.TraceFlags()), + } + l.Attrs, l.DroppedAttrs = convCappedAttrs(maxSpan.LinkAttrs, link.Attributes) + + return l +} + +func (s *autoSpan) SetName(name string) { + if s == nil || !s.sampled.Load() { + return + } + + s.mu.Lock() + defer s.mu.Unlock() + + s.span.Name = name +} + +func (*autoSpan) TracerProvider() TracerProvider { return newAutoTracerProvider() } + +// maxSpan are the span limits resolved during startup. +var maxSpan = newSpanLimits() + +type spanLimits struct { + // Attrs is the number of allowed attributes for a span. + // + // This is resolved from the environment variable value for the + // OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT key if it exists. Otherwise, the + // environment variable value for OTEL_ATTRIBUTE_COUNT_LIMIT, or 128 if + // that is not set, is used. + Attrs int + // AttrValueLen is the maximum attribute value length allowed for a span. + // + // This is resolved from the environment variable value for the + // OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT key if it exists. Otherwise, the + // environment variable value for OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT, or -1 + // if that is not set, is used. + AttrValueLen int + // Events is the number of allowed events for a span. + // + // This is resolved from the environment variable value for the + // OTEL_SPAN_EVENT_COUNT_LIMIT key, or 128 is used if that is not set. + Events int + // EventAttrs is the number of allowed attributes for a span event. + // + // The is resolved from the environment variable value for the + // OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT key, or 128 is used if that is not set. + EventAttrs int + // Links is the number of allowed Links for a span. + // + // This is resolved from the environment variable value for the + // OTEL_SPAN_LINK_COUNT_LIMIT, or 128 is used if that is not set. + Links int + // LinkAttrs is the number of allowed attributes for a span link. + // + // This is resolved from the environment variable value for the + // OTEL_LINK_ATTRIBUTE_COUNT_LIMIT, or 128 is used if that is not set. + LinkAttrs int +} + +func newSpanLimits() spanLimits { + return spanLimits{ + Attrs: firstEnv( + 128, + "OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT", + "OTEL_ATTRIBUTE_COUNT_LIMIT", + ), + AttrValueLen: firstEnv( + -1, // Unlimited. + "OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT", + "OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT", + ), + Events: firstEnv(128, "OTEL_SPAN_EVENT_COUNT_LIMIT"), + EventAttrs: firstEnv(128, "OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT"), + Links: firstEnv(128, "OTEL_SPAN_LINK_COUNT_LIMIT"), + LinkAttrs: firstEnv(128, "OTEL_LINK_ATTRIBUTE_COUNT_LIMIT"), + } +} + +// firstEnv returns the parsed integer value of the first matching environment +// variable from keys. The defaultVal is returned if the value is not an +// integer or no match is found. +func firstEnv(defaultVal int, keys ...string) int { + for _, key := range keys { + strV := os.Getenv(key) + if strV == "" { + continue + } + + v, err := strconv.Atoi(strV) + if err == nil { + return v + } + // Ignore invalid environment variable. + } + + return defaultVal +} diff --git a/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/attr.go b/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/attr.go new file mode 100644 index 0000000000..f663547b4e --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/attr.go @@ -0,0 +1,58 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package telemetry // import "go.opentelemetry.io/otel/trace/internal/telemetry" + +// Attr is a key-value pair. +type Attr struct { + Key string `json:"key,omitempty"` + Value Value `json:"value,omitempty"` +} + +// String returns an Attr for a string value. +func String(key, value string) Attr { + return Attr{key, StringValue(value)} +} + +// Int64 returns an Attr for an int64 value. +func Int64(key string, value int64) Attr { + return Attr{key, Int64Value(value)} +} + +// Int returns an Attr for an int value. +func Int(key string, value int) Attr { + return Int64(key, int64(value)) +} + +// Float64 returns an Attr for a float64 value. +func Float64(key string, value float64) Attr { + return Attr{key, Float64Value(value)} +} + +// Bool returns an Attr for a bool value. +func Bool(key string, value bool) Attr { + return Attr{key, BoolValue(value)} +} + +// Bytes returns an Attr for a []byte value. +// The passed slice must not be changed after it is passed. +func Bytes(key string, value []byte) Attr { + return Attr{key, BytesValue(value)} +} + +// Slice returns an Attr for a []Value value. +// The passed slice must not be changed after it is passed. +func Slice(key string, value ...Value) Attr { + return Attr{key, SliceValue(value...)} +} + +// Map returns an Attr for a map value. +// The passed slice must not be changed after it is passed. +func Map(key string, value ...Attr) Attr { + return Attr{key, MapValue(value...)} +} + +// Equal returns if a is equal to b. +func (a Attr) Equal(b Attr) bool { + return a.Key == b.Key && a.Value.Equal(b.Value) +} diff --git a/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/doc.go b/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/doc.go new file mode 100644 index 0000000000..5debe90bbb --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/doc.go @@ -0,0 +1,8 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +/* +Package telemetry provides a lightweight representations of OpenTelemetry +telemetry that is compatible with the OTLP JSON protobuf encoding. +*/ +package telemetry // import "go.opentelemetry.io/otel/trace/internal/telemetry" diff --git a/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/id.go b/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/id.go new file mode 100644 index 0000000000..7b1ae3c4ea --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/id.go @@ -0,0 +1,103 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package telemetry // import "go.opentelemetry.io/otel/trace/internal/telemetry" + +import ( + "encoding/hex" + "errors" + "fmt" +) + +const ( + traceIDSize = 16 + spanIDSize = 8 +) + +// TraceID is a custom data type that is used for all trace IDs. +type TraceID [traceIDSize]byte + +// String returns the hex string representation form of a TraceID. +func (tid TraceID) String() string { + return hex.EncodeToString(tid[:]) +} + +// IsEmpty returns false if id contains at least one non-zero byte. +func (tid TraceID) IsEmpty() bool { + return tid == [traceIDSize]byte{} +} + +// MarshalJSON converts the trace ID into a hex string enclosed in quotes. +func (tid TraceID) MarshalJSON() ([]byte, error) { + if tid.IsEmpty() { + return []byte(`""`), nil + } + return marshalJSON(tid[:]) +} + +// UnmarshalJSON inflates the trace ID from hex string, possibly enclosed in +// quotes. +func (tid *TraceID) UnmarshalJSON(data []byte) error { + *tid = [traceIDSize]byte{} + return unmarshalJSON(tid[:], data) +} + +// SpanID is a custom data type that is used for all span IDs. +type SpanID [spanIDSize]byte + +// String returns the hex string representation form of a SpanID. +func (sid SpanID) String() string { + return hex.EncodeToString(sid[:]) +} + +// IsEmpty returns true if the span ID contains at least one non-zero byte. +func (sid SpanID) IsEmpty() bool { + return sid == [spanIDSize]byte{} +} + +// MarshalJSON converts span ID into a hex string enclosed in quotes. +func (sid SpanID) MarshalJSON() ([]byte, error) { + if sid.IsEmpty() { + return []byte(`""`), nil + } + return marshalJSON(sid[:]) +} + +// UnmarshalJSON decodes span ID from hex string, possibly enclosed in quotes. +func (sid *SpanID) UnmarshalJSON(data []byte) error { + *sid = [spanIDSize]byte{} + return unmarshalJSON(sid[:], data) +} + +// marshalJSON converts id into a hex string enclosed in quotes. +func marshalJSON(id []byte) ([]byte, error) { + // Plus 2 quote chars at the start and end. + hexLen := hex.EncodedLen(len(id)) + 2 + + b := make([]byte, hexLen) + hex.Encode(b[1:hexLen-1], id) + b[0], b[hexLen-1] = '"', '"' + + return b, nil +} + +// unmarshalJSON inflates trace id from hex string, possibly enclosed in quotes. +func unmarshalJSON(dst []byte, src []byte) error { + if l := len(src); l >= 2 && src[0] == '"' && src[l-1] == '"' { + src = src[1 : l-1] + } + nLen := len(src) + if nLen == 0 { + return nil + } + + if len(dst) != hex.DecodedLen(nLen) { + return errors.New("invalid length for ID") + } + + _, err := hex.Decode(dst, src) + if err != nil { + return fmt.Errorf("cannot unmarshal ID from string '%s': %w", string(src), err) + } + return nil +} diff --git a/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/number.go b/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/number.go new file mode 100644 index 0000000000..f5e3a8cec9 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/number.go @@ -0,0 +1,67 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package telemetry // import "go.opentelemetry.io/otel/trace/internal/telemetry" + +import ( + "encoding/json" + "strconv" +) + +// protoInt64 represents the protobuf encoding of integers which can be either +// strings or integers. +type protoInt64 int64 + +// Int64 returns the protoInt64 as an int64. +func (i *protoInt64) Int64() int64 { return int64(*i) } + +// UnmarshalJSON decodes both strings and integers. +func (i *protoInt64) UnmarshalJSON(data []byte) error { + if data[0] == '"' { + var str string + if err := json.Unmarshal(data, &str); err != nil { + return err + } + parsedInt, err := strconv.ParseInt(str, 10, 64) + if err != nil { + return err + } + *i = protoInt64(parsedInt) + } else { + var parsedInt int64 + if err := json.Unmarshal(data, &parsedInt); err != nil { + return err + } + *i = protoInt64(parsedInt) + } + return nil +} + +// protoUint64 represents the protobuf encoding of integers which can be either +// strings or integers. +type protoUint64 uint64 + +// Int64 returns the protoUint64 as a uint64. +func (i *protoUint64) Uint64() uint64 { return uint64(*i) } + +// UnmarshalJSON decodes both strings and integers. +func (i *protoUint64) UnmarshalJSON(data []byte) error { + if data[0] == '"' { + var str string + if err := json.Unmarshal(data, &str); err != nil { + return err + } + parsedUint, err := strconv.ParseUint(str, 10, 64) + if err != nil { + return err + } + *i = protoUint64(parsedUint) + } else { + var parsedUint uint64 + if err := json.Unmarshal(data, &parsedUint); err != nil { + return err + } + *i = protoUint64(parsedUint) + } + return nil +} diff --git a/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/resource.go b/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/resource.go new file mode 100644 index 0000000000..1798a702d4 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/resource.go @@ -0,0 +1,66 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package telemetry // import "go.opentelemetry.io/otel/trace/internal/telemetry" + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" +) + +// Resource information. +type Resource struct { + // Attrs are the set of attributes that describe the resource. Attribute + // keys MUST be unique (it is not allowed to have more than one attribute + // with the same key). + Attrs []Attr `json:"attributes,omitempty"` + // DroppedAttrs is the number of dropped attributes. If the value + // is 0, then no attributes were dropped. + DroppedAttrs uint32 `json:"droppedAttributesCount,omitempty"` +} + +// UnmarshalJSON decodes the OTLP formatted JSON contained in data into r. +func (r *Resource) UnmarshalJSON(data []byte) error { + decoder := json.NewDecoder(bytes.NewReader(data)) + + t, err := decoder.Token() + if err != nil { + return err + } + if t != json.Delim('{') { + return errors.New("invalid Resource type") + } + + for decoder.More() { + keyIface, err := decoder.Token() + if err != nil { + if errors.Is(err, io.EOF) { + // Empty. + return nil + } + return err + } + + key, ok := keyIface.(string) + if !ok { + return fmt.Errorf("invalid Resource field: %#v", keyIface) + } + + switch key { + case "attributes": + err = decoder.Decode(&r.Attrs) + case "droppedAttributesCount", "dropped_attributes_count": + err = decoder.Decode(&r.DroppedAttrs) + default: + // Skip unknown. + } + + if err != nil { + return err + } + } + return nil +} diff --git a/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/scope.go b/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/scope.go new file mode 100644 index 0000000000..c2b4c635b7 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/scope.go @@ -0,0 +1,67 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package telemetry // import "go.opentelemetry.io/otel/trace/internal/telemetry" + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" +) + +// Scope is the identifying values of the instrumentation scope. +type Scope struct { + Name string `json:"name,omitempty"` + Version string `json:"version,omitempty"` + Attrs []Attr `json:"attributes,omitempty"` + DroppedAttrs uint32 `json:"droppedAttributesCount,omitempty"` +} + +// UnmarshalJSON decodes the OTLP formatted JSON contained in data into r. +func (s *Scope) UnmarshalJSON(data []byte) error { + decoder := json.NewDecoder(bytes.NewReader(data)) + + t, err := decoder.Token() + if err != nil { + return err + } + if t != json.Delim('{') { + return errors.New("invalid Scope type") + } + + for decoder.More() { + keyIface, err := decoder.Token() + if err != nil { + if errors.Is(err, io.EOF) { + // Empty. + return nil + } + return err + } + + key, ok := keyIface.(string) + if !ok { + return fmt.Errorf("invalid Scope field: %#v", keyIface) + } + + switch key { + case "name": + err = decoder.Decode(&s.Name) + case "version": + err = decoder.Decode(&s.Version) + case "attributes": + err = decoder.Decode(&s.Attrs) + case "droppedAttributesCount", "dropped_attributes_count": + err = decoder.Decode(&s.DroppedAttrs) + default: + // Skip unknown. + } + + if err != nil { + return err + } + } + return nil +} diff --git a/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/span.go b/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/span.go new file mode 100644 index 0000000000..3c5e1cdb1b --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/span.go @@ -0,0 +1,460 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package telemetry // import "go.opentelemetry.io/otel/trace/internal/telemetry" + +import ( + "bytes" + "encoding/hex" + "encoding/json" + "errors" + "fmt" + "io" + "math" + "time" +) + +// A Span represents a single operation performed by a single component of the +// system. +type Span struct { + // A unique identifier for a trace. All spans from the same trace share + // the same `trace_id`. The ID is a 16-byte array. An ID with all zeroes OR + // of length other than 16 bytes is considered invalid (empty string in OTLP/JSON + // is zero-length and thus is also invalid). + // + // This field is required. + TraceID TraceID `json:"traceId,omitempty"` + // A unique identifier for a span within a trace, assigned when the span + // is created. The ID is an 8-byte array. An ID with all zeroes OR of length + // other than 8 bytes is considered invalid (empty string in OTLP/JSON + // is zero-length and thus is also invalid). + // + // This field is required. + SpanID SpanID `json:"spanId,omitempty"` + // trace_state conveys information about request position in multiple distributed tracing graphs. + // It is a trace_state in w3c-trace-context format: https://www.w3.org/TR/trace-context/#tracestate-header + // See also https://github.com/w3c/distributed-tracing for more details about this field. + TraceState string `json:"traceState,omitempty"` + // The `span_id` of this span's parent span. If this is a root span, then this + // field must be empty. The ID is an 8-byte array. + ParentSpanID SpanID `json:"parentSpanId,omitempty"` + // Flags, a bit field. + // + // Bits 0-7 (8 least significant bits) are the trace flags as defined in W3C Trace + // Context specification. To read the 8-bit W3C trace flag, use + // `flags & SPAN_FLAGS_TRACE_FLAGS_MASK`. + // + // See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions. + // + // Bits 8 and 9 represent the 3 states of whether a span's parent + // is remote. The states are (unknown, is not remote, is remote). + // To read whether the value is known, use `(flags & SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0`. + // To read whether the span is remote, use `(flags & SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0`. + // + // When creating span messages, if the message is logically forwarded from another source + // with an equivalent flags fields (i.e., usually another OTLP span message), the field SHOULD + // be copied as-is. If creating from a source that does not have an equivalent flags field + // (such as a runtime representation of an OpenTelemetry span), the high 22 bits MUST + // be set to zero. + // Readers MUST NOT assume that bits 10-31 (22 most significant bits) will be zero. + // + // [Optional]. + Flags uint32 `json:"flags,omitempty"` + // A description of the span's operation. + // + // For example, the name can be a qualified method name or a file name + // and a line number where the operation is called. A best practice is to use + // the same display name at the same call point in an application. + // This makes it easier to correlate spans in different traces. + // + // This field is semantically required to be set to non-empty string. + // Empty value is equivalent to an unknown span name. + // + // This field is required. + Name string `json:"name"` + // Distinguishes between spans generated in a particular context. For example, + // two spans with the same name may be distinguished using `CLIENT` (caller) + // and `SERVER` (callee) to identify queueing latency associated with the span. + Kind SpanKind `json:"kind,omitempty"` + // start_time_unix_nano is the start time of the span. On the client side, this is the time + // kept by the local machine where the span execution starts. On the server side, this + // is the time when the server's application handler starts running. + // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. + // + // This field is semantically required and it is expected that end_time >= start_time. + StartTime time.Time `json:"startTimeUnixNano,omitempty"` + // end_time_unix_nano is the end time of the span. On the client side, this is the time + // kept by the local machine where the span execution ends. On the server side, this + // is the time when the server application handler stops running. + // Value is UNIX Epoch time in nanoseconds since 00:00:00 UTC on 1 January 1970. + // + // This field is semantically required and it is expected that end_time >= start_time. + EndTime time.Time `json:"endTimeUnixNano,omitempty"` + // attributes is a collection of key/value pairs. Note, global attributes + // like server name can be set using the resource API. Examples of attributes: + // + // "/http/user_agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/71.0.3578.98 Safari/537.36" + // "/http/server_latency": 300 + // "example.com/myattribute": true + // "example.com/score": 10.239 + // + // The OpenTelemetry API specification further restricts the allowed value types: + // https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/common/README.md#attribute + // Attribute keys MUST be unique (it is not allowed to have more than one + // attribute with the same key). + Attrs []Attr `json:"attributes,omitempty"` + // dropped_attributes_count is the number of attributes that were discarded. Attributes + // can be discarded because their keys are too long or because there are too many + // attributes. If this value is 0, then no attributes were dropped. + DroppedAttrs uint32 `json:"droppedAttributesCount,omitempty"` + // events is a collection of Event items. + Events []*SpanEvent `json:"events,omitempty"` + // dropped_events_count is the number of dropped events. If the value is 0, then no + // events were dropped. + DroppedEvents uint32 `json:"droppedEventsCount,omitempty"` + // links is a collection of Links, which are references from this span to a span + // in the same or different trace. + Links []*SpanLink `json:"links,omitempty"` + // dropped_links_count is the number of dropped links after the maximum size was + // enforced. If this value is 0, then no links were dropped. + DroppedLinks uint32 `json:"droppedLinksCount,omitempty"` + // An optional final status for this span. Semantically when Status isn't set, it means + // span's status code is unset, i.e. assume STATUS_CODE_UNSET (code = 0). + Status *Status `json:"status,omitempty"` +} + +// MarshalJSON encodes s into OTLP formatted JSON. +func (s Span) MarshalJSON() ([]byte, error) { + startT := s.StartTime.UnixNano() + if s.StartTime.IsZero() || startT < 0 { + startT = 0 + } + + endT := s.EndTime.UnixNano() + if s.EndTime.IsZero() || endT < 0 { + endT = 0 + } + + // Override non-empty default SpanID marshal and omitempty. + var parentSpanId string + if !s.ParentSpanID.IsEmpty() { + b := make([]byte, hex.EncodedLen(spanIDSize)) + hex.Encode(b, s.ParentSpanID[:]) + parentSpanId = string(b) + } + + type Alias Span + return json.Marshal(struct { + Alias + ParentSpanID string `json:"parentSpanId,omitempty"` + StartTime uint64 `json:"startTimeUnixNano,omitempty"` + EndTime uint64 `json:"endTimeUnixNano,omitempty"` + }{ + Alias: Alias(s), + ParentSpanID: parentSpanId, + StartTime: uint64(startT), // nolint:gosec // >0 checked above. + EndTime: uint64(endT), // nolint:gosec // >0 checked above. + }) +} + +// UnmarshalJSON decodes the OTLP formatted JSON contained in data into s. +func (s *Span) UnmarshalJSON(data []byte) error { + decoder := json.NewDecoder(bytes.NewReader(data)) + + t, err := decoder.Token() + if err != nil { + return err + } + if t != json.Delim('{') { + return errors.New("invalid Span type") + } + + for decoder.More() { + keyIface, err := decoder.Token() + if err != nil { + if errors.Is(err, io.EOF) { + // Empty. + return nil + } + return err + } + + key, ok := keyIface.(string) + if !ok { + return fmt.Errorf("invalid Span field: %#v", keyIface) + } + + switch key { + case "traceId", "trace_id": + err = decoder.Decode(&s.TraceID) + case "spanId", "span_id": + err = decoder.Decode(&s.SpanID) + case "traceState", "trace_state": + err = decoder.Decode(&s.TraceState) + case "parentSpanId", "parent_span_id": + err = decoder.Decode(&s.ParentSpanID) + case "flags": + err = decoder.Decode(&s.Flags) + case "name": + err = decoder.Decode(&s.Name) + case "kind": + err = decoder.Decode(&s.Kind) + case "startTimeUnixNano", "start_time_unix_nano": + var val protoUint64 + err = decoder.Decode(&val) + v := int64(min(val.Uint64(), math.MaxInt64)) // nolint: gosec // Overflow checked. + s.StartTime = time.Unix(0, v) + case "endTimeUnixNano", "end_time_unix_nano": + var val protoUint64 + err = decoder.Decode(&val) + v := int64(min(val.Uint64(), math.MaxInt64)) // nolint: gosec // Overflow checked. + s.EndTime = time.Unix(0, v) + case "attributes": + err = decoder.Decode(&s.Attrs) + case "droppedAttributesCount", "dropped_attributes_count": + err = decoder.Decode(&s.DroppedAttrs) + case "events": + err = decoder.Decode(&s.Events) + case "droppedEventsCount", "dropped_events_count": + err = decoder.Decode(&s.DroppedEvents) + case "links": + err = decoder.Decode(&s.Links) + case "droppedLinksCount", "dropped_links_count": + err = decoder.Decode(&s.DroppedLinks) + case "status": + err = decoder.Decode(&s.Status) + default: + // Skip unknown. + } + + if err != nil { + return err + } + } + return nil +} + +// SpanFlags represents constants used to interpret the +// Span.flags field, which is protobuf 'fixed32' type and is to +// be used as bit-fields. Each non-zero value defined in this enum is +// a bit-mask. To extract the bit-field, for example, use an +// expression like: +// +// (span.flags & SPAN_FLAGS_TRACE_FLAGS_MASK) +// +// See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions. +// +// Note that Span flags were introduced in version 1.1 of the +// OpenTelemetry protocol. Older Span producers do not set this +// field, consequently consumers should not rely on the absence of a +// particular flag bit to indicate the presence of a particular feature. +type SpanFlags int32 + +const ( + // Bits 0-7 are used for trace flags. + SpanFlagsTraceFlagsMask SpanFlags = 255 + // Bits 8 and 9 are used to indicate that the parent span or link span is remote. + // Bit 8 (`HAS_IS_REMOTE`) indicates whether the value is known. + // Bit 9 (`IS_REMOTE`) indicates whether the span or link is remote. + SpanFlagsContextHasIsRemoteMask SpanFlags = 256 + // SpanFlagsContextHasIsRemoteMask indicates the Span is remote. + SpanFlagsContextIsRemoteMask SpanFlags = 512 +) + +// SpanKind is the type of span. Can be used to specify additional relationships between spans +// in addition to a parent/child relationship. +type SpanKind int32 + +const ( + // Indicates that the span represents an internal operation within an application, + // as opposed to an operation happening at the boundaries. Default value. + SpanKindInternal SpanKind = 1 + // Indicates that the span covers server-side handling of an RPC or other + // remote network request. + SpanKindServer SpanKind = 2 + // Indicates that the span describes a request to some remote service. + SpanKindClient SpanKind = 3 + // Indicates that the span describes a producer sending a message to a broker. + // Unlike CLIENT and SERVER, there is often no direct critical path latency relationship + // between producer and consumer spans. A PRODUCER span ends when the message was accepted + // by the broker while the logical processing of the message might span a much longer time. + SpanKindProducer SpanKind = 4 + // Indicates that the span describes consumer receiving a message from a broker. + // Like the PRODUCER kind, there is often no direct critical path latency relationship + // between producer and consumer spans. + SpanKindConsumer SpanKind = 5 +) + +// Event is a time-stamped annotation of the span, consisting of user-supplied +// text description and key-value pairs. +type SpanEvent struct { + // time_unix_nano is the time the event occurred. + Time time.Time `json:"timeUnixNano,omitempty"` + // name of the event. + // This field is semantically required to be set to non-empty string. + Name string `json:"name,omitempty"` + // attributes is a collection of attribute key/value pairs on the event. + // Attribute keys MUST be unique (it is not allowed to have more than one + // attribute with the same key). + Attrs []Attr `json:"attributes,omitempty"` + // dropped_attributes_count is the number of dropped attributes. If the value is 0, + // then no attributes were dropped. + DroppedAttrs uint32 `json:"droppedAttributesCount,omitempty"` +} + +// MarshalJSON encodes e into OTLP formatted JSON. +func (e SpanEvent) MarshalJSON() ([]byte, error) { + t := e.Time.UnixNano() + if e.Time.IsZero() || t < 0 { + t = 0 + } + + type Alias SpanEvent + return json.Marshal(struct { + Alias + Time uint64 `json:"timeUnixNano,omitempty"` + }{ + Alias: Alias(e), + Time: uint64(t), // nolint: gosec // >0 checked above + }) +} + +// UnmarshalJSON decodes the OTLP formatted JSON contained in data into se. +func (se *SpanEvent) UnmarshalJSON(data []byte) error { + decoder := json.NewDecoder(bytes.NewReader(data)) + + t, err := decoder.Token() + if err != nil { + return err + } + if t != json.Delim('{') { + return errors.New("invalid SpanEvent type") + } + + for decoder.More() { + keyIface, err := decoder.Token() + if err != nil { + if errors.Is(err, io.EOF) { + // Empty. + return nil + } + return err + } + + key, ok := keyIface.(string) + if !ok { + return fmt.Errorf("invalid SpanEvent field: %#v", keyIface) + } + + switch key { + case "timeUnixNano", "time_unix_nano": + var val protoUint64 + err = decoder.Decode(&val) + v := int64(min(val.Uint64(), math.MaxInt64)) // nolint: gosec // Overflow checked. + se.Time = time.Unix(0, v) + case "name": + err = decoder.Decode(&se.Name) + case "attributes": + err = decoder.Decode(&se.Attrs) + case "droppedAttributesCount", "dropped_attributes_count": + err = decoder.Decode(&se.DroppedAttrs) + default: + // Skip unknown. + } + + if err != nil { + return err + } + } + return nil +} + +// A pointer from the current span to another span in the same trace or in a +// different trace. For example, this can be used in batching operations, +// where a single batch handler processes multiple requests from different +// traces or when the handler receives a request from a different project. +type SpanLink struct { + // A unique identifier of a trace that this linked span is part of. The ID is a + // 16-byte array. + TraceID TraceID `json:"traceId,omitempty"` + // A unique identifier for the linked span. The ID is an 8-byte array. + SpanID SpanID `json:"spanId,omitempty"` + // The trace_state associated with the link. + TraceState string `json:"traceState,omitempty"` + // attributes is a collection of attribute key/value pairs on the link. + // Attribute keys MUST be unique (it is not allowed to have more than one + // attribute with the same key). + Attrs []Attr `json:"attributes,omitempty"` + // dropped_attributes_count is the number of dropped attributes. If the value is 0, + // then no attributes were dropped. + DroppedAttrs uint32 `json:"droppedAttributesCount,omitempty"` + // Flags, a bit field. + // + // Bits 0-7 (8 least significant bits) are the trace flags as defined in W3C Trace + // Context specification. To read the 8-bit W3C trace flag, use + // `flags & SPAN_FLAGS_TRACE_FLAGS_MASK`. + // + // See https://www.w3.org/TR/trace-context-2/#trace-flags for the flag definitions. + // + // Bits 8 and 9 represent the 3 states of whether the link is remote. + // The states are (unknown, is not remote, is remote). + // To read whether the value is known, use `(flags & SPAN_FLAGS_CONTEXT_HAS_IS_REMOTE_MASK) != 0`. + // To read whether the link is remote, use `(flags & SPAN_FLAGS_CONTEXT_IS_REMOTE_MASK) != 0`. + // + // Readers MUST NOT assume that bits 10-31 (22 most significant bits) will be zero. + // When creating new spans, bits 10-31 (most-significant 22-bits) MUST be zero. + // + // [Optional]. + Flags uint32 `json:"flags,omitempty"` +} + +// UnmarshalJSON decodes the OTLP formatted JSON contained in data into sl. +func (sl *SpanLink) UnmarshalJSON(data []byte) error { + decoder := json.NewDecoder(bytes.NewReader(data)) + + t, err := decoder.Token() + if err != nil { + return err + } + if t != json.Delim('{') { + return errors.New("invalid SpanLink type") + } + + for decoder.More() { + keyIface, err := decoder.Token() + if err != nil { + if errors.Is(err, io.EOF) { + // Empty. + return nil + } + return err + } + + key, ok := keyIface.(string) + if !ok { + return fmt.Errorf("invalid SpanLink field: %#v", keyIface) + } + + switch key { + case "traceId", "trace_id": + err = decoder.Decode(&sl.TraceID) + case "spanId", "span_id": + err = decoder.Decode(&sl.SpanID) + case "traceState", "trace_state": + err = decoder.Decode(&sl.TraceState) + case "attributes": + err = decoder.Decode(&sl.Attrs) + case "droppedAttributesCount", "dropped_attributes_count": + err = decoder.Decode(&sl.DroppedAttrs) + case "flags": + err = decoder.Decode(&sl.Flags) + default: + // Skip unknown. + } + + if err != nil { + return err + } + } + return nil +} diff --git a/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/status.go b/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/status.go new file mode 100644 index 0000000000..1d013a8fa8 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/status.go @@ -0,0 +1,40 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package telemetry // import "go.opentelemetry.io/otel/trace/internal/telemetry" + +// For the semantics of status codes see +// https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/api.md#set-status +type StatusCode int32 + +const ( + // The default status. + StatusCodeUnset StatusCode = 0 + // The Span has been validated by an Application developer or Operator to + // have completed successfully. + StatusCodeOK StatusCode = 1 + // The Span contains an error. + StatusCodeError StatusCode = 2 +) + +var statusCodeStrings = []string{ + "Unset", + "OK", + "Error", +} + +func (s StatusCode) String() string { + if s >= 0 && int(s) < len(statusCodeStrings) { + return statusCodeStrings[s] + } + return "" +} + +// The Status type defines a logical error model that is suitable for different +// programming environments, including REST APIs and RPC APIs. +type Status struct { + // A developer-facing human readable error message. + Message string `json:"message,omitempty"` + // The status code. + Code StatusCode `json:"code,omitempty"` +} diff --git a/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/traces.go b/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/traces.go new file mode 100644 index 0000000000..b039407081 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/traces.go @@ -0,0 +1,189 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package telemetry // import "go.opentelemetry.io/otel/trace/internal/telemetry" + +import ( + "bytes" + "encoding/json" + "errors" + "fmt" + "io" +) + +// Traces represents the traces data that can be stored in a persistent storage, +// OR can be embedded by other protocols that transfer OTLP traces data but do +// not implement the OTLP protocol. +// +// The main difference between this message and collector protocol is that +// in this message there will not be any "control" or "metadata" specific to +// OTLP protocol. +// +// When new fields are added into this message, the OTLP request MUST be updated +// as well. +type Traces struct { + // An array of ResourceSpans. + // For data coming from a single resource this array will typically contain + // one element. Intermediary nodes that receive data from multiple origins + // typically batch the data before forwarding further and in that case this + // array will contain multiple elements. + ResourceSpans []*ResourceSpans `json:"resourceSpans,omitempty"` +} + +// UnmarshalJSON decodes the OTLP formatted JSON contained in data into td. +func (td *Traces) UnmarshalJSON(data []byte) error { + decoder := json.NewDecoder(bytes.NewReader(data)) + + t, err := decoder.Token() + if err != nil { + return err + } + if t != json.Delim('{') { + return errors.New("invalid TracesData type") + } + + for decoder.More() { + keyIface, err := decoder.Token() + if err != nil { + if errors.Is(err, io.EOF) { + // Empty. + return nil + } + return err + } + + key, ok := keyIface.(string) + if !ok { + return fmt.Errorf("invalid TracesData field: %#v", keyIface) + } + + switch key { + case "resourceSpans", "resource_spans": + err = decoder.Decode(&td.ResourceSpans) + default: + // Skip unknown. + } + + if err != nil { + return err + } + } + return nil +} + +// A collection of ScopeSpans from a Resource. +type ResourceSpans struct { + // The resource for the spans in this message. + // If this field is not set then no resource info is known. + Resource Resource `json:"resource"` + // A list of ScopeSpans that originate from a resource. + ScopeSpans []*ScopeSpans `json:"scopeSpans,omitempty"` + // This schema_url applies to the data in the "resource" field. It does not apply + // to the data in the "scope_spans" field which have their own schema_url field. + SchemaURL string `json:"schemaUrl,omitempty"` +} + +// UnmarshalJSON decodes the OTLP formatted JSON contained in data into rs. +func (rs *ResourceSpans) UnmarshalJSON(data []byte) error { + decoder := json.NewDecoder(bytes.NewReader(data)) + + t, err := decoder.Token() + if err != nil { + return err + } + if t != json.Delim('{') { + return errors.New("invalid ResourceSpans type") + } + + for decoder.More() { + keyIface, err := decoder.Token() + if err != nil { + if errors.Is(err, io.EOF) { + // Empty. + return nil + } + return err + } + + key, ok := keyIface.(string) + if !ok { + return fmt.Errorf("invalid ResourceSpans field: %#v", keyIface) + } + + switch key { + case "resource": + err = decoder.Decode(&rs.Resource) + case "scopeSpans", "scope_spans": + err = decoder.Decode(&rs.ScopeSpans) + case "schemaUrl", "schema_url": + err = decoder.Decode(&rs.SchemaURL) + default: + // Skip unknown. + } + + if err != nil { + return err + } + } + return nil +} + +// A collection of Spans produced by an InstrumentationScope. +type ScopeSpans struct { + // The instrumentation scope information for the spans in this message. + // Semantically when InstrumentationScope isn't set, it is equivalent with + // an empty instrumentation scope name (unknown). + Scope *Scope `json:"scope"` + // A list of Spans that originate from an instrumentation scope. + Spans []*Span `json:"spans,omitempty"` + // The Schema URL, if known. This is the identifier of the Schema that the span data + // is recorded in. To learn more about Schema URL see + // https://opentelemetry.io/docs/specs/otel/schemas/#schema-url + // This schema_url applies to all spans and span events in the "spans" field. + SchemaURL string `json:"schemaUrl,omitempty"` +} + +// UnmarshalJSON decodes the OTLP formatted JSON contained in data into ss. +func (ss *ScopeSpans) UnmarshalJSON(data []byte) error { + decoder := json.NewDecoder(bytes.NewReader(data)) + + t, err := decoder.Token() + if err != nil { + return err + } + if t != json.Delim('{') { + return errors.New("invalid ScopeSpans type") + } + + for decoder.More() { + keyIface, err := decoder.Token() + if err != nil { + if errors.Is(err, io.EOF) { + // Empty. + return nil + } + return err + } + + key, ok := keyIface.(string) + if !ok { + return fmt.Errorf("invalid ScopeSpans field: %#v", keyIface) + } + + switch key { + case "scope": + err = decoder.Decode(&ss.Scope) + case "spans": + err = decoder.Decode(&ss.Spans) + case "schemaUrl", "schema_url": + err = decoder.Decode(&ss.SchemaURL) + default: + // Skip unknown. + } + + if err != nil { + return err + } + } + return nil +} diff --git a/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/value.go b/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/value.go new file mode 100644 index 0000000000..7251492da0 --- /dev/null +++ b/vendor/go.opentelemetry.io/otel/trace/internal/telemetry/value.go @@ -0,0 +1,453 @@ +// Copyright The OpenTelemetry Authors +// SPDX-License-Identifier: Apache-2.0 + +package telemetry // import "go.opentelemetry.io/otel/trace/internal/telemetry" + +import ( + "bytes" + "cmp" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "io" + "math" + "slices" + "strconv" + "unsafe" +) + +// A Value represents a structured value. +// A zero value is valid and represents an empty value. +type Value struct { + // Ensure forward compatibility by explicitly making this not comparable. + noCmp [0]func() //nolint: unused // This is indeed used. + + // num holds the value for Int64, Float64, and Bool. It holds the length + // for String, Bytes, Slice, Map. + num uint64 + // any holds either the KindBool, KindInt64, KindFloat64, stringptr, + // bytesptr, sliceptr, or mapptr. If KindBool, KindInt64, or KindFloat64 + // then the value of Value is in num as described above. Otherwise, it + // contains the value wrapped in the appropriate type. + any any +} + +type ( + // sliceptr represents a value in Value.any for KindString Values. + stringptr *byte + // bytesptr represents a value in Value.any for KindBytes Values. + bytesptr *byte + // sliceptr represents a value in Value.any for KindSlice Values. + sliceptr *Value + // mapptr represents a value in Value.any for KindMap Values. + mapptr *Attr +) + +// ValueKind is the kind of a [Value]. +type ValueKind int + +// ValueKind values. +const ( + ValueKindEmpty ValueKind = iota + ValueKindBool + ValueKindFloat64 + ValueKindInt64 + ValueKindString + ValueKindBytes + ValueKindSlice + ValueKindMap +) + +var valueKindStrings = []string{ + "Empty", + "Bool", + "Float64", + "Int64", + "String", + "Bytes", + "Slice", + "Map", +} + +func (k ValueKind) String() string { + if k >= 0 && int(k) < len(valueKindStrings) { + return valueKindStrings[k] + } + return "" +} + +// StringValue returns a new [Value] for a string. +func StringValue(v string) Value { + return Value{ + num: uint64(len(v)), + any: stringptr(unsafe.StringData(v)), + } +} + +// IntValue returns a [Value] for an int. +func IntValue(v int) Value { return Int64Value(int64(v)) } + +// Int64Value returns a [Value] for an int64. +func Int64Value(v int64) Value { + return Value{ + num: uint64(v), // nolint: gosec // Store raw bytes. + any: ValueKindInt64, + } +} + +// Float64Value returns a [Value] for a float64. +func Float64Value(v float64) Value { + return Value{num: math.Float64bits(v), any: ValueKindFloat64} +} + +// BoolValue returns a [Value] for a bool. +func BoolValue(v bool) Value { //nolint:revive // Not a control flag. + var n uint64 + if v { + n = 1 + } + return Value{num: n, any: ValueKindBool} +} + +// BytesValue returns a [Value] for a byte slice. The passed slice must not be +// changed after it is passed. +func BytesValue(v []byte) Value { + return Value{ + num: uint64(len(v)), + any: bytesptr(unsafe.SliceData(v)), + } +} + +// SliceValue returns a [Value] for a slice of [Value]. The passed slice must +// not be changed after it is passed. +func SliceValue(vs ...Value) Value { + return Value{ + num: uint64(len(vs)), + any: sliceptr(unsafe.SliceData(vs)), + } +} + +// MapValue returns a new [Value] for a slice of key-value pairs. The passed +// slice must not be changed after it is passed. +func MapValue(kvs ...Attr) Value { + return Value{ + num: uint64(len(kvs)), + any: mapptr(unsafe.SliceData(kvs)), + } +} + +// AsString returns the value held by v as a string. +func (v Value) AsString() string { + if sp, ok := v.any.(stringptr); ok { + return unsafe.String(sp, v.num) + } + // TODO: error handle + return "" +} + +// asString returns the value held by v as a string. It will panic if the Value +// is not KindString. +func (v Value) asString() string { + return unsafe.String(v.any.(stringptr), v.num) +} + +// AsInt64 returns the value held by v as an int64. +func (v Value) AsInt64() int64 { + if v.Kind() != ValueKindInt64 { + // TODO: error handle + return 0 + } + return v.asInt64() +} + +// asInt64 returns the value held by v as an int64. If v is not of KindInt64, +// this will return garbage. +func (v Value) asInt64() int64 { + // Assumes v.num was a valid int64 (overflow not checked). + return int64(v.num) // nolint: gosec +} + +// AsBool returns the value held by v as a bool. +func (v Value) AsBool() bool { + if v.Kind() != ValueKindBool { + // TODO: error handle + return false + } + return v.asBool() +} + +// asBool returns the value held by v as a bool. If v is not of KindBool, this +// will return garbage. +func (v Value) asBool() bool { return v.num == 1 } + +// AsFloat64 returns the value held by v as a float64. +func (v Value) AsFloat64() float64 { + if v.Kind() != ValueKindFloat64 { + // TODO: error handle + return 0 + } + return v.asFloat64() +} + +// asFloat64 returns the value held by v as a float64. If v is not of +// KindFloat64, this will return garbage. +func (v Value) asFloat64() float64 { return math.Float64frombits(v.num) } + +// AsBytes returns the value held by v as a []byte. +func (v Value) AsBytes() []byte { + if sp, ok := v.any.(bytesptr); ok { + return unsafe.Slice((*byte)(sp), v.num) + } + // TODO: error handle + return nil +} + +// asBytes returns the value held by v as a []byte. It will panic if the Value +// is not KindBytes. +func (v Value) asBytes() []byte { + return unsafe.Slice((*byte)(v.any.(bytesptr)), v.num) +} + +// AsSlice returns the value held by v as a []Value. +func (v Value) AsSlice() []Value { + if sp, ok := v.any.(sliceptr); ok { + return unsafe.Slice((*Value)(sp), v.num) + } + // TODO: error handle + return nil +} + +// asSlice returns the value held by v as a []Value. It will panic if the Value +// is not KindSlice. +func (v Value) asSlice() []Value { + return unsafe.Slice((*Value)(v.any.(sliceptr)), v.num) +} + +// AsMap returns the value held by v as a []Attr. +func (v Value) AsMap() []Attr { + if sp, ok := v.any.(mapptr); ok { + return unsafe.Slice((*Attr)(sp), v.num) + } + // TODO: error handle + return nil +} + +// asMap returns the value held by v as a []Attr. It will panic if the +// Value is not KindMap. +func (v Value) asMap() []Attr { + return unsafe.Slice((*Attr)(v.any.(mapptr)), v.num) +} + +// Kind returns the Kind of v. +func (v Value) Kind() ValueKind { + switch x := v.any.(type) { + case ValueKind: + return x + case stringptr: + return ValueKindString + case bytesptr: + return ValueKindBytes + case sliceptr: + return ValueKindSlice + case mapptr: + return ValueKindMap + default: + return ValueKindEmpty + } +} + +// Empty returns if v does not hold any value. +func (v Value) Empty() bool { return v.Kind() == ValueKindEmpty } + +// Equal returns if v is equal to w. +func (v Value) Equal(w Value) bool { + k1 := v.Kind() + k2 := w.Kind() + if k1 != k2 { + return false + } + switch k1 { + case ValueKindInt64, ValueKindBool: + return v.num == w.num + case ValueKindString: + return v.asString() == w.asString() + case ValueKindFloat64: + return v.asFloat64() == w.asFloat64() + case ValueKindSlice: + return slices.EqualFunc(v.asSlice(), w.asSlice(), Value.Equal) + case ValueKindMap: + sv := sortMap(v.asMap()) + sw := sortMap(w.asMap()) + return slices.EqualFunc(sv, sw, Attr.Equal) + case ValueKindBytes: + return bytes.Equal(v.asBytes(), w.asBytes()) + case ValueKindEmpty: + return true + default: + // TODO: error handle + return false + } +} + +func sortMap(m []Attr) []Attr { + sm := make([]Attr, len(m)) + copy(sm, m) + slices.SortFunc(sm, func(a, b Attr) int { + return cmp.Compare(a.Key, b.Key) + }) + + return sm +} + +// String returns Value's value as a string, formatted like [fmt.Sprint]. +// +// The returned string is meant for debugging; +// the string representation is not stable. +func (v Value) String() string { + switch v.Kind() { + case ValueKindString: + return v.asString() + case ValueKindInt64: + // Assumes v.num was a valid int64 (overflow not checked). + return strconv.FormatInt(int64(v.num), 10) // nolint: gosec + case ValueKindFloat64: + return strconv.FormatFloat(v.asFloat64(), 'g', -1, 64) + case ValueKindBool: + return strconv.FormatBool(v.asBool()) + case ValueKindBytes: + return fmt.Sprint(v.asBytes()) + case ValueKindMap: + return fmt.Sprint(v.asMap()) + case ValueKindSlice: + return fmt.Sprint(v.asSlice()) + case ValueKindEmpty: + return "" + default: + // Try to handle this as gracefully as possible. + // + // Don't panic here. The goal here is to have developers find this + // first if a slog.Kind is is not handled. It is + // preferable to have user's open issue asking why their attributes + // have a "unhandled: " prefix than say that their code is panicking. + return fmt.Sprintf("", v.Kind()) + } +} + +// MarshalJSON encodes v into OTLP formatted JSON. +func (v *Value) MarshalJSON() ([]byte, error) { + switch v.Kind() { + case ValueKindString: + return json.Marshal(struct { + Value string `json:"stringValue"` + }{v.asString()}) + case ValueKindInt64: + return json.Marshal(struct { + Value string `json:"intValue"` + }{strconv.FormatInt(int64(v.num), 10)}) // nolint: gosec // From raw bytes. + case ValueKindFloat64: + return json.Marshal(struct { + Value float64 `json:"doubleValue"` + }{v.asFloat64()}) + case ValueKindBool: + return json.Marshal(struct { + Value bool `json:"boolValue"` + }{v.asBool()}) + case ValueKindBytes: + return json.Marshal(struct { + Value []byte `json:"bytesValue"` + }{v.asBytes()}) + case ValueKindMap: + return json.Marshal(struct { + Value struct { + Values []Attr `json:"values"` + } `json:"kvlistValue"` + }{struct { + Values []Attr `json:"values"` + }{v.asMap()}}) + case ValueKindSlice: + return json.Marshal(struct { + Value struct { + Values []Value `json:"values"` + } `json:"arrayValue"` + }{struct { + Values []Value `json:"values"` + }{v.asSlice()}}) + case ValueKindEmpty: + return nil, nil + default: + return nil, fmt.Errorf("unknown Value kind: %s", v.Kind().String()) + } +} + +// UnmarshalJSON decodes the OTLP formatted JSON contained in data into v. +func (v *Value) UnmarshalJSON(data []byte) error { + decoder := json.NewDecoder(bytes.NewReader(data)) + + t, err := decoder.Token() + if err != nil { + return err + } + if t != json.Delim('{') { + return errors.New("invalid Value type") + } + + for decoder.More() { + keyIface, err := decoder.Token() + if err != nil { + if errors.Is(err, io.EOF) { + // Empty. + return nil + } + return err + } + + key, ok := keyIface.(string) + if !ok { + return fmt.Errorf("invalid Value key: %#v", keyIface) + } + + switch key { + case "stringValue", "string_value": + var val string + err = decoder.Decode(&val) + *v = StringValue(val) + case "boolValue", "bool_value": + var val bool + err = decoder.Decode(&val) + *v = BoolValue(val) + case "intValue", "int_value": + var val protoInt64 + err = decoder.Decode(&val) + *v = Int64Value(val.Int64()) + case "doubleValue", "double_value": + var val float64 + err = decoder.Decode(&val) + *v = Float64Value(val) + case "bytesValue", "bytes_value": + var val64 string + if err := decoder.Decode(&val64); err != nil { + return err + } + var val []byte + val, err = base64.StdEncoding.DecodeString(val64) + *v = BytesValue(val) + case "arrayValue", "array_value": + var val struct{ Values []Value } + err = decoder.Decode(&val) + *v = SliceValue(val.Values...) + case "kvlistValue", "kvlist_value": + var val struct{ Values []Attr } + err = decoder.Decode(&val) + *v = MapValue(val.Values...) + default: + // Skip unknown. + continue + } + // Use first valid. Ignore the rest. + return err + } + + // Only unknown fields. Return nil without unmarshaling any value. + return nil +} diff --git a/vendor/go.opentelemetry.io/otel/trace/noop.go b/vendor/go.opentelemetry.io/otel/trace/noop.go index ca20e9997a..c8b1ae5d67 100644 --- a/vendor/go.opentelemetry.io/otel/trace/noop.go +++ b/vendor/go.opentelemetry.io/otel/trace/noop.go @@ -82,4 +82,22 @@ func (noopSpan) AddLink(Link) {} func (noopSpan) SetName(string) {} // TracerProvider returns a no-op TracerProvider. -func (noopSpan) TracerProvider() TracerProvider { return noopTracerProvider{} } +func (s noopSpan) TracerProvider() TracerProvider { + return s.tracerProvider(autoInstEnabled) +} + +// autoInstEnabled defines if the auto-instrumentation SDK is enabled. +// +// The auto-instrumentation is expected to overwrite this value to true when it +// attaches to the process. +var autoInstEnabled = new(bool) + +// tracerProvider return a noopTracerProvider if autoEnabled is false, +// otherwise it will return a TracerProvider from the sdk package used in +// auto-instrumentation. +func (noopSpan) tracerProvider(autoEnabled *bool) TracerProvider { + if *autoEnabled { + return newAutoTracerProvider() + } + return noopTracerProvider{} +} diff --git a/vendor/go.opentelemetry.io/otel/version.go b/vendor/go.opentelemetry.io/otel/version.go index eb22002d82..d5fa71f674 100644 --- a/vendor/go.opentelemetry.io/otel/version.go +++ b/vendor/go.opentelemetry.io/otel/version.go @@ -5,5 +5,5 @@ package otel // import "go.opentelemetry.io/otel" // Version is the current release version of OpenTelemetry in use. func Version() string { - return "1.34.0" + return "1.35.0" } diff --git a/vendor/go.opentelemetry.io/otel/versions.yaml b/vendor/go.opentelemetry.io/otel/versions.yaml index ce4fe59b0e..2b4cb4b418 100644 --- a/vendor/go.opentelemetry.io/otel/versions.yaml +++ b/vendor/go.opentelemetry.io/otel/versions.yaml @@ -3,7 +3,7 @@ module-sets: stable-v1: - version: v1.34.0 + version: v1.35.0 modules: - go.opentelemetry.io/otel - go.opentelemetry.io/otel/bridge/opencensus @@ -23,11 +23,11 @@ module-sets: - go.opentelemetry.io/otel/sdk/metric - go.opentelemetry.io/otel/trace experimental-metrics: - version: v0.56.0 + version: v0.57.0 modules: - go.opentelemetry.io/otel/exporters/prometheus experimental-logs: - version: v0.10.0 + version: v0.11.0 modules: - go.opentelemetry.io/otel/log - go.opentelemetry.io/otel/sdk/log @@ -40,3 +40,4 @@ module-sets: - go.opentelemetry.io/otel/schema excluded-modules: - go.opentelemetry.io/otel/internal/tools + - go.opentelemetry.io/otel/trace/internal/telemetry/test diff --git a/vendor/golang.org/x/exp/LICENSE b/vendor/golang.org/x/exp/LICENSE deleted file mode 100644 index 2a7cf70da6..0000000000 --- a/vendor/golang.org/x/exp/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright 2009 The Go Authors. - -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 LLC 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/golang.org/x/exp/PATENTS b/vendor/golang.org/x/exp/PATENTS deleted file mode 100644 index 733099041f..0000000000 --- a/vendor/golang.org/x/exp/PATENTS +++ /dev/null @@ -1,22 +0,0 @@ -Additional IP Rights Grant (Patents) - -"This implementation" means the copyrightable works distributed by -Google as part of the Go project. - -Google hereby grants to You a perpetual, worldwide, non-exclusive, -no-charge, royalty-free, irrevocable (except as stated in this section) -patent license to make, have made, use, offer to sell, sell, import, -transfer and otherwise run, modify and propagate the contents of this -implementation of Go, where such license applies only to those patent -claims, both currently owned or controlled by Google and acquired in -the future, licensable by Google that are necessarily infringed by this -implementation of Go. This grant does not include claims that would be -infringed only as a consequence of further modification of this -implementation. If you or your agent or exclusive licensee institute or -order or agree to the institution of patent litigation against any -entity (including a cross-claim or counterclaim in a lawsuit) alleging -that this implementation of Go or any code incorporated within this -implementation of Go constitutes direct or contributory patent -infringement, or inducement of patent infringement, then any patent -rights granted to you under this License for this implementation of Go -shall terminate as of the date such litigation is filed. diff --git a/vendor/golang.org/x/exp/constraints/constraints.go b/vendor/golang.org/x/exp/constraints/constraints.go deleted file mode 100644 index 2c033dff47..0000000000 --- a/vendor/golang.org/x/exp/constraints/constraints.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package constraints defines a set of useful constraints to be used -// with type parameters. -package constraints - -// Signed is a constraint that permits any signed integer type. -// If future releases of Go add new predeclared signed integer types, -// this constraint will be modified to include them. -type Signed interface { - ~int | ~int8 | ~int16 | ~int32 | ~int64 -} - -// Unsigned is a constraint that permits any unsigned integer type. -// If future releases of Go add new predeclared unsigned integer types, -// this constraint will be modified to include them. -type Unsigned interface { - ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr -} - -// Integer is a constraint that permits any integer type. -// If future releases of Go add new predeclared integer types, -// this constraint will be modified to include them. -type Integer interface { - Signed | Unsigned -} - -// Float is a constraint that permits any floating-point type. -// If future releases of Go add new predeclared floating-point types, -// this constraint will be modified to include them. -type Float interface { - ~float32 | ~float64 -} - -// Complex is a constraint that permits any complex numeric type. -// If future releases of Go add new predeclared complex numeric types, -// this constraint will be modified to include them. -type Complex interface { - ~complex64 | ~complex128 -} - -// Ordered is a constraint that permits any ordered type: any type -// that supports the operators < <= >= >. -// If future releases of Go add new ordered types, -// this constraint will be modified to include them. -type Ordered interface { - Integer | Float | ~string -} diff --git a/vendor/golang.org/x/exp/slices/cmp.go b/vendor/golang.org/x/exp/slices/cmp.go deleted file mode 100644 index fbf1934a06..0000000000 --- a/vendor/golang.org/x/exp/slices/cmp.go +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2023 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package slices - -import "golang.org/x/exp/constraints" - -// min is a version of the predeclared function from the Go 1.21 release. -func min[T constraints.Ordered](a, b T) T { - if a < b || isNaN(a) { - return a - } - return b -} - -// max is a version of the predeclared function from the Go 1.21 release. -func max[T constraints.Ordered](a, b T) T { - if a > b || isNaN(a) { - return a - } - return b -} - -// cmpLess is a copy of cmp.Less from the Go 1.21 release. -func cmpLess[T constraints.Ordered](x, y T) bool { - return (isNaN(x) && !isNaN(y)) || x < y -} - -// cmpCompare is a copy of cmp.Compare from the Go 1.21 release. -func cmpCompare[T constraints.Ordered](x, y T) int { - xNaN := isNaN(x) - yNaN := isNaN(y) - if xNaN && yNaN { - return 0 - } - if xNaN || x < y { - return -1 - } - if yNaN || x > y { - return +1 - } - return 0 -} diff --git a/vendor/golang.org/x/exp/slices/slices.go b/vendor/golang.org/x/exp/slices/slices.go deleted file mode 100644 index 46ceac3439..0000000000 --- a/vendor/golang.org/x/exp/slices/slices.go +++ /dev/null @@ -1,515 +0,0 @@ -// Copyright 2021 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package slices defines various functions useful with slices of any type. -package slices - -import ( - "unsafe" - - "golang.org/x/exp/constraints" -) - -// Equal reports whether two slices are equal: the same length and all -// elements equal. If the lengths are different, Equal returns false. -// Otherwise, the elements are compared in increasing index order, and the -// comparison stops at the first unequal pair. -// Floating point NaNs are not considered equal. -func Equal[S ~[]E, E comparable](s1, s2 S) bool { - if len(s1) != len(s2) { - return false - } - for i := range s1 { - if s1[i] != s2[i] { - return false - } - } - return true -} - -// EqualFunc reports whether two slices are equal using an equality -// function on each pair of elements. If the lengths are different, -// EqualFunc returns false. Otherwise, the elements are compared in -// increasing index order, and the comparison stops at the first index -// for which eq returns false. -func EqualFunc[S1 ~[]E1, S2 ~[]E2, E1, E2 any](s1 S1, s2 S2, eq func(E1, E2) bool) bool { - if len(s1) != len(s2) { - return false - } - for i, v1 := range s1 { - v2 := s2[i] - if !eq(v1, v2) { - return false - } - } - return true -} - -// Compare compares the elements of s1 and s2, using [cmp.Compare] on each pair -// of elements. The elements are compared sequentially, starting at index 0, -// until one element is not equal to the other. -// The result of comparing the first non-matching elements is returned. -// If both slices are equal until one of them ends, the shorter slice is -// considered less than the longer one. -// The result is 0 if s1 == s2, -1 if s1 < s2, and +1 if s1 > s2. -func Compare[S ~[]E, E constraints.Ordered](s1, s2 S) int { - for i, v1 := range s1 { - if i >= len(s2) { - return +1 - } - v2 := s2[i] - if c := cmpCompare(v1, v2); c != 0 { - return c - } - } - if len(s1) < len(s2) { - return -1 - } - return 0 -} - -// CompareFunc is like [Compare] but uses a custom comparison function on each -// pair of elements. -// The result is the first non-zero result of cmp; if cmp always -// returns 0 the result is 0 if len(s1) == len(s2), -1 if len(s1) < len(s2), -// and +1 if len(s1) > len(s2). -func CompareFunc[S1 ~[]E1, S2 ~[]E2, E1, E2 any](s1 S1, s2 S2, cmp func(E1, E2) int) int { - for i, v1 := range s1 { - if i >= len(s2) { - return +1 - } - v2 := s2[i] - if c := cmp(v1, v2); c != 0 { - return c - } - } - if len(s1) < len(s2) { - return -1 - } - return 0 -} - -// Index returns the index of the first occurrence of v in s, -// or -1 if not present. -func Index[S ~[]E, E comparable](s S, v E) int { - for i := range s { - if v == s[i] { - return i - } - } - return -1 -} - -// IndexFunc returns the first index i satisfying f(s[i]), -// or -1 if none do. -func IndexFunc[S ~[]E, E any](s S, f func(E) bool) int { - for i := range s { - if f(s[i]) { - return i - } - } - return -1 -} - -// Contains reports whether v is present in s. -func Contains[S ~[]E, E comparable](s S, v E) bool { - return Index(s, v) >= 0 -} - -// ContainsFunc reports whether at least one -// element e of s satisfies f(e). -func ContainsFunc[S ~[]E, E any](s S, f func(E) bool) bool { - return IndexFunc(s, f) >= 0 -} - -// Insert inserts the values v... into s at index i, -// returning the modified slice. -// The elements at s[i:] are shifted up to make room. -// In the returned slice r, r[i] == v[0], -// and r[i+len(v)] == value originally at r[i]. -// Insert panics if i is out of range. -// This function is O(len(s) + len(v)). -func Insert[S ~[]E, E any](s S, i int, v ...E) S { - m := len(v) - if m == 0 { - return s - } - n := len(s) - if i == n { - return append(s, v...) - } - if n+m > cap(s) { - // Use append rather than make so that we bump the size of - // the slice up to the next storage class. - // This is what Grow does but we don't call Grow because - // that might copy the values twice. - s2 := append(s[:i], make(S, n+m-i)...) - copy(s2[i:], v) - copy(s2[i+m:], s[i:]) - return s2 - } - s = s[:n+m] - - // before: - // s: aaaaaaaabbbbccccccccdddd - // ^ ^ ^ ^ - // i i+m n n+m - // after: - // s: aaaaaaaavvvvbbbbcccccccc - // ^ ^ ^ ^ - // i i+m n n+m - // - // a are the values that don't move in s. - // v are the values copied in from v. - // b and c are the values from s that are shifted up in index. - // d are the values that get overwritten, never to be seen again. - - if !overlaps(v, s[i+m:]) { - // Easy case - v does not overlap either the c or d regions. - // (It might be in some of a or b, or elsewhere entirely.) - // The data we copy up doesn't write to v at all, so just do it. - - copy(s[i+m:], s[i:]) - - // Now we have - // s: aaaaaaaabbbbbbbbcccccccc - // ^ ^ ^ ^ - // i i+m n n+m - // Note the b values are duplicated. - - copy(s[i:], v) - - // Now we have - // s: aaaaaaaavvvvbbbbcccccccc - // ^ ^ ^ ^ - // i i+m n n+m - // That's the result we want. - return s - } - - // The hard case - v overlaps c or d. We can't just shift up - // the data because we'd move or clobber the values we're trying - // to insert. - // So instead, write v on top of d, then rotate. - copy(s[n:], v) - - // Now we have - // s: aaaaaaaabbbbccccccccvvvv - // ^ ^ ^ ^ - // i i+m n n+m - - rotateRight(s[i:], m) - - // Now we have - // s: aaaaaaaavvvvbbbbcccccccc - // ^ ^ ^ ^ - // i i+m n n+m - // That's the result we want. - return s -} - -// clearSlice sets all elements up to the length of s to the zero value of E. -// We may use the builtin clear func instead, and remove clearSlice, when upgrading -// to Go 1.21+. -func clearSlice[S ~[]E, E any](s S) { - var zero E - for i := range s { - s[i] = zero - } -} - -// Delete removes the elements s[i:j] from s, returning the modified slice. -// Delete panics if j > len(s) or s[i:j] is not a valid slice of s. -// Delete is O(len(s)-i), so if many items must be deleted, it is better to -// make a single call deleting them all together than to delete one at a time. -// Delete zeroes the elements s[len(s)-(j-i):len(s)]. -func Delete[S ~[]E, E any](s S, i, j int) S { - _ = s[i:j:len(s)] // bounds check - - if i == j { - return s - } - - oldlen := len(s) - s = append(s[:i], s[j:]...) - clearSlice(s[len(s):oldlen]) // zero/nil out the obsolete elements, for GC - return s -} - -// DeleteFunc removes any elements from s for which del returns true, -// returning the modified slice. -// DeleteFunc zeroes the elements between the new length and the original length. -func DeleteFunc[S ~[]E, E any](s S, del func(E) bool) S { - i := IndexFunc(s, del) - if i == -1 { - return s - } - // Don't start copying elements until we find one to delete. - for j := i + 1; j < len(s); j++ { - if v := s[j]; !del(v) { - s[i] = v - i++ - } - } - clearSlice(s[i:]) // zero/nil out the obsolete elements, for GC - return s[:i] -} - -// Replace replaces the elements s[i:j] by the given v, and returns the -// modified slice. Replace panics if s[i:j] is not a valid slice of s. -// When len(v) < (j-i), Replace zeroes the elements between the new length and the original length. -func Replace[S ~[]E, E any](s S, i, j int, v ...E) S { - _ = s[i:j] // verify that i:j is a valid subslice - - if i == j { - return Insert(s, i, v...) - } - if j == len(s) { - return append(s[:i], v...) - } - - tot := len(s[:i]) + len(v) + len(s[j:]) - if tot > cap(s) { - // Too big to fit, allocate and copy over. - s2 := append(s[:i], make(S, tot-i)...) // See Insert - copy(s2[i:], v) - copy(s2[i+len(v):], s[j:]) - return s2 - } - - r := s[:tot] - - if i+len(v) <= j { - // Easy, as v fits in the deleted portion. - copy(r[i:], v) - if i+len(v) != j { - copy(r[i+len(v):], s[j:]) - } - clearSlice(s[tot:]) // zero/nil out the obsolete elements, for GC - return r - } - - // We are expanding (v is bigger than j-i). - // The situation is something like this: - // (example has i=4,j=8,len(s)=16,len(v)=6) - // s: aaaaxxxxbbbbbbbbyy - // ^ ^ ^ ^ - // i j len(s) tot - // a: prefix of s - // x: deleted range - // b: more of s - // y: area to expand into - - if !overlaps(r[i+len(v):], v) { - // Easy, as v is not clobbered by the first copy. - copy(r[i+len(v):], s[j:]) - copy(r[i:], v) - return r - } - - // This is a situation where we don't have a single place to which - // we can copy v. Parts of it need to go to two different places. - // We want to copy the prefix of v into y and the suffix into x, then - // rotate |y| spots to the right. - // - // v[2:] v[:2] - // | | - // s: aaaavvvvbbbbbbbbvv - // ^ ^ ^ ^ - // i j len(s) tot - // - // If either of those two destinations don't alias v, then we're good. - y := len(v) - (j - i) // length of y portion - - if !overlaps(r[i:j], v) { - copy(r[i:j], v[y:]) - copy(r[len(s):], v[:y]) - rotateRight(r[i:], y) - return r - } - if !overlaps(r[len(s):], v) { - copy(r[len(s):], v[:y]) - copy(r[i:j], v[y:]) - rotateRight(r[i:], y) - return r - } - - // Now we know that v overlaps both x and y. - // That means that the entirety of b is *inside* v. - // So we don't need to preserve b at all; instead we - // can copy v first, then copy the b part of v out of - // v to the right destination. - k := startIdx(v, s[j:]) - copy(r[i:], v) - copy(r[i+len(v):], r[i+k:]) - return r -} - -// Clone returns a copy of the slice. -// The elements are copied using assignment, so this is a shallow clone. -func Clone[S ~[]E, E any](s S) S { - // Preserve nil in case it matters. - if s == nil { - return nil - } - return append(S([]E{}), s...) -} - -// Compact replaces consecutive runs of equal elements with a single copy. -// This is like the uniq command found on Unix. -// Compact modifies the contents of the slice s and returns the modified slice, -// which may have a smaller length. -// Compact zeroes the elements between the new length and the original length. -func Compact[S ~[]E, E comparable](s S) S { - if len(s) < 2 { - return s - } - i := 1 - for k := 1; k < len(s); k++ { - if s[k] != s[k-1] { - if i != k { - s[i] = s[k] - } - i++ - } - } - clearSlice(s[i:]) // zero/nil out the obsolete elements, for GC - return s[:i] -} - -// CompactFunc is like [Compact] but uses an equality function to compare elements. -// For runs of elements that compare equal, CompactFunc keeps the first one. -// CompactFunc zeroes the elements between the new length and the original length. -func CompactFunc[S ~[]E, E any](s S, eq func(E, E) bool) S { - if len(s) < 2 { - return s - } - i := 1 - for k := 1; k < len(s); k++ { - if !eq(s[k], s[k-1]) { - if i != k { - s[i] = s[k] - } - i++ - } - } - clearSlice(s[i:]) // zero/nil out the obsolete elements, for GC - return s[:i] -} - -// Grow increases the slice's capacity, if necessary, to guarantee space for -// another n elements. After Grow(n), at least n elements can be appended -// to the slice without another allocation. If n is negative or too large to -// allocate the memory, Grow panics. -func Grow[S ~[]E, E any](s S, n int) S { - if n < 0 { - panic("cannot be negative") - } - if n -= cap(s) - len(s); n > 0 { - // TODO(https://go.dev/issue/53888): Make using []E instead of S - // to workaround a compiler bug where the runtime.growslice optimization - // does not take effect. Revert when the compiler is fixed. - s = append([]E(s)[:cap(s)], make([]E, n)...)[:len(s)] - } - return s -} - -// Clip removes unused capacity from the slice, returning s[:len(s):len(s)]. -func Clip[S ~[]E, E any](s S) S { - return s[:len(s):len(s)] -} - -// Rotation algorithm explanation: -// -// rotate left by 2 -// start with -// 0123456789 -// split up like this -// 01 234567 89 -// swap first 2 and last 2 -// 89 234567 01 -// join first parts -// 89234567 01 -// recursively rotate first left part by 2 -// 23456789 01 -// join at the end -// 2345678901 -// -// rotate left by 8 -// start with -// 0123456789 -// split up like this -// 01 234567 89 -// swap first 2 and last 2 -// 89 234567 01 -// join last parts -// 89 23456701 -// recursively rotate second part left by 6 -// 89 01234567 -// join at the end -// 8901234567 - -// TODO: There are other rotate algorithms. -// This algorithm has the desirable property that it moves each element exactly twice. -// The triple-reverse algorithm is simpler and more cache friendly, but takes more writes. -// The follow-cycles algorithm can be 1-write but it is not very cache friendly. - -// rotateLeft rotates b left by n spaces. -// s_final[i] = s_orig[i+r], wrapping around. -func rotateLeft[E any](s []E, r int) { - for r != 0 && r != len(s) { - if r*2 <= len(s) { - swap(s[:r], s[len(s)-r:]) - s = s[:len(s)-r] - } else { - swap(s[:len(s)-r], s[r:]) - s, r = s[len(s)-r:], r*2-len(s) - } - } -} -func rotateRight[E any](s []E, r int) { - rotateLeft(s, len(s)-r) -} - -// swap swaps the contents of x and y. x and y must be equal length and disjoint. -func swap[E any](x, y []E) { - for i := 0; i < len(x); i++ { - x[i], y[i] = y[i], x[i] - } -} - -// overlaps reports whether the memory ranges a[0:len(a)] and b[0:len(b)] overlap. -func overlaps[E any](a, b []E) bool { - if len(a) == 0 || len(b) == 0 { - return false - } - elemSize := unsafe.Sizeof(a[0]) - if elemSize == 0 { - return false - } - // TODO: use a runtime/unsafe facility once one becomes available. See issue 12445. - // Also see crypto/internal/alias/alias.go:AnyOverlap - return uintptr(unsafe.Pointer(&a[0])) <= uintptr(unsafe.Pointer(&b[len(b)-1]))+(elemSize-1) && - uintptr(unsafe.Pointer(&b[0])) <= uintptr(unsafe.Pointer(&a[len(a)-1]))+(elemSize-1) -} - -// startIdx returns the index in haystack where the needle starts. -// prerequisite: the needle must be aliased entirely inside the haystack. -func startIdx[E any](haystack, needle []E) int { - p := &needle[0] - for i := range haystack { - if p == &haystack[i] { - return i - } - } - // TODO: what if the overlap is by a non-integral number of Es? - panic("needle not found") -} - -// Reverse reverses the elements of the slice in place. -func Reverse[S ~[]E, E any](s S) { - for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { - s[i], s[j] = s[j], s[i] - } -} diff --git a/vendor/golang.org/x/exp/slices/sort.go b/vendor/golang.org/x/exp/slices/sort.go deleted file mode 100644 index f58bbc7ba4..0000000000 --- a/vendor/golang.org/x/exp/slices/sort.go +++ /dev/null @@ -1,197 +0,0 @@ -// Copyright 2022 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. - -//go:generate go run $GOROOT/src/sort/gen_sort_variants.go -exp - -package slices - -import ( - "math/bits" - - "golang.org/x/exp/constraints" -) - -// Sort sorts a slice of any ordered type in ascending order. -// When sorting floating-point numbers, NaNs are ordered before other values. -func Sort[S ~[]E, E constraints.Ordered](x S) { - n := len(x) - pdqsortOrdered(x, 0, n, bits.Len(uint(n))) -} - -// SortFunc sorts the slice x in ascending order as determined by the cmp -// function. This sort is not guaranteed to be stable. -// cmp(a, b) should return a negative number when a < b, a positive number when -// a > b and zero when a == b or when a is not comparable to b in the sense -// of the formal definition of Strict Weak Ordering. -// -// SortFunc requires that cmp is a strict weak ordering. -// See https://en.wikipedia.org/wiki/Weak_ordering#Strict_weak_orderings. -// To indicate 'uncomparable', return 0 from the function. -func SortFunc[S ~[]E, E any](x S, cmp func(a, b E) int) { - n := len(x) - pdqsortCmpFunc(x, 0, n, bits.Len(uint(n)), cmp) -} - -// SortStableFunc sorts the slice x while keeping the original order of equal -// elements, using cmp to compare elements in the same way as [SortFunc]. -func SortStableFunc[S ~[]E, E any](x S, cmp func(a, b E) int) { - stableCmpFunc(x, len(x), cmp) -} - -// IsSorted reports whether x is sorted in ascending order. -func IsSorted[S ~[]E, E constraints.Ordered](x S) bool { - for i := len(x) - 1; i > 0; i-- { - if cmpLess(x[i], x[i-1]) { - return false - } - } - return true -} - -// IsSortedFunc reports whether x is sorted in ascending order, with cmp as the -// comparison function as defined by [SortFunc]. -func IsSortedFunc[S ~[]E, E any](x S, cmp func(a, b E) int) bool { - for i := len(x) - 1; i > 0; i-- { - if cmp(x[i], x[i-1]) < 0 { - return false - } - } - return true -} - -// Min returns the minimal value in x. It panics if x is empty. -// For floating-point numbers, Min propagates NaNs (any NaN value in x -// forces the output to be NaN). -func Min[S ~[]E, E constraints.Ordered](x S) E { - if len(x) < 1 { - panic("slices.Min: empty list") - } - m := x[0] - for i := 1; i < len(x); i++ { - m = min(m, x[i]) - } - return m -} - -// MinFunc returns the minimal value in x, using cmp to compare elements. -// It panics if x is empty. If there is more than one minimal element -// according to the cmp function, MinFunc returns the first one. -func MinFunc[S ~[]E, E any](x S, cmp func(a, b E) int) E { - if len(x) < 1 { - panic("slices.MinFunc: empty list") - } - m := x[0] - for i := 1; i < len(x); i++ { - if cmp(x[i], m) < 0 { - m = x[i] - } - } - return m -} - -// Max returns the maximal value in x. It panics if x is empty. -// For floating-point E, Max propagates NaNs (any NaN value in x -// forces the output to be NaN). -func Max[S ~[]E, E constraints.Ordered](x S) E { - if len(x) < 1 { - panic("slices.Max: empty list") - } - m := x[0] - for i := 1; i < len(x); i++ { - m = max(m, x[i]) - } - return m -} - -// MaxFunc returns the maximal value in x, using cmp to compare elements. -// It panics if x is empty. If there is more than one maximal element -// according to the cmp function, MaxFunc returns the first one. -func MaxFunc[S ~[]E, E any](x S, cmp func(a, b E) int) E { - if len(x) < 1 { - panic("slices.MaxFunc: empty list") - } - m := x[0] - for i := 1; i < len(x); i++ { - if cmp(x[i], m) > 0 { - m = x[i] - } - } - return m -} - -// BinarySearch searches for target in a sorted slice and returns the position -// where target is found, or the position where target would appear in the -// sort order; it also returns a bool saying whether the target is really found -// in the slice. The slice must be sorted in increasing order. -func BinarySearch[S ~[]E, E constraints.Ordered](x S, target E) (int, bool) { - // Inlining is faster than calling BinarySearchFunc with a lambda. - n := len(x) - // Define x[-1] < target and x[n] >= target. - // Invariant: x[i-1] < target, x[j] >= target. - i, j := 0, n - for i < j { - h := int(uint(i+j) >> 1) // avoid overflow when computing h - // i ≤ h < j - if cmpLess(x[h], target) { - i = h + 1 // preserves x[i-1] < target - } else { - j = h // preserves x[j] >= target - } - } - // i == j, x[i-1] < target, and x[j] (= x[i]) >= target => answer is i. - return i, i < n && (x[i] == target || (isNaN(x[i]) && isNaN(target))) -} - -// BinarySearchFunc works like [BinarySearch], but uses a custom comparison -// function. The slice must be sorted in increasing order, where "increasing" -// is defined by cmp. cmp should return 0 if the slice element matches -// the target, a negative number if the slice element precedes the target, -// or a positive number if the slice element follows the target. -// cmp must implement the same ordering as the slice, such that if -// cmp(a, t) < 0 and cmp(b, t) >= 0, then a must precede b in the slice. -func BinarySearchFunc[S ~[]E, E, T any](x S, target T, cmp func(E, T) int) (int, bool) { - n := len(x) - // Define cmp(x[-1], target) < 0 and cmp(x[n], target) >= 0 . - // Invariant: cmp(x[i - 1], target) < 0, cmp(x[j], target) >= 0. - i, j := 0, n - for i < j { - h := int(uint(i+j) >> 1) // avoid overflow when computing h - // i ≤ h < j - if cmp(x[h], target) < 0 { - i = h + 1 // preserves cmp(x[i - 1], target) < 0 - } else { - j = h // preserves cmp(x[j], target) >= 0 - } - } - // i == j, cmp(x[i-1], target) < 0, and cmp(x[j], target) (= cmp(x[i], target)) >= 0 => answer is i. - return i, i < n && cmp(x[i], target) == 0 -} - -type sortedHint int // hint for pdqsort when choosing the pivot - -const ( - unknownHint sortedHint = iota - increasingHint - decreasingHint -) - -// xorshift paper: https://www.jstatsoft.org/article/view/v008i14/xorshift.pdf -type xorshift uint64 - -func (r *xorshift) Next() uint64 { - *r ^= *r << 13 - *r ^= *r >> 17 - *r ^= *r << 5 - return uint64(*r) -} - -func nextPowerOfTwo(length int) uint { - return 1 << bits.Len(uint(length)) -} - -// isNaN reports whether x is a NaN without requiring the math package. -// This will always return false if T is not floating-point. -func isNaN[T constraints.Ordered](x T) bool { - return x != x -} diff --git a/vendor/golang.org/x/exp/slices/zsortanyfunc.go b/vendor/golang.org/x/exp/slices/zsortanyfunc.go deleted file mode 100644 index 06f2c7a248..0000000000 --- a/vendor/golang.org/x/exp/slices/zsortanyfunc.go +++ /dev/null @@ -1,479 +0,0 @@ -// Code generated by gen_sort_variants.go; DO NOT EDIT. - -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package slices - -// insertionSortCmpFunc sorts data[a:b] using insertion sort. -func insertionSortCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) { - for i := a + 1; i < b; i++ { - for j := i; j > a && (cmp(data[j], data[j-1]) < 0); j-- { - data[j], data[j-1] = data[j-1], data[j] - } - } -} - -// siftDownCmpFunc implements the heap property on data[lo:hi]. -// first is an offset into the array where the root of the heap lies. -func siftDownCmpFunc[E any](data []E, lo, hi, first int, cmp func(a, b E) int) { - root := lo - for { - child := 2*root + 1 - if child >= hi { - break - } - if child+1 < hi && (cmp(data[first+child], data[first+child+1]) < 0) { - child++ - } - if !(cmp(data[first+root], data[first+child]) < 0) { - return - } - data[first+root], data[first+child] = data[first+child], data[first+root] - root = child - } -} - -func heapSortCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) { - first := a - lo := 0 - hi := b - a - - // Build heap with greatest element at top. - for i := (hi - 1) / 2; i >= 0; i-- { - siftDownCmpFunc(data, i, hi, first, cmp) - } - - // Pop elements, largest first, into end of data. - for i := hi - 1; i >= 0; i-- { - data[first], data[first+i] = data[first+i], data[first] - siftDownCmpFunc(data, lo, i, first, cmp) - } -} - -// pdqsortCmpFunc sorts data[a:b]. -// The algorithm based on pattern-defeating quicksort(pdqsort), but without the optimizations from BlockQuicksort. -// pdqsort paper: https://arxiv.org/pdf/2106.05123.pdf -// C++ implementation: https://github.com/orlp/pdqsort -// Rust implementation: https://docs.rs/pdqsort/latest/pdqsort/ -// limit is the number of allowed bad (very unbalanced) pivots before falling back to heapsort. -func pdqsortCmpFunc[E any](data []E, a, b, limit int, cmp func(a, b E) int) { - const maxInsertion = 12 - - var ( - wasBalanced = true // whether the last partitioning was reasonably balanced - wasPartitioned = true // whether the slice was already partitioned - ) - - for { - length := b - a - - if length <= maxInsertion { - insertionSortCmpFunc(data, a, b, cmp) - return - } - - // Fall back to heapsort if too many bad choices were made. - if limit == 0 { - heapSortCmpFunc(data, a, b, cmp) - return - } - - // If the last partitioning was imbalanced, we need to breaking patterns. - if !wasBalanced { - breakPatternsCmpFunc(data, a, b, cmp) - limit-- - } - - pivot, hint := choosePivotCmpFunc(data, a, b, cmp) - if hint == decreasingHint { - reverseRangeCmpFunc(data, a, b, cmp) - // The chosen pivot was pivot-a elements after the start of the array. - // After reversing it is pivot-a elements before the end of the array. - // The idea came from Rust's implementation. - pivot = (b - 1) - (pivot - a) - hint = increasingHint - } - - // The slice is likely already sorted. - if wasBalanced && wasPartitioned && hint == increasingHint { - if partialInsertionSortCmpFunc(data, a, b, cmp) { - return - } - } - - // Probably the slice contains many duplicate elements, partition the slice into - // elements equal to and elements greater than the pivot. - if a > 0 && !(cmp(data[a-1], data[pivot]) < 0) { - mid := partitionEqualCmpFunc(data, a, b, pivot, cmp) - a = mid - continue - } - - mid, alreadyPartitioned := partitionCmpFunc(data, a, b, pivot, cmp) - wasPartitioned = alreadyPartitioned - - leftLen, rightLen := mid-a, b-mid - balanceThreshold := length / 8 - if leftLen < rightLen { - wasBalanced = leftLen >= balanceThreshold - pdqsortCmpFunc(data, a, mid, limit, cmp) - a = mid + 1 - } else { - wasBalanced = rightLen >= balanceThreshold - pdqsortCmpFunc(data, mid+1, b, limit, cmp) - b = mid - } - } -} - -// partitionCmpFunc does one quicksort partition. -// Let p = data[pivot] -// Moves elements in data[a:b] around, so that data[i]

      =p for inewpivot. -// On return, data[newpivot] = p -func partitionCmpFunc[E any](data []E, a, b, pivot int, cmp func(a, b E) int) (newpivot int, alreadyPartitioned bool) { - data[a], data[pivot] = data[pivot], data[a] - i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned - - for i <= j && (cmp(data[i], data[a]) < 0) { - i++ - } - for i <= j && !(cmp(data[j], data[a]) < 0) { - j-- - } - if i > j { - data[j], data[a] = data[a], data[j] - return j, true - } - data[i], data[j] = data[j], data[i] - i++ - j-- - - for { - for i <= j && (cmp(data[i], data[a]) < 0) { - i++ - } - for i <= j && !(cmp(data[j], data[a]) < 0) { - j-- - } - if i > j { - break - } - data[i], data[j] = data[j], data[i] - i++ - j-- - } - data[j], data[a] = data[a], data[j] - return j, false -} - -// partitionEqualCmpFunc partitions data[a:b] into elements equal to data[pivot] followed by elements greater than data[pivot]. -// It assumed that data[a:b] does not contain elements smaller than the data[pivot]. -func partitionEqualCmpFunc[E any](data []E, a, b, pivot int, cmp func(a, b E) int) (newpivot int) { - data[a], data[pivot] = data[pivot], data[a] - i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned - - for { - for i <= j && !(cmp(data[a], data[i]) < 0) { - i++ - } - for i <= j && (cmp(data[a], data[j]) < 0) { - j-- - } - if i > j { - break - } - data[i], data[j] = data[j], data[i] - i++ - j-- - } - return i -} - -// partialInsertionSortCmpFunc partially sorts a slice, returns true if the slice is sorted at the end. -func partialInsertionSortCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) bool { - const ( - maxSteps = 5 // maximum number of adjacent out-of-order pairs that will get shifted - shortestShifting = 50 // don't shift any elements on short arrays - ) - i := a + 1 - for j := 0; j < maxSteps; j++ { - for i < b && !(cmp(data[i], data[i-1]) < 0) { - i++ - } - - if i == b { - return true - } - - if b-a < shortestShifting { - return false - } - - data[i], data[i-1] = data[i-1], data[i] - - // Shift the smaller one to the left. - if i-a >= 2 { - for j := i - 1; j >= 1; j-- { - if !(cmp(data[j], data[j-1]) < 0) { - break - } - data[j], data[j-1] = data[j-1], data[j] - } - } - // Shift the greater one to the right. - if b-i >= 2 { - for j := i + 1; j < b; j++ { - if !(cmp(data[j], data[j-1]) < 0) { - break - } - data[j], data[j-1] = data[j-1], data[j] - } - } - } - return false -} - -// breakPatternsCmpFunc scatters some elements around in an attempt to break some patterns -// that might cause imbalanced partitions in quicksort. -func breakPatternsCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) { - length := b - a - if length >= 8 { - random := xorshift(length) - modulus := nextPowerOfTwo(length) - - for idx := a + (length/4)*2 - 1; idx <= a+(length/4)*2+1; idx++ { - other := int(uint(random.Next()) & (modulus - 1)) - if other >= length { - other -= length - } - data[idx], data[a+other] = data[a+other], data[idx] - } - } -} - -// choosePivotCmpFunc chooses a pivot in data[a:b]. -// -// [0,8): chooses a static pivot. -// [8,shortestNinther): uses the simple median-of-three method. -// [shortestNinther,∞): uses the Tukey ninther method. -func choosePivotCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) (pivot int, hint sortedHint) { - const ( - shortestNinther = 50 - maxSwaps = 4 * 3 - ) - - l := b - a - - var ( - swaps int - i = a + l/4*1 - j = a + l/4*2 - k = a + l/4*3 - ) - - if l >= 8 { - if l >= shortestNinther { - // Tukey ninther method, the idea came from Rust's implementation. - i = medianAdjacentCmpFunc(data, i, &swaps, cmp) - j = medianAdjacentCmpFunc(data, j, &swaps, cmp) - k = medianAdjacentCmpFunc(data, k, &swaps, cmp) - } - // Find the median among i, j, k and stores it into j. - j = medianCmpFunc(data, i, j, k, &swaps, cmp) - } - - switch swaps { - case 0: - return j, increasingHint - case maxSwaps: - return j, decreasingHint - default: - return j, unknownHint - } -} - -// order2CmpFunc returns x,y where data[x] <= data[y], where x,y=a,b or x,y=b,a. -func order2CmpFunc[E any](data []E, a, b int, swaps *int, cmp func(a, b E) int) (int, int) { - if cmp(data[b], data[a]) < 0 { - *swaps++ - return b, a - } - return a, b -} - -// medianCmpFunc returns x where data[x] is the median of data[a],data[b],data[c], where x is a, b, or c. -func medianCmpFunc[E any](data []E, a, b, c int, swaps *int, cmp func(a, b E) int) int { - a, b = order2CmpFunc(data, a, b, swaps, cmp) - b, c = order2CmpFunc(data, b, c, swaps, cmp) - a, b = order2CmpFunc(data, a, b, swaps, cmp) - return b -} - -// medianAdjacentCmpFunc finds the median of data[a - 1], data[a], data[a + 1] and stores the index into a. -func medianAdjacentCmpFunc[E any](data []E, a int, swaps *int, cmp func(a, b E) int) int { - return medianCmpFunc(data, a-1, a, a+1, swaps, cmp) -} - -func reverseRangeCmpFunc[E any](data []E, a, b int, cmp func(a, b E) int) { - i := a - j := b - 1 - for i < j { - data[i], data[j] = data[j], data[i] - i++ - j-- - } -} - -func swapRangeCmpFunc[E any](data []E, a, b, n int, cmp func(a, b E) int) { - for i := 0; i < n; i++ { - data[a+i], data[b+i] = data[b+i], data[a+i] - } -} - -func stableCmpFunc[E any](data []E, n int, cmp func(a, b E) int) { - blockSize := 20 // must be > 0 - a, b := 0, blockSize - for b <= n { - insertionSortCmpFunc(data, a, b, cmp) - a = b - b += blockSize - } - insertionSortCmpFunc(data, a, n, cmp) - - for blockSize < n { - a, b = 0, 2*blockSize - for b <= n { - symMergeCmpFunc(data, a, a+blockSize, b, cmp) - a = b - b += 2 * blockSize - } - if m := a + blockSize; m < n { - symMergeCmpFunc(data, a, m, n, cmp) - } - blockSize *= 2 - } -} - -// symMergeCmpFunc merges the two sorted subsequences data[a:m] and data[m:b] using -// the SymMerge algorithm from Pok-Son Kim and Arne Kutzner, "Stable Minimum -// Storage Merging by Symmetric Comparisons", in Susanne Albers and Tomasz -// Radzik, editors, Algorithms - ESA 2004, volume 3221 of Lecture Notes in -// Computer Science, pages 714-723. Springer, 2004. -// -// Let M = m-a and N = b-n. Wolog M < N. -// The recursion depth is bound by ceil(log(N+M)). -// The algorithm needs O(M*log(N/M + 1)) calls to data.Less. -// The algorithm needs O((M+N)*log(M)) calls to data.Swap. -// -// The paper gives O((M+N)*log(M)) as the number of assignments assuming a -// rotation algorithm which uses O(M+N+gcd(M+N)) assignments. The argumentation -// in the paper carries through for Swap operations, especially as the block -// swapping rotate uses only O(M+N) Swaps. -// -// symMerge assumes non-degenerate arguments: a < m && m < b. -// Having the caller check this condition eliminates many leaf recursion calls, -// which improves performance. -func symMergeCmpFunc[E any](data []E, a, m, b int, cmp func(a, b E) int) { - // Avoid unnecessary recursions of symMerge - // by direct insertion of data[a] into data[m:b] - // if data[a:m] only contains one element. - if m-a == 1 { - // Use binary search to find the lowest index i - // such that data[i] >= data[a] for m <= i < b. - // Exit the search loop with i == b in case no such index exists. - i := m - j := b - for i < j { - h := int(uint(i+j) >> 1) - if cmp(data[h], data[a]) < 0 { - i = h + 1 - } else { - j = h - } - } - // Swap values until data[a] reaches the position before i. - for k := a; k < i-1; k++ { - data[k], data[k+1] = data[k+1], data[k] - } - return - } - - // Avoid unnecessary recursions of symMerge - // by direct insertion of data[m] into data[a:m] - // if data[m:b] only contains one element. - if b-m == 1 { - // Use binary search to find the lowest index i - // such that data[i] > data[m] for a <= i < m. - // Exit the search loop with i == m in case no such index exists. - i := a - j := m - for i < j { - h := int(uint(i+j) >> 1) - if !(cmp(data[m], data[h]) < 0) { - i = h + 1 - } else { - j = h - } - } - // Swap values until data[m] reaches the position i. - for k := m; k > i; k-- { - data[k], data[k-1] = data[k-1], data[k] - } - return - } - - mid := int(uint(a+b) >> 1) - n := mid + m - var start, r int - if m > mid { - start = n - b - r = mid - } else { - start = a - r = m - } - p := n - 1 - - for start < r { - c := int(uint(start+r) >> 1) - if !(cmp(data[p-c], data[c]) < 0) { - start = c + 1 - } else { - r = c - } - } - - end := n - start - if start < m && m < end { - rotateCmpFunc(data, start, m, end, cmp) - } - if a < start && start < mid { - symMergeCmpFunc(data, a, start, mid, cmp) - } - if mid < end && end < b { - symMergeCmpFunc(data, mid, end, b, cmp) - } -} - -// rotateCmpFunc rotates two consecutive blocks u = data[a:m] and v = data[m:b] in data: -// Data of the form 'x u v y' is changed to 'x v u y'. -// rotate performs at most b-a many calls to data.Swap, -// and it assumes non-degenerate arguments: a < m && m < b. -func rotateCmpFunc[E any](data []E, a, m, b int, cmp func(a, b E) int) { - i := m - a - j := b - m - - for i != j { - if i > j { - swapRangeCmpFunc(data, m-i, m, j, cmp) - i -= j - } else { - swapRangeCmpFunc(data, m-i, m+j-i, i, cmp) - j -= i - } - } - // i == j - swapRangeCmpFunc(data, m-i, m, i, cmp) -} diff --git a/vendor/golang.org/x/exp/slices/zsortordered.go b/vendor/golang.org/x/exp/slices/zsortordered.go deleted file mode 100644 index 99b47c3986..0000000000 --- a/vendor/golang.org/x/exp/slices/zsortordered.go +++ /dev/null @@ -1,481 +0,0 @@ -// Code generated by gen_sort_variants.go; DO NOT EDIT. - -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package slices - -import "golang.org/x/exp/constraints" - -// insertionSortOrdered sorts data[a:b] using insertion sort. -func insertionSortOrdered[E constraints.Ordered](data []E, a, b int) { - for i := a + 1; i < b; i++ { - for j := i; j > a && cmpLess(data[j], data[j-1]); j-- { - data[j], data[j-1] = data[j-1], data[j] - } - } -} - -// siftDownOrdered implements the heap property on data[lo:hi]. -// first is an offset into the array where the root of the heap lies. -func siftDownOrdered[E constraints.Ordered](data []E, lo, hi, first int) { - root := lo - for { - child := 2*root + 1 - if child >= hi { - break - } - if child+1 < hi && cmpLess(data[first+child], data[first+child+1]) { - child++ - } - if !cmpLess(data[first+root], data[first+child]) { - return - } - data[first+root], data[first+child] = data[first+child], data[first+root] - root = child - } -} - -func heapSortOrdered[E constraints.Ordered](data []E, a, b int) { - first := a - lo := 0 - hi := b - a - - // Build heap with greatest element at top. - for i := (hi - 1) / 2; i >= 0; i-- { - siftDownOrdered(data, i, hi, first) - } - - // Pop elements, largest first, into end of data. - for i := hi - 1; i >= 0; i-- { - data[first], data[first+i] = data[first+i], data[first] - siftDownOrdered(data, lo, i, first) - } -} - -// pdqsortOrdered sorts data[a:b]. -// The algorithm based on pattern-defeating quicksort(pdqsort), but without the optimizations from BlockQuicksort. -// pdqsort paper: https://arxiv.org/pdf/2106.05123.pdf -// C++ implementation: https://github.com/orlp/pdqsort -// Rust implementation: https://docs.rs/pdqsort/latest/pdqsort/ -// limit is the number of allowed bad (very unbalanced) pivots before falling back to heapsort. -func pdqsortOrdered[E constraints.Ordered](data []E, a, b, limit int) { - const maxInsertion = 12 - - var ( - wasBalanced = true // whether the last partitioning was reasonably balanced - wasPartitioned = true // whether the slice was already partitioned - ) - - for { - length := b - a - - if length <= maxInsertion { - insertionSortOrdered(data, a, b) - return - } - - // Fall back to heapsort if too many bad choices were made. - if limit == 0 { - heapSortOrdered(data, a, b) - return - } - - // If the last partitioning was imbalanced, we need to breaking patterns. - if !wasBalanced { - breakPatternsOrdered(data, a, b) - limit-- - } - - pivot, hint := choosePivotOrdered(data, a, b) - if hint == decreasingHint { - reverseRangeOrdered(data, a, b) - // The chosen pivot was pivot-a elements after the start of the array. - // After reversing it is pivot-a elements before the end of the array. - // The idea came from Rust's implementation. - pivot = (b - 1) - (pivot - a) - hint = increasingHint - } - - // The slice is likely already sorted. - if wasBalanced && wasPartitioned && hint == increasingHint { - if partialInsertionSortOrdered(data, a, b) { - return - } - } - - // Probably the slice contains many duplicate elements, partition the slice into - // elements equal to and elements greater than the pivot. - if a > 0 && !cmpLess(data[a-1], data[pivot]) { - mid := partitionEqualOrdered(data, a, b, pivot) - a = mid - continue - } - - mid, alreadyPartitioned := partitionOrdered(data, a, b, pivot) - wasPartitioned = alreadyPartitioned - - leftLen, rightLen := mid-a, b-mid - balanceThreshold := length / 8 - if leftLen < rightLen { - wasBalanced = leftLen >= balanceThreshold - pdqsortOrdered(data, a, mid, limit) - a = mid + 1 - } else { - wasBalanced = rightLen >= balanceThreshold - pdqsortOrdered(data, mid+1, b, limit) - b = mid - } - } -} - -// partitionOrdered does one quicksort partition. -// Let p = data[pivot] -// Moves elements in data[a:b] around, so that data[i]

      =p for inewpivot. -// On return, data[newpivot] = p -func partitionOrdered[E constraints.Ordered](data []E, a, b, pivot int) (newpivot int, alreadyPartitioned bool) { - data[a], data[pivot] = data[pivot], data[a] - i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned - - for i <= j && cmpLess(data[i], data[a]) { - i++ - } - for i <= j && !cmpLess(data[j], data[a]) { - j-- - } - if i > j { - data[j], data[a] = data[a], data[j] - return j, true - } - data[i], data[j] = data[j], data[i] - i++ - j-- - - for { - for i <= j && cmpLess(data[i], data[a]) { - i++ - } - for i <= j && !cmpLess(data[j], data[a]) { - j-- - } - if i > j { - break - } - data[i], data[j] = data[j], data[i] - i++ - j-- - } - data[j], data[a] = data[a], data[j] - return j, false -} - -// partitionEqualOrdered partitions data[a:b] into elements equal to data[pivot] followed by elements greater than data[pivot]. -// It assumed that data[a:b] does not contain elements smaller than the data[pivot]. -func partitionEqualOrdered[E constraints.Ordered](data []E, a, b, pivot int) (newpivot int) { - data[a], data[pivot] = data[pivot], data[a] - i, j := a+1, b-1 // i and j are inclusive of the elements remaining to be partitioned - - for { - for i <= j && !cmpLess(data[a], data[i]) { - i++ - } - for i <= j && cmpLess(data[a], data[j]) { - j-- - } - if i > j { - break - } - data[i], data[j] = data[j], data[i] - i++ - j-- - } - return i -} - -// partialInsertionSortOrdered partially sorts a slice, returns true if the slice is sorted at the end. -func partialInsertionSortOrdered[E constraints.Ordered](data []E, a, b int) bool { - const ( - maxSteps = 5 // maximum number of adjacent out-of-order pairs that will get shifted - shortestShifting = 50 // don't shift any elements on short arrays - ) - i := a + 1 - for j := 0; j < maxSteps; j++ { - for i < b && !cmpLess(data[i], data[i-1]) { - i++ - } - - if i == b { - return true - } - - if b-a < shortestShifting { - return false - } - - data[i], data[i-1] = data[i-1], data[i] - - // Shift the smaller one to the left. - if i-a >= 2 { - for j := i - 1; j >= 1; j-- { - if !cmpLess(data[j], data[j-1]) { - break - } - data[j], data[j-1] = data[j-1], data[j] - } - } - // Shift the greater one to the right. - if b-i >= 2 { - for j := i + 1; j < b; j++ { - if !cmpLess(data[j], data[j-1]) { - break - } - data[j], data[j-1] = data[j-1], data[j] - } - } - } - return false -} - -// breakPatternsOrdered scatters some elements around in an attempt to break some patterns -// that might cause imbalanced partitions in quicksort. -func breakPatternsOrdered[E constraints.Ordered](data []E, a, b int) { - length := b - a - if length >= 8 { - random := xorshift(length) - modulus := nextPowerOfTwo(length) - - for idx := a + (length/4)*2 - 1; idx <= a+(length/4)*2+1; idx++ { - other := int(uint(random.Next()) & (modulus - 1)) - if other >= length { - other -= length - } - data[idx], data[a+other] = data[a+other], data[idx] - } - } -} - -// choosePivotOrdered chooses a pivot in data[a:b]. -// -// [0,8): chooses a static pivot. -// [8,shortestNinther): uses the simple median-of-three method. -// [shortestNinther,∞): uses the Tukey ninther method. -func choosePivotOrdered[E constraints.Ordered](data []E, a, b int) (pivot int, hint sortedHint) { - const ( - shortestNinther = 50 - maxSwaps = 4 * 3 - ) - - l := b - a - - var ( - swaps int - i = a + l/4*1 - j = a + l/4*2 - k = a + l/4*3 - ) - - if l >= 8 { - if l >= shortestNinther { - // Tukey ninther method, the idea came from Rust's implementation. - i = medianAdjacentOrdered(data, i, &swaps) - j = medianAdjacentOrdered(data, j, &swaps) - k = medianAdjacentOrdered(data, k, &swaps) - } - // Find the median among i, j, k and stores it into j. - j = medianOrdered(data, i, j, k, &swaps) - } - - switch swaps { - case 0: - return j, increasingHint - case maxSwaps: - return j, decreasingHint - default: - return j, unknownHint - } -} - -// order2Ordered returns x,y where data[x] <= data[y], where x,y=a,b or x,y=b,a. -func order2Ordered[E constraints.Ordered](data []E, a, b int, swaps *int) (int, int) { - if cmpLess(data[b], data[a]) { - *swaps++ - return b, a - } - return a, b -} - -// medianOrdered returns x where data[x] is the median of data[a],data[b],data[c], where x is a, b, or c. -func medianOrdered[E constraints.Ordered](data []E, a, b, c int, swaps *int) int { - a, b = order2Ordered(data, a, b, swaps) - b, c = order2Ordered(data, b, c, swaps) - a, b = order2Ordered(data, a, b, swaps) - return b -} - -// medianAdjacentOrdered finds the median of data[a - 1], data[a], data[a + 1] and stores the index into a. -func medianAdjacentOrdered[E constraints.Ordered](data []E, a int, swaps *int) int { - return medianOrdered(data, a-1, a, a+1, swaps) -} - -func reverseRangeOrdered[E constraints.Ordered](data []E, a, b int) { - i := a - j := b - 1 - for i < j { - data[i], data[j] = data[j], data[i] - i++ - j-- - } -} - -func swapRangeOrdered[E constraints.Ordered](data []E, a, b, n int) { - for i := 0; i < n; i++ { - data[a+i], data[b+i] = data[b+i], data[a+i] - } -} - -func stableOrdered[E constraints.Ordered](data []E, n int) { - blockSize := 20 // must be > 0 - a, b := 0, blockSize - for b <= n { - insertionSortOrdered(data, a, b) - a = b - b += blockSize - } - insertionSortOrdered(data, a, n) - - for blockSize < n { - a, b = 0, 2*blockSize - for b <= n { - symMergeOrdered(data, a, a+blockSize, b) - a = b - b += 2 * blockSize - } - if m := a + blockSize; m < n { - symMergeOrdered(data, a, m, n) - } - blockSize *= 2 - } -} - -// symMergeOrdered merges the two sorted subsequences data[a:m] and data[m:b] using -// the SymMerge algorithm from Pok-Son Kim and Arne Kutzner, "Stable Minimum -// Storage Merging by Symmetric Comparisons", in Susanne Albers and Tomasz -// Radzik, editors, Algorithms - ESA 2004, volume 3221 of Lecture Notes in -// Computer Science, pages 714-723. Springer, 2004. -// -// Let M = m-a and N = b-n. Wolog M < N. -// The recursion depth is bound by ceil(log(N+M)). -// The algorithm needs O(M*log(N/M + 1)) calls to data.Less. -// The algorithm needs O((M+N)*log(M)) calls to data.Swap. -// -// The paper gives O((M+N)*log(M)) as the number of assignments assuming a -// rotation algorithm which uses O(M+N+gcd(M+N)) assignments. The argumentation -// in the paper carries through for Swap operations, especially as the block -// swapping rotate uses only O(M+N) Swaps. -// -// symMerge assumes non-degenerate arguments: a < m && m < b. -// Having the caller check this condition eliminates many leaf recursion calls, -// which improves performance. -func symMergeOrdered[E constraints.Ordered](data []E, a, m, b int) { - // Avoid unnecessary recursions of symMerge - // by direct insertion of data[a] into data[m:b] - // if data[a:m] only contains one element. - if m-a == 1 { - // Use binary search to find the lowest index i - // such that data[i] >= data[a] for m <= i < b. - // Exit the search loop with i == b in case no such index exists. - i := m - j := b - for i < j { - h := int(uint(i+j) >> 1) - if cmpLess(data[h], data[a]) { - i = h + 1 - } else { - j = h - } - } - // Swap values until data[a] reaches the position before i. - for k := a; k < i-1; k++ { - data[k], data[k+1] = data[k+1], data[k] - } - return - } - - // Avoid unnecessary recursions of symMerge - // by direct insertion of data[m] into data[a:m] - // if data[m:b] only contains one element. - if b-m == 1 { - // Use binary search to find the lowest index i - // such that data[i] > data[m] for a <= i < m. - // Exit the search loop with i == m in case no such index exists. - i := a - j := m - for i < j { - h := int(uint(i+j) >> 1) - if !cmpLess(data[m], data[h]) { - i = h + 1 - } else { - j = h - } - } - // Swap values until data[m] reaches the position i. - for k := m; k > i; k-- { - data[k], data[k-1] = data[k-1], data[k] - } - return - } - - mid := int(uint(a+b) >> 1) - n := mid + m - var start, r int - if m > mid { - start = n - b - r = mid - } else { - start = a - r = m - } - p := n - 1 - - for start < r { - c := int(uint(start+r) >> 1) - if !cmpLess(data[p-c], data[c]) { - start = c + 1 - } else { - r = c - } - } - - end := n - start - if start < m && m < end { - rotateOrdered(data, start, m, end) - } - if a < start && start < mid { - symMergeOrdered(data, a, start, mid) - } - if mid < end && end < b { - symMergeOrdered(data, mid, end, b) - } -} - -// rotateOrdered rotates two consecutive blocks u = data[a:m] and v = data[m:b] in data: -// Data of the form 'x u v y' is changed to 'x v u y'. -// rotate performs at most b-a many calls to data.Swap, -// and it assumes non-degenerate arguments: a < m && m < b. -func rotateOrdered[E constraints.Ordered](data []E, a, m, b int) { - i := m - a - j := b - m - - for i != j { - if i > j { - swapRangeOrdered(data, m-i, m, j) - i -= j - } else { - swapRangeOrdered(data, m-i, m+j-i, i) - j -= i - } - } - // i == j - swapRangeOrdered(data, m-i, m, i) -} diff --git a/vendor/golang.org/x/exp/slog/attr.go b/vendor/golang.org/x/exp/slog/attr.go deleted file mode 100644 index a180d0e1d3..0000000000 --- a/vendor/golang.org/x/exp/slog/attr.go +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package slog - -import ( - "fmt" - "time" -) - -// An Attr is a key-value pair. -type Attr struct { - Key string - Value Value -} - -// String returns an Attr for a string value. -func String(key, value string) Attr { - return Attr{key, StringValue(value)} -} - -// Int64 returns an Attr for an int64. -func Int64(key string, value int64) Attr { - return Attr{key, Int64Value(value)} -} - -// Int converts an int to an int64 and returns -// an Attr with that value. -func Int(key string, value int) Attr { - return Int64(key, int64(value)) -} - -// Uint64 returns an Attr for a uint64. -func Uint64(key string, v uint64) Attr { - return Attr{key, Uint64Value(v)} -} - -// Float64 returns an Attr for a floating-point number. -func Float64(key string, v float64) Attr { - return Attr{key, Float64Value(v)} -} - -// Bool returns an Attr for a bool. -func Bool(key string, v bool) Attr { - return Attr{key, BoolValue(v)} -} - -// Time returns an Attr for a time.Time. -// It discards the monotonic portion. -func Time(key string, v time.Time) Attr { - return Attr{key, TimeValue(v)} -} - -// Duration returns an Attr for a time.Duration. -func Duration(key string, v time.Duration) Attr { - return Attr{key, DurationValue(v)} -} - -// Group returns an Attr for a Group Value. -// The first argument is the key; the remaining arguments -// are converted to Attrs as in [Logger.Log]. -// -// Use Group to collect several key-value pairs under a single -// key on a log line, or as the result of LogValue -// in order to log a single value as multiple Attrs. -func Group(key string, args ...any) Attr { - return Attr{key, GroupValue(argsToAttrSlice(args)...)} -} - -func argsToAttrSlice(args []any) []Attr { - var ( - attr Attr - attrs []Attr - ) - for len(args) > 0 { - attr, args = argsToAttr(args) - attrs = append(attrs, attr) - } - return attrs -} - -// Any returns an Attr for the supplied value. -// See [Value.AnyValue] for how values are treated. -func Any(key string, value any) Attr { - return Attr{key, AnyValue(value)} -} - -// Equal reports whether a and b have equal keys and values. -func (a Attr) Equal(b Attr) bool { - return a.Key == b.Key && a.Value.Equal(b.Value) -} - -func (a Attr) String() string { - return fmt.Sprintf("%s=%s", a.Key, a.Value) -} - -// isEmpty reports whether a has an empty key and a nil value. -// That can be written as Attr{} or Any("", nil). -func (a Attr) isEmpty() bool { - return a.Key == "" && a.Value.num == 0 && a.Value.any == nil -} diff --git a/vendor/golang.org/x/exp/slog/doc.go b/vendor/golang.org/x/exp/slog/doc.go deleted file mode 100644 index 4beaf86748..0000000000 --- a/vendor/golang.org/x/exp/slog/doc.go +++ /dev/null @@ -1,316 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -/* -Package slog provides structured logging, -in which log records include a message, -a severity level, and various other attributes -expressed as key-value pairs. - -It defines a type, [Logger], -which provides several methods (such as [Logger.Info] and [Logger.Error]) -for reporting events of interest. - -Each Logger is associated with a [Handler]. -A Logger output method creates a [Record] from the method arguments -and passes it to the Handler, which decides how to handle it. -There is a default Logger accessible through top-level functions -(such as [Info] and [Error]) that call the corresponding Logger methods. - -A log record consists of a time, a level, a message, and a set of key-value -pairs, where the keys are strings and the values may be of any type. -As an example, - - slog.Info("hello", "count", 3) - -creates a record containing the time of the call, -a level of Info, the message "hello", and a single -pair with key "count" and value 3. - -The [Info] top-level function calls the [Logger.Info] method on the default Logger. -In addition to [Logger.Info], there are methods for Debug, Warn and Error levels. -Besides these convenience methods for common levels, -there is also a [Logger.Log] method which takes the level as an argument. -Each of these methods has a corresponding top-level function that uses the -default logger. - -The default handler formats the log record's message, time, level, and attributes -as a string and passes it to the [log] package. - - 2022/11/08 15:28:26 INFO hello count=3 - -For more control over the output format, create a logger with a different handler. -This statement uses [New] to create a new logger with a TextHandler -that writes structured records in text form to standard error: - - logger := slog.New(slog.NewTextHandler(os.Stderr, nil)) - -[TextHandler] output is a sequence of key=value pairs, easily and unambiguously -parsed by machine. This statement: - - logger.Info("hello", "count", 3) - -produces this output: - - time=2022-11-08T15:28:26.000-05:00 level=INFO msg=hello count=3 - -The package also provides [JSONHandler], whose output is line-delimited JSON: - - logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) - logger.Info("hello", "count", 3) - -produces this output: - - {"time":"2022-11-08T15:28:26.000000000-05:00","level":"INFO","msg":"hello","count":3} - -Both [TextHandler] and [JSONHandler] can be configured with [HandlerOptions]. -There are options for setting the minimum level (see Levels, below), -displaying the source file and line of the log call, and -modifying attributes before they are logged. - -Setting a logger as the default with - - slog.SetDefault(logger) - -will cause the top-level functions like [Info] to use it. -[SetDefault] also updates the default logger used by the [log] package, -so that existing applications that use [log.Printf] and related functions -will send log records to the logger's handler without needing to be rewritten. - -Some attributes are common to many log calls. -For example, you may wish to include the URL or trace identifier of a server request -with all log events arising from the request. -Rather than repeat the attribute with every log call, you can use [Logger.With] -to construct a new Logger containing the attributes: - - logger2 := logger.With("url", r.URL) - -The arguments to With are the same key-value pairs used in [Logger.Info]. -The result is a new Logger with the same handler as the original, but additional -attributes that will appear in the output of every call. - -# Levels - -A [Level] is an integer representing the importance or severity of a log event. -The higher the level, the more severe the event. -This package defines constants for the most common levels, -but any int can be used as a level. - -In an application, you may wish to log messages only at a certain level or greater. -One common configuration is to log messages at Info or higher levels, -suppressing debug logging until it is needed. -The built-in handlers can be configured with the minimum level to output by -setting [HandlerOptions.Level]. -The program's `main` function typically does this. -The default value is LevelInfo. - -Setting the [HandlerOptions.Level] field to a [Level] value -fixes the handler's minimum level throughout its lifetime. -Setting it to a [LevelVar] allows the level to be varied dynamically. -A LevelVar holds a Level and is safe to read or write from multiple -goroutines. -To vary the level dynamically for an entire program, first initialize -a global LevelVar: - - var programLevel = new(slog.LevelVar) // Info by default - -Then use the LevelVar to construct a handler, and make it the default: - - h := slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: programLevel}) - slog.SetDefault(slog.New(h)) - -Now the program can change its logging level with a single statement: - - programLevel.Set(slog.LevelDebug) - -# Groups - -Attributes can be collected into groups. -A group has a name that is used to qualify the names of its attributes. -How this qualification is displayed depends on the handler. -[TextHandler] separates the group and attribute names with a dot. -[JSONHandler] treats each group as a separate JSON object, with the group name as the key. - -Use [Group] to create a Group attribute from a name and a list of key-value pairs: - - slog.Group("request", - "method", r.Method, - "url", r.URL) - -TextHandler would display this group as - - request.method=GET request.url=http://example.com - -JSONHandler would display it as - - "request":{"method":"GET","url":"http://example.com"} - -Use [Logger.WithGroup] to qualify all of a Logger's output -with a group name. Calling WithGroup on a Logger results in a -new Logger with the same Handler as the original, but with all -its attributes qualified by the group name. - -This can help prevent duplicate attribute keys in large systems, -where subsystems might use the same keys. -Pass each subsystem a different Logger with its own group name so that -potential duplicates are qualified: - - logger := slog.Default().With("id", systemID) - parserLogger := logger.WithGroup("parser") - parseInput(input, parserLogger) - -When parseInput logs with parserLogger, its keys will be qualified with "parser", -so even if it uses the common key "id", the log line will have distinct keys. - -# Contexts - -Some handlers may wish to include information from the [context.Context] that is -available at the call site. One example of such information -is the identifier for the current span when tracing is enabled. - -The [Logger.Log] and [Logger.LogAttrs] methods take a context as a first -argument, as do their corresponding top-level functions. - -Although the convenience methods on Logger (Info and so on) and the -corresponding top-level functions do not take a context, the alternatives ending -in "Context" do. For example, - - slog.InfoContext(ctx, "message") - -It is recommended to pass a context to an output method if one is available. - -# Attrs and Values - -An [Attr] is a key-value pair. The Logger output methods accept Attrs as well as -alternating keys and values. The statement - - slog.Info("hello", slog.Int("count", 3)) - -behaves the same as - - slog.Info("hello", "count", 3) - -There are convenience constructors for [Attr] such as [Int], [String], and [Bool] -for common types, as well as the function [Any] for constructing Attrs of any -type. - -The value part of an Attr is a type called [Value]. -Like an [any], a Value can hold any Go value, -but it can represent typical values, including all numbers and strings, -without an allocation. - -For the most efficient log output, use [Logger.LogAttrs]. -It is similar to [Logger.Log] but accepts only Attrs, not alternating -keys and values; this allows it, too, to avoid allocation. - -The call - - logger.LogAttrs(nil, slog.LevelInfo, "hello", slog.Int("count", 3)) - -is the most efficient way to achieve the same output as - - slog.Info("hello", "count", 3) - -# Customizing a type's logging behavior - -If a type implements the [LogValuer] interface, the [Value] returned from its LogValue -method is used for logging. You can use this to control how values of the type -appear in logs. For example, you can redact secret information like passwords, -or gather a struct's fields in a Group. See the examples under [LogValuer] for -details. - -A LogValue method may return a Value that itself implements [LogValuer]. The [Value.Resolve] -method handles these cases carefully, avoiding infinite loops and unbounded recursion. -Handler authors and others may wish to use Value.Resolve instead of calling LogValue directly. - -# Wrapping output methods - -The logger functions use reflection over the call stack to find the file name -and line number of the logging call within the application. This can produce -incorrect source information for functions that wrap slog. For instance, if you -define this function in file mylog.go: - - func Infof(format string, args ...any) { - slog.Default().Info(fmt.Sprintf(format, args...)) - } - -and you call it like this in main.go: - - Infof(slog.Default(), "hello, %s", "world") - -then slog will report the source file as mylog.go, not main.go. - -A correct implementation of Infof will obtain the source location -(pc) and pass it to NewRecord. -The Infof function in the package-level example called "wrapping" -demonstrates how to do this. - -# Working with Records - -Sometimes a Handler will need to modify a Record -before passing it on to another Handler or backend. -A Record contains a mixture of simple public fields (e.g. Time, Level, Message) -and hidden fields that refer to state (such as attributes) indirectly. This -means that modifying a simple copy of a Record (e.g. by calling -[Record.Add] or [Record.AddAttrs] to add attributes) -may have unexpected effects on the original. -Before modifying a Record, use [Clone] to -create a copy that shares no state with the original, -or create a new Record with [NewRecord] -and build up its Attrs by traversing the old ones with [Record.Attrs]. - -# Performance considerations - -If profiling your application demonstrates that logging is taking significant time, -the following suggestions may help. - -If many log lines have a common attribute, use [Logger.With] to create a Logger with -that attribute. The built-in handlers will format that attribute only once, at the -call to [Logger.With]. The [Handler] interface is designed to allow that optimization, -and a well-written Handler should take advantage of it. - -The arguments to a log call are always evaluated, even if the log event is discarded. -If possible, defer computation so that it happens only if the value is actually logged. -For example, consider the call - - slog.Info("starting request", "url", r.URL.String()) // may compute String unnecessarily - -The URL.String method will be called even if the logger discards Info-level events. -Instead, pass the URL directly: - - slog.Info("starting request", "url", &r.URL) // calls URL.String only if needed - -The built-in [TextHandler] will call its String method, but only -if the log event is enabled. -Avoiding the call to String also preserves the structure of the underlying value. -For example [JSONHandler] emits the components of the parsed URL as a JSON object. -If you want to avoid eagerly paying the cost of the String call -without causing the handler to potentially inspect the structure of the value, -wrap the value in a fmt.Stringer implementation that hides its Marshal methods. - -You can also use the [LogValuer] interface to avoid unnecessary work in disabled log -calls. Say you need to log some expensive value: - - slog.Debug("frobbing", "value", computeExpensiveValue(arg)) - -Even if this line is disabled, computeExpensiveValue will be called. -To avoid that, define a type implementing LogValuer: - - type expensive struct { arg int } - - func (e expensive) LogValue() slog.Value { - return slog.AnyValue(computeExpensiveValue(e.arg)) - } - -Then use a value of that type in log calls: - - slog.Debug("frobbing", "value", expensive{arg}) - -Now computeExpensiveValue will only be called when the line is enabled. - -The built-in handlers acquire a lock before calling [io.Writer.Write] -to ensure that each record is written in one piece. User-defined -handlers are responsible for their own locking. -*/ -package slog diff --git a/vendor/golang.org/x/exp/slog/handler.go b/vendor/golang.org/x/exp/slog/handler.go deleted file mode 100644 index bd635cb818..0000000000 --- a/vendor/golang.org/x/exp/slog/handler.go +++ /dev/null @@ -1,577 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package slog - -import ( - "context" - "fmt" - "io" - "reflect" - "strconv" - "sync" - "time" - - "golang.org/x/exp/slices" - "golang.org/x/exp/slog/internal/buffer" -) - -// A Handler handles log records produced by a Logger.. -// -// A typical handler may print log records to standard error, -// or write them to a file or database, or perhaps augment them -// with additional attributes and pass them on to another handler. -// -// Any of the Handler's methods may be called concurrently with itself -// or with other methods. It is the responsibility of the Handler to -// manage this concurrency. -// -// Users of the slog package should not invoke Handler methods directly. -// They should use the methods of [Logger] instead. -type Handler interface { - // Enabled reports whether the handler handles records at the given level. - // The handler ignores records whose level is lower. - // It is called early, before any arguments are processed, - // to save effort if the log event should be discarded. - // If called from a Logger method, the first argument is the context - // passed to that method, or context.Background() if nil was passed - // or the method does not take a context. - // The context is passed so Enabled can use its values - // to make a decision. - Enabled(context.Context, Level) bool - - // Handle handles the Record. - // It will only be called when Enabled returns true. - // The Context argument is as for Enabled. - // It is present solely to provide Handlers access to the context's values. - // Canceling the context should not affect record processing. - // (Among other things, log messages may be necessary to debug a - // cancellation-related problem.) - // - // Handle methods that produce output should observe the following rules: - // - If r.Time is the zero time, ignore the time. - // - If r.PC is zero, ignore it. - // - Attr's values should be resolved. - // - If an Attr's key and value are both the zero value, ignore the Attr. - // This can be tested with attr.Equal(Attr{}). - // - If a group's key is empty, inline the group's Attrs. - // - If a group has no Attrs (even if it has a non-empty key), - // ignore it. - Handle(context.Context, Record) error - - // WithAttrs returns a new Handler whose attributes consist of - // both the receiver's attributes and the arguments. - // The Handler owns the slice: it may retain, modify or discard it. - WithAttrs(attrs []Attr) Handler - - // WithGroup returns a new Handler with the given group appended to - // the receiver's existing groups. - // The keys of all subsequent attributes, whether added by With or in a - // Record, should be qualified by the sequence of group names. - // - // How this qualification happens is up to the Handler, so long as - // this Handler's attribute keys differ from those of another Handler - // with a different sequence of group names. - // - // A Handler should treat WithGroup as starting a Group of Attrs that ends - // at the end of the log event. That is, - // - // logger.WithGroup("s").LogAttrs(level, msg, slog.Int("a", 1), slog.Int("b", 2)) - // - // should behave like - // - // logger.LogAttrs(level, msg, slog.Group("s", slog.Int("a", 1), slog.Int("b", 2))) - // - // If the name is empty, WithGroup returns the receiver. - WithGroup(name string) Handler -} - -type defaultHandler struct { - ch *commonHandler - // log.Output, except for testing - output func(calldepth int, message string) error -} - -func newDefaultHandler(output func(int, string) error) *defaultHandler { - return &defaultHandler{ - ch: &commonHandler{json: false}, - output: output, - } -} - -func (*defaultHandler) Enabled(_ context.Context, l Level) bool { - return l >= LevelInfo -} - -// Collect the level, attributes and message in a string and -// write it with the default log.Logger. -// Let the log.Logger handle time and file/line. -func (h *defaultHandler) Handle(ctx context.Context, r Record) error { - buf := buffer.New() - buf.WriteString(r.Level.String()) - buf.WriteByte(' ') - buf.WriteString(r.Message) - state := h.ch.newHandleState(buf, true, " ", nil) - defer state.free() - state.appendNonBuiltIns(r) - - // skip [h.output, defaultHandler.Handle, handlerWriter.Write, log.Output] - return h.output(4, buf.String()) -} - -func (h *defaultHandler) WithAttrs(as []Attr) Handler { - return &defaultHandler{h.ch.withAttrs(as), h.output} -} - -func (h *defaultHandler) WithGroup(name string) Handler { - return &defaultHandler{h.ch.withGroup(name), h.output} -} - -// HandlerOptions are options for a TextHandler or JSONHandler. -// A zero HandlerOptions consists entirely of default values. -type HandlerOptions struct { - // AddSource causes the handler to compute the source code position - // of the log statement and add a SourceKey attribute to the output. - AddSource bool - - // Level reports the minimum record level that will be logged. - // The handler discards records with lower levels. - // If Level is nil, the handler assumes LevelInfo. - // The handler calls Level.Level for each record processed; - // to adjust the minimum level dynamically, use a LevelVar. - Level Leveler - - // ReplaceAttr is called to rewrite each non-group attribute before it is logged. - // The attribute's value has been resolved (see [Value.Resolve]). - // If ReplaceAttr returns an Attr with Key == "", the attribute is discarded. - // - // The built-in attributes with keys "time", "level", "source", and "msg" - // are passed to this function, except that time is omitted - // if zero, and source is omitted if AddSource is false. - // - // The first argument is a list of currently open groups that contain the - // Attr. It must not be retained or modified. ReplaceAttr is never called - // for Group attributes, only their contents. For example, the attribute - // list - // - // Int("a", 1), Group("g", Int("b", 2)), Int("c", 3) - // - // results in consecutive calls to ReplaceAttr with the following arguments: - // - // nil, Int("a", 1) - // []string{"g"}, Int("b", 2) - // nil, Int("c", 3) - // - // ReplaceAttr can be used to change the default keys of the built-in - // attributes, convert types (for example, to replace a `time.Time` with the - // integer seconds since the Unix epoch), sanitize personal information, or - // remove attributes from the output. - ReplaceAttr func(groups []string, a Attr) Attr -} - -// Keys for "built-in" attributes. -const ( - // TimeKey is the key used by the built-in handlers for the time - // when the log method is called. The associated Value is a [time.Time]. - TimeKey = "time" - // LevelKey is the key used by the built-in handlers for the level - // of the log call. The associated value is a [Level]. - LevelKey = "level" - // MessageKey is the key used by the built-in handlers for the - // message of the log call. The associated value is a string. - MessageKey = "msg" - // SourceKey is the key used by the built-in handlers for the source file - // and line of the log call. The associated value is a string. - SourceKey = "source" -) - -type commonHandler struct { - json bool // true => output JSON; false => output text - opts HandlerOptions - preformattedAttrs []byte - groupPrefix string // for text: prefix of groups opened in preformatting - groups []string // all groups started from WithGroup - nOpenGroups int // the number of groups opened in preformattedAttrs - mu sync.Mutex - w io.Writer -} - -func (h *commonHandler) clone() *commonHandler { - // We can't use assignment because we can't copy the mutex. - return &commonHandler{ - json: h.json, - opts: h.opts, - preformattedAttrs: slices.Clip(h.preformattedAttrs), - groupPrefix: h.groupPrefix, - groups: slices.Clip(h.groups), - nOpenGroups: h.nOpenGroups, - w: h.w, - } -} - -// enabled reports whether l is greater than or equal to the -// minimum level. -func (h *commonHandler) enabled(l Level) bool { - minLevel := LevelInfo - if h.opts.Level != nil { - minLevel = h.opts.Level.Level() - } - return l >= minLevel -} - -func (h *commonHandler) withAttrs(as []Attr) *commonHandler { - h2 := h.clone() - // Pre-format the attributes as an optimization. - prefix := buffer.New() - defer prefix.Free() - prefix.WriteString(h.groupPrefix) - state := h2.newHandleState((*buffer.Buffer)(&h2.preformattedAttrs), false, "", prefix) - defer state.free() - if len(h2.preformattedAttrs) > 0 { - state.sep = h.attrSep() - } - state.openGroups() - for _, a := range as { - state.appendAttr(a) - } - // Remember the new prefix for later keys. - h2.groupPrefix = state.prefix.String() - // Remember how many opened groups are in preformattedAttrs, - // so we don't open them again when we handle a Record. - h2.nOpenGroups = len(h2.groups) - return h2 -} - -func (h *commonHandler) withGroup(name string) *commonHandler { - if name == "" { - return h - } - h2 := h.clone() - h2.groups = append(h2.groups, name) - return h2 -} - -func (h *commonHandler) handle(r Record) error { - state := h.newHandleState(buffer.New(), true, "", nil) - defer state.free() - if h.json { - state.buf.WriteByte('{') - } - // Built-in attributes. They are not in a group. - stateGroups := state.groups - state.groups = nil // So ReplaceAttrs sees no groups instead of the pre groups. - rep := h.opts.ReplaceAttr - // time - if !r.Time.IsZero() { - key := TimeKey - val := r.Time.Round(0) // strip monotonic to match Attr behavior - if rep == nil { - state.appendKey(key) - state.appendTime(val) - } else { - state.appendAttr(Time(key, val)) - } - } - // level - key := LevelKey - val := r.Level - if rep == nil { - state.appendKey(key) - state.appendString(val.String()) - } else { - state.appendAttr(Any(key, val)) - } - // source - if h.opts.AddSource { - state.appendAttr(Any(SourceKey, r.source())) - } - key = MessageKey - msg := r.Message - if rep == nil { - state.appendKey(key) - state.appendString(msg) - } else { - state.appendAttr(String(key, msg)) - } - state.groups = stateGroups // Restore groups passed to ReplaceAttrs. - state.appendNonBuiltIns(r) - state.buf.WriteByte('\n') - - h.mu.Lock() - defer h.mu.Unlock() - _, err := h.w.Write(*state.buf) - return err -} - -func (s *handleState) appendNonBuiltIns(r Record) { - // preformatted Attrs - if len(s.h.preformattedAttrs) > 0 { - s.buf.WriteString(s.sep) - s.buf.Write(s.h.preformattedAttrs) - s.sep = s.h.attrSep() - } - // Attrs in Record -- unlike the built-in ones, they are in groups started - // from WithGroup. - s.prefix = buffer.New() - defer s.prefix.Free() - s.prefix.WriteString(s.h.groupPrefix) - s.openGroups() - r.Attrs(func(a Attr) bool { - s.appendAttr(a) - return true - }) - if s.h.json { - // Close all open groups. - for range s.h.groups { - s.buf.WriteByte('}') - } - // Close the top-level object. - s.buf.WriteByte('}') - } -} - -// attrSep returns the separator between attributes. -func (h *commonHandler) attrSep() string { - if h.json { - return "," - } - return " " -} - -// handleState holds state for a single call to commonHandler.handle. -// The initial value of sep determines whether to emit a separator -// before the next key, after which it stays true. -type handleState struct { - h *commonHandler - buf *buffer.Buffer - freeBuf bool // should buf be freed? - sep string // separator to write before next key - prefix *buffer.Buffer // for text: key prefix - groups *[]string // pool-allocated slice of active groups, for ReplaceAttr -} - -var groupPool = sync.Pool{New: func() any { - s := make([]string, 0, 10) - return &s -}} - -func (h *commonHandler) newHandleState(buf *buffer.Buffer, freeBuf bool, sep string, prefix *buffer.Buffer) handleState { - s := handleState{ - h: h, - buf: buf, - freeBuf: freeBuf, - sep: sep, - prefix: prefix, - } - if h.opts.ReplaceAttr != nil { - s.groups = groupPool.Get().(*[]string) - *s.groups = append(*s.groups, h.groups[:h.nOpenGroups]...) - } - return s -} - -func (s *handleState) free() { - if s.freeBuf { - s.buf.Free() - } - if gs := s.groups; gs != nil { - *gs = (*gs)[:0] - groupPool.Put(gs) - } -} - -func (s *handleState) openGroups() { - for _, n := range s.h.groups[s.h.nOpenGroups:] { - s.openGroup(n) - } -} - -// Separator for group names and keys. -const keyComponentSep = '.' - -// openGroup starts a new group of attributes -// with the given name. -func (s *handleState) openGroup(name string) { - if s.h.json { - s.appendKey(name) - s.buf.WriteByte('{') - s.sep = "" - } else { - s.prefix.WriteString(name) - s.prefix.WriteByte(keyComponentSep) - } - // Collect group names for ReplaceAttr. - if s.groups != nil { - *s.groups = append(*s.groups, name) - } -} - -// closeGroup ends the group with the given name. -func (s *handleState) closeGroup(name string) { - if s.h.json { - s.buf.WriteByte('}') - } else { - (*s.prefix) = (*s.prefix)[:len(*s.prefix)-len(name)-1 /* for keyComponentSep */] - } - s.sep = s.h.attrSep() - if s.groups != nil { - *s.groups = (*s.groups)[:len(*s.groups)-1] - } -} - -// appendAttr appends the Attr's key and value using app. -// It handles replacement and checking for an empty key. -// after replacement). -func (s *handleState) appendAttr(a Attr) { - if rep := s.h.opts.ReplaceAttr; rep != nil && a.Value.Kind() != KindGroup { - var gs []string - if s.groups != nil { - gs = *s.groups - } - // Resolve before calling ReplaceAttr, so the user doesn't have to. - a.Value = a.Value.Resolve() - a = rep(gs, a) - } - a.Value = a.Value.Resolve() - // Elide empty Attrs. - if a.isEmpty() { - return - } - // Special case: Source. - if v := a.Value; v.Kind() == KindAny { - if src, ok := v.Any().(*Source); ok { - if s.h.json { - a.Value = src.group() - } else { - a.Value = StringValue(fmt.Sprintf("%s:%d", src.File, src.Line)) - } - } - } - if a.Value.Kind() == KindGroup { - attrs := a.Value.Group() - // Output only non-empty groups. - if len(attrs) > 0 { - // Inline a group with an empty key. - if a.Key != "" { - s.openGroup(a.Key) - } - for _, aa := range attrs { - s.appendAttr(aa) - } - if a.Key != "" { - s.closeGroup(a.Key) - } - } - } else { - s.appendKey(a.Key) - s.appendValue(a.Value) - } -} - -func (s *handleState) appendError(err error) { - s.appendString(fmt.Sprintf("!ERROR:%v", err)) -} - -func (s *handleState) appendKey(key string) { - s.buf.WriteString(s.sep) - if s.prefix != nil { - // TODO: optimize by avoiding allocation. - s.appendString(string(*s.prefix) + key) - } else { - s.appendString(key) - } - if s.h.json { - s.buf.WriteByte(':') - } else { - s.buf.WriteByte('=') - } - s.sep = s.h.attrSep() -} - -func (s *handleState) appendString(str string) { - if s.h.json { - s.buf.WriteByte('"') - *s.buf = appendEscapedJSONString(*s.buf, str) - s.buf.WriteByte('"') - } else { - // text - if needsQuoting(str) { - *s.buf = strconv.AppendQuote(*s.buf, str) - } else { - s.buf.WriteString(str) - } - } -} - -func (s *handleState) appendValue(v Value) { - defer func() { - if r := recover(); r != nil { - // If it panics with a nil pointer, the most likely cases are - // an encoding.TextMarshaler or error fails to guard against nil, - // in which case "" seems to be the feasible choice. - // - // Adapted from the code in fmt/print.go. - if v := reflect.ValueOf(v.any); v.Kind() == reflect.Pointer && v.IsNil() { - s.appendString("") - return - } - - // Otherwise just print the original panic message. - s.appendString(fmt.Sprintf("!PANIC: %v", r)) - } - }() - - var err error - if s.h.json { - err = appendJSONValue(s, v) - } else { - err = appendTextValue(s, v) - } - if err != nil { - s.appendError(err) - } -} - -func (s *handleState) appendTime(t time.Time) { - if s.h.json { - appendJSONTime(s, t) - } else { - writeTimeRFC3339Millis(s.buf, t) - } -} - -// This takes half the time of Time.AppendFormat. -func writeTimeRFC3339Millis(buf *buffer.Buffer, t time.Time) { - year, month, day := t.Date() - buf.WritePosIntWidth(year, 4) - buf.WriteByte('-') - buf.WritePosIntWidth(int(month), 2) - buf.WriteByte('-') - buf.WritePosIntWidth(day, 2) - buf.WriteByte('T') - hour, min, sec := t.Clock() - buf.WritePosIntWidth(hour, 2) - buf.WriteByte(':') - buf.WritePosIntWidth(min, 2) - buf.WriteByte(':') - buf.WritePosIntWidth(sec, 2) - ns := t.Nanosecond() - buf.WriteByte('.') - buf.WritePosIntWidth(ns/1e6, 3) - _, offsetSeconds := t.Zone() - if offsetSeconds == 0 { - buf.WriteByte('Z') - } else { - offsetMinutes := offsetSeconds / 60 - if offsetMinutes < 0 { - buf.WriteByte('-') - offsetMinutes = -offsetMinutes - } else { - buf.WriteByte('+') - } - buf.WritePosIntWidth(offsetMinutes/60, 2) - buf.WriteByte(':') - buf.WritePosIntWidth(offsetMinutes%60, 2) - } -} diff --git a/vendor/golang.org/x/exp/slog/internal/buffer/buffer.go b/vendor/golang.org/x/exp/slog/internal/buffer/buffer.go deleted file mode 100644 index 7786c166e0..0000000000 --- a/vendor/golang.org/x/exp/slog/internal/buffer/buffer.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package buffer provides a pool-allocated byte buffer. -package buffer - -import ( - "sync" -) - -// Buffer adapted from go/src/fmt/print.go -type Buffer []byte - -// Having an initial size gives a dramatic speedup. -var bufPool = sync.Pool{ - New: func() any { - b := make([]byte, 0, 1024) - return (*Buffer)(&b) - }, -} - -func New() *Buffer { - return bufPool.Get().(*Buffer) -} - -func (b *Buffer) Free() { - // To reduce peak allocation, return only smaller buffers to the pool. - const maxBufferSize = 16 << 10 - if cap(*b) <= maxBufferSize { - *b = (*b)[:0] - bufPool.Put(b) - } -} - -func (b *Buffer) Reset() { - *b = (*b)[:0] -} - -func (b *Buffer) Write(p []byte) (int, error) { - *b = append(*b, p...) - return len(p), nil -} - -func (b *Buffer) WriteString(s string) { - *b = append(*b, s...) -} - -func (b *Buffer) WriteByte(c byte) { - *b = append(*b, c) -} - -func (b *Buffer) WritePosInt(i int) { - b.WritePosIntWidth(i, 0) -} - -// WritePosIntWidth writes non-negative integer i to the buffer, padded on the left -// by zeroes to the given width. Use a width of 0 to omit padding. -func (b *Buffer) WritePosIntWidth(i, width int) { - // Cheap integer to fixed-width decimal ASCII. - // Copied from log/log.go. - - if i < 0 { - panic("negative int") - } - - // Assemble decimal in reverse order. - var bb [20]byte - bp := len(bb) - 1 - for i >= 10 || width > 1 { - width-- - q := i / 10 - bb[bp] = byte('0' + i - q*10) - bp-- - i = q - } - // i < 10 - bb[bp] = byte('0' + i) - b.Write(bb[bp:]) -} - -func (b *Buffer) String() string { - return string(*b) -} diff --git a/vendor/golang.org/x/exp/slog/internal/ignorepc.go b/vendor/golang.org/x/exp/slog/internal/ignorepc.go deleted file mode 100644 index d1256426ff..0000000000 --- a/vendor/golang.org/x/exp/slog/internal/ignorepc.go +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright 2023 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package internal - -// If IgnorePC is true, do not invoke runtime.Callers to get the pc. -// This is solely for benchmarking the slowdown from runtime.Callers. -var IgnorePC = false diff --git a/vendor/golang.org/x/exp/slog/json_handler.go b/vendor/golang.org/x/exp/slog/json_handler.go deleted file mode 100644 index 157ada8692..0000000000 --- a/vendor/golang.org/x/exp/slog/json_handler.go +++ /dev/null @@ -1,336 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package slog - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "io" - "strconv" - "time" - "unicode/utf8" - - "golang.org/x/exp/slog/internal/buffer" -) - -// JSONHandler is a Handler that writes Records to an io.Writer as -// line-delimited JSON objects. -type JSONHandler struct { - *commonHandler -} - -// NewJSONHandler creates a JSONHandler that writes to w, -// using the given options. -// If opts is nil, the default options are used. -func NewJSONHandler(w io.Writer, opts *HandlerOptions) *JSONHandler { - if opts == nil { - opts = &HandlerOptions{} - } - return &JSONHandler{ - &commonHandler{ - json: true, - w: w, - opts: *opts, - }, - } -} - -// Enabled reports whether the handler handles records at the given level. -// The handler ignores records whose level is lower. -func (h *JSONHandler) Enabled(_ context.Context, level Level) bool { - return h.commonHandler.enabled(level) -} - -// WithAttrs returns a new JSONHandler whose attributes consists -// of h's attributes followed by attrs. -func (h *JSONHandler) WithAttrs(attrs []Attr) Handler { - return &JSONHandler{commonHandler: h.commonHandler.withAttrs(attrs)} -} - -func (h *JSONHandler) WithGroup(name string) Handler { - return &JSONHandler{commonHandler: h.commonHandler.withGroup(name)} -} - -// Handle formats its argument Record as a JSON object on a single line. -// -// If the Record's time is zero, the time is omitted. -// Otherwise, the key is "time" -// and the value is output as with json.Marshal. -// -// If the Record's level is zero, the level is omitted. -// Otherwise, the key is "level" -// and the value of [Level.String] is output. -// -// If the AddSource option is set and source information is available, -// the key is "source" -// and the value is output as "FILE:LINE". -// -// The message's key is "msg". -// -// To modify these or other attributes, or remove them from the output, use -// [HandlerOptions.ReplaceAttr]. -// -// Values are formatted as with an [encoding/json.Encoder] with SetEscapeHTML(false), -// with two exceptions. -// -// First, an Attr whose Value is of type error is formatted as a string, by -// calling its Error method. Only errors in Attrs receive this special treatment, -// not errors embedded in structs, slices, maps or other data structures that -// are processed by the encoding/json package. -// -// Second, an encoding failure does not cause Handle to return an error. -// Instead, the error message is formatted as a string. -// -// Each call to Handle results in a single serialized call to io.Writer.Write. -func (h *JSONHandler) Handle(_ context.Context, r Record) error { - return h.commonHandler.handle(r) -} - -// Adapted from time.Time.MarshalJSON to avoid allocation. -func appendJSONTime(s *handleState, t time.Time) { - if y := t.Year(); y < 0 || y >= 10000 { - // RFC 3339 is clear that years are 4 digits exactly. - // See golang.org/issue/4556#c15 for more discussion. - s.appendError(errors.New("time.Time year outside of range [0,9999]")) - } - s.buf.WriteByte('"') - *s.buf = t.AppendFormat(*s.buf, time.RFC3339Nano) - s.buf.WriteByte('"') -} - -func appendJSONValue(s *handleState, v Value) error { - switch v.Kind() { - case KindString: - s.appendString(v.str()) - case KindInt64: - *s.buf = strconv.AppendInt(*s.buf, v.Int64(), 10) - case KindUint64: - *s.buf = strconv.AppendUint(*s.buf, v.Uint64(), 10) - case KindFloat64: - // json.Marshal is funny about floats; it doesn't - // always match strconv.AppendFloat. So just call it. - // That's expensive, but floats are rare. - if err := appendJSONMarshal(s.buf, v.Float64()); err != nil { - return err - } - case KindBool: - *s.buf = strconv.AppendBool(*s.buf, v.Bool()) - case KindDuration: - // Do what json.Marshal does. - *s.buf = strconv.AppendInt(*s.buf, int64(v.Duration()), 10) - case KindTime: - s.appendTime(v.Time()) - case KindAny: - a := v.Any() - _, jm := a.(json.Marshaler) - if err, ok := a.(error); ok && !jm { - s.appendString(err.Error()) - } else { - return appendJSONMarshal(s.buf, a) - } - default: - panic(fmt.Sprintf("bad kind: %s", v.Kind())) - } - return nil -} - -func appendJSONMarshal(buf *buffer.Buffer, v any) error { - // Use a json.Encoder to avoid escaping HTML. - var bb bytes.Buffer - enc := json.NewEncoder(&bb) - enc.SetEscapeHTML(false) - if err := enc.Encode(v); err != nil { - return err - } - bs := bb.Bytes() - buf.Write(bs[:len(bs)-1]) // remove final newline - return nil -} - -// appendEscapedJSONString escapes s for JSON and appends it to buf. -// It does not surround the string in quotation marks. -// -// Modified from encoding/json/encode.go:encodeState.string, -// with escapeHTML set to false. -func appendEscapedJSONString(buf []byte, s string) []byte { - char := func(b byte) { buf = append(buf, b) } - str := func(s string) { buf = append(buf, s...) } - - start := 0 - for i := 0; i < len(s); { - if b := s[i]; b < utf8.RuneSelf { - if safeSet[b] { - i++ - continue - } - if start < i { - str(s[start:i]) - } - char('\\') - switch b { - case '\\', '"': - char(b) - case '\n': - char('n') - case '\r': - char('r') - case '\t': - char('t') - default: - // This encodes bytes < 0x20 except for \t, \n and \r. - str(`u00`) - char(hex[b>>4]) - char(hex[b&0xF]) - } - i++ - start = i - continue - } - c, size := utf8.DecodeRuneInString(s[i:]) - if c == utf8.RuneError && size == 1 { - if start < i { - str(s[start:i]) - } - str(`\ufffd`) - i += size - start = i - continue - } - // U+2028 is LINE SEPARATOR. - // U+2029 is PARAGRAPH SEPARATOR. - // They are both technically valid characters in JSON strings, - // but don't work in JSONP, which has to be evaluated as JavaScript, - // and can lead to security holes there. It is valid JSON to - // escape them, so we do so unconditionally. - // See http://timelessrepo.com/json-isnt-a-javascript-subset for discussion. - if c == '\u2028' || c == '\u2029' { - if start < i { - str(s[start:i]) - } - str(`\u202`) - char(hex[c&0xF]) - i += size - start = i - continue - } - i += size - } - if start < len(s) { - str(s[start:]) - } - return buf -} - -var hex = "0123456789abcdef" - -// Copied from encoding/json/tables.go. -// -// safeSet holds the value true if the ASCII character with the given array -// position can be represented inside a JSON string without any further -// escaping. -// -// All values are true except for the ASCII control characters (0-31), the -// double quote ("), and the backslash character ("\"). -var safeSet = [utf8.RuneSelf]bool{ - ' ': true, - '!': true, - '"': false, - '#': true, - '$': true, - '%': true, - '&': true, - '\'': true, - '(': true, - ')': true, - '*': true, - '+': true, - ',': true, - '-': true, - '.': true, - '/': true, - '0': true, - '1': true, - '2': true, - '3': true, - '4': true, - '5': true, - '6': true, - '7': true, - '8': true, - '9': true, - ':': true, - ';': true, - '<': true, - '=': true, - '>': true, - '?': true, - '@': true, - 'A': true, - 'B': true, - 'C': true, - 'D': true, - 'E': true, - 'F': true, - 'G': true, - 'H': true, - 'I': true, - 'J': true, - 'K': true, - 'L': true, - 'M': true, - 'N': true, - 'O': true, - 'P': true, - 'Q': true, - 'R': true, - 'S': true, - 'T': true, - 'U': true, - 'V': true, - 'W': true, - 'X': true, - 'Y': true, - 'Z': true, - '[': true, - '\\': false, - ']': true, - '^': true, - '_': true, - '`': true, - 'a': true, - 'b': true, - 'c': true, - 'd': true, - 'e': true, - 'f': true, - 'g': true, - 'h': true, - 'i': true, - 'j': true, - 'k': true, - 'l': true, - 'm': true, - 'n': true, - 'o': true, - 'p': true, - 'q': true, - 'r': true, - 's': true, - 't': true, - 'u': true, - 'v': true, - 'w': true, - 'x': true, - 'y': true, - 'z': true, - '{': true, - '|': true, - '}': true, - '~': true, - '\u007f': true, -} diff --git a/vendor/golang.org/x/exp/slog/level.go b/vendor/golang.org/x/exp/slog/level.go deleted file mode 100644 index b2365f0aa5..0000000000 --- a/vendor/golang.org/x/exp/slog/level.go +++ /dev/null @@ -1,201 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package slog - -import ( - "errors" - "fmt" - "strconv" - "strings" - "sync/atomic" -) - -// A Level is the importance or severity of a log event. -// The higher the level, the more important or severe the event. -type Level int - -// Level numbers are inherently arbitrary, -// but we picked them to satisfy three constraints. -// Any system can map them to another numbering scheme if it wishes. -// -// First, we wanted the default level to be Info, Since Levels are ints, Info is -// the default value for int, zero. -// - -// Second, we wanted to make it easy to use levels to specify logger verbosity. -// Since a larger level means a more severe event, a logger that accepts events -// with smaller (or more negative) level means a more verbose logger. Logger -// verbosity is thus the negation of event severity, and the default verbosity -// of 0 accepts all events at least as severe as INFO. -// -// Third, we wanted some room between levels to accommodate schemes with named -// levels between ours. For example, Google Cloud Logging defines a Notice level -// between Info and Warn. Since there are only a few of these intermediate -// levels, the gap between the numbers need not be large. Our gap of 4 matches -// OpenTelemetry's mapping. Subtracting 9 from an OpenTelemetry level in the -// DEBUG, INFO, WARN and ERROR ranges converts it to the corresponding slog -// Level range. OpenTelemetry also has the names TRACE and FATAL, which slog -// does not. But those OpenTelemetry levels can still be represented as slog -// Levels by using the appropriate integers. -// -// Names for common levels. -const ( - LevelDebug Level = -4 - LevelInfo Level = 0 - LevelWarn Level = 4 - LevelError Level = 8 -) - -// String returns a name for the level. -// If the level has a name, then that name -// in uppercase is returned. -// If the level is between named values, then -// an integer is appended to the uppercased name. -// Examples: -// -// LevelWarn.String() => "WARN" -// (LevelInfo+2).String() => "INFO+2" -func (l Level) String() string { - str := func(base string, val Level) string { - if val == 0 { - return base - } - return fmt.Sprintf("%s%+d", base, val) - } - - switch { - case l < LevelInfo: - return str("DEBUG", l-LevelDebug) - case l < LevelWarn: - return str("INFO", l-LevelInfo) - case l < LevelError: - return str("WARN", l-LevelWarn) - default: - return str("ERROR", l-LevelError) - } -} - -// MarshalJSON implements [encoding/json.Marshaler] -// by quoting the output of [Level.String]. -func (l Level) MarshalJSON() ([]byte, error) { - // AppendQuote is sufficient for JSON-encoding all Level strings. - // They don't contain any runes that would produce invalid JSON - // when escaped. - return strconv.AppendQuote(nil, l.String()), nil -} - -// UnmarshalJSON implements [encoding/json.Unmarshaler] -// It accepts any string produced by [Level.MarshalJSON], -// ignoring case. -// It also accepts numeric offsets that would result in a different string on -// output. For example, "Error-8" would marshal as "INFO". -func (l *Level) UnmarshalJSON(data []byte) error { - s, err := strconv.Unquote(string(data)) - if err != nil { - return err - } - return l.parse(s) -} - -// MarshalText implements [encoding.TextMarshaler] -// by calling [Level.String]. -func (l Level) MarshalText() ([]byte, error) { - return []byte(l.String()), nil -} - -// UnmarshalText implements [encoding.TextUnmarshaler]. -// It accepts any string produced by [Level.MarshalText], -// ignoring case. -// It also accepts numeric offsets that would result in a different string on -// output. For example, "Error-8" would marshal as "INFO". -func (l *Level) UnmarshalText(data []byte) error { - return l.parse(string(data)) -} - -func (l *Level) parse(s string) (err error) { - defer func() { - if err != nil { - err = fmt.Errorf("slog: level string %q: %w", s, err) - } - }() - - name := s - offset := 0 - if i := strings.IndexAny(s, "+-"); i >= 0 { - name = s[:i] - offset, err = strconv.Atoi(s[i:]) - if err != nil { - return err - } - } - switch strings.ToUpper(name) { - case "DEBUG": - *l = LevelDebug - case "INFO": - *l = LevelInfo - case "WARN": - *l = LevelWarn - case "ERROR": - *l = LevelError - default: - return errors.New("unknown name") - } - *l += Level(offset) - return nil -} - -// Level returns the receiver. -// It implements Leveler. -func (l Level) Level() Level { return l } - -// A LevelVar is a Level variable, to allow a Handler level to change -// dynamically. -// It implements Leveler as well as a Set method, -// and it is safe for use by multiple goroutines. -// The zero LevelVar corresponds to LevelInfo. -type LevelVar struct { - val atomic.Int64 -} - -// Level returns v's level. -func (v *LevelVar) Level() Level { - return Level(int(v.val.Load())) -} - -// Set sets v's level to l. -func (v *LevelVar) Set(l Level) { - v.val.Store(int64(l)) -} - -func (v *LevelVar) String() string { - return fmt.Sprintf("LevelVar(%s)", v.Level()) -} - -// MarshalText implements [encoding.TextMarshaler] -// by calling [Level.MarshalText]. -func (v *LevelVar) MarshalText() ([]byte, error) { - return v.Level().MarshalText() -} - -// UnmarshalText implements [encoding.TextUnmarshaler] -// by calling [Level.UnmarshalText]. -func (v *LevelVar) UnmarshalText(data []byte) error { - var l Level - if err := l.UnmarshalText(data); err != nil { - return err - } - v.Set(l) - return nil -} - -// A Leveler provides a Level value. -// -// As Level itself implements Leveler, clients typically supply -// a Level value wherever a Leveler is needed, such as in HandlerOptions. -// Clients who need to vary the level dynamically can provide a more complex -// Leveler implementation such as *LevelVar. -type Leveler interface { - Level() Level -} diff --git a/vendor/golang.org/x/exp/slog/logger.go b/vendor/golang.org/x/exp/slog/logger.go deleted file mode 100644 index e87ec9936c..0000000000 --- a/vendor/golang.org/x/exp/slog/logger.go +++ /dev/null @@ -1,343 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package slog - -import ( - "context" - "log" - "runtime" - "sync/atomic" - "time" - - "golang.org/x/exp/slog/internal" -) - -var defaultLogger atomic.Value - -func init() { - defaultLogger.Store(New(newDefaultHandler(log.Output))) -} - -// Default returns the default Logger. -func Default() *Logger { return defaultLogger.Load().(*Logger) } - -// SetDefault makes l the default Logger. -// After this call, output from the log package's default Logger -// (as with [log.Print], etc.) will be logged at LevelInfo using l's Handler. -func SetDefault(l *Logger) { - defaultLogger.Store(l) - // If the default's handler is a defaultHandler, then don't use a handleWriter, - // or we'll deadlock as they both try to acquire the log default mutex. - // The defaultHandler will use whatever the log default writer is currently - // set to, which is correct. - // This can occur with SetDefault(Default()). - // See TestSetDefault. - if _, ok := l.Handler().(*defaultHandler); !ok { - capturePC := log.Flags()&(log.Lshortfile|log.Llongfile) != 0 - log.SetOutput(&handlerWriter{l.Handler(), LevelInfo, capturePC}) - log.SetFlags(0) // we want just the log message, no time or location - } -} - -// handlerWriter is an io.Writer that calls a Handler. -// It is used to link the default log.Logger to the default slog.Logger. -type handlerWriter struct { - h Handler - level Level - capturePC bool -} - -func (w *handlerWriter) Write(buf []byte) (int, error) { - if !w.h.Enabled(context.Background(), w.level) { - return 0, nil - } - var pc uintptr - if !internal.IgnorePC && w.capturePC { - // skip [runtime.Callers, w.Write, Logger.Output, log.Print] - var pcs [1]uintptr - runtime.Callers(4, pcs[:]) - pc = pcs[0] - } - - // Remove final newline. - origLen := len(buf) // Report that the entire buf was written. - if len(buf) > 0 && buf[len(buf)-1] == '\n' { - buf = buf[:len(buf)-1] - } - r := NewRecord(time.Now(), w.level, string(buf), pc) - return origLen, w.h.Handle(context.Background(), r) -} - -// A Logger records structured information about each call to its -// Log, Debug, Info, Warn, and Error methods. -// For each call, it creates a Record and passes it to a Handler. -// -// To create a new Logger, call [New] or a Logger method -// that begins "With". -type Logger struct { - handler Handler // for structured logging -} - -func (l *Logger) clone() *Logger { - c := *l - return &c -} - -// Handler returns l's Handler. -func (l *Logger) Handler() Handler { return l.handler } - -// With returns a new Logger that includes the given arguments, converted to -// Attrs as in [Logger.Log]. -// The Attrs will be added to each output from the Logger. -// The new Logger shares the old Logger's context. -// The new Logger's handler is the result of calling WithAttrs on the receiver's -// handler. -func (l *Logger) With(args ...any) *Logger { - c := l.clone() - c.handler = l.handler.WithAttrs(argsToAttrSlice(args)) - return c -} - -// WithGroup returns a new Logger that starts a group. The keys of all -// attributes added to the Logger will be qualified by the given name. -// (How that qualification happens depends on the [Handler.WithGroup] -// method of the Logger's Handler.) -// The new Logger shares the old Logger's context. -// -// The new Logger's handler is the result of calling WithGroup on the receiver's -// handler. -func (l *Logger) WithGroup(name string) *Logger { - c := l.clone() - c.handler = l.handler.WithGroup(name) - return c - -} - -// New creates a new Logger with the given non-nil Handler and a nil context. -func New(h Handler) *Logger { - if h == nil { - panic("nil Handler") - } - return &Logger{handler: h} -} - -// With calls Logger.With on the default logger. -func With(args ...any) *Logger { - return Default().With(args...) -} - -// Enabled reports whether l emits log records at the given context and level. -func (l *Logger) Enabled(ctx context.Context, level Level) bool { - if ctx == nil { - ctx = context.Background() - } - return l.Handler().Enabled(ctx, level) -} - -// NewLogLogger returns a new log.Logger such that each call to its Output method -// dispatches a Record to the specified handler. The logger acts as a bridge from -// the older log API to newer structured logging handlers. -func NewLogLogger(h Handler, level Level) *log.Logger { - return log.New(&handlerWriter{h, level, true}, "", 0) -} - -// Log emits a log record with the current time and the given level and message. -// The Record's Attrs consist of the Logger's attributes followed by -// the Attrs specified by args. -// -// The attribute arguments are processed as follows: -// - If an argument is an Attr, it is used as is. -// - If an argument is a string and this is not the last argument, -// the following argument is treated as the value and the two are combined -// into an Attr. -// - Otherwise, the argument is treated as a value with key "!BADKEY". -func (l *Logger) Log(ctx context.Context, level Level, msg string, args ...any) { - l.log(ctx, level, msg, args...) -} - -// LogAttrs is a more efficient version of [Logger.Log] that accepts only Attrs. -func (l *Logger) LogAttrs(ctx context.Context, level Level, msg string, attrs ...Attr) { - l.logAttrs(ctx, level, msg, attrs...) -} - -// Debug logs at LevelDebug. -func (l *Logger) Debug(msg string, args ...any) { - l.log(nil, LevelDebug, msg, args...) -} - -// DebugContext logs at LevelDebug with the given context. -func (l *Logger) DebugContext(ctx context.Context, msg string, args ...any) { - l.log(ctx, LevelDebug, msg, args...) -} - -// DebugCtx logs at LevelDebug with the given context. -// Deprecated: Use Logger.DebugContext. -func (l *Logger) DebugCtx(ctx context.Context, msg string, args ...any) { - l.log(ctx, LevelDebug, msg, args...) -} - -// Info logs at LevelInfo. -func (l *Logger) Info(msg string, args ...any) { - l.log(nil, LevelInfo, msg, args...) -} - -// InfoContext logs at LevelInfo with the given context. -func (l *Logger) InfoContext(ctx context.Context, msg string, args ...any) { - l.log(ctx, LevelInfo, msg, args...) -} - -// InfoCtx logs at LevelInfo with the given context. -// Deprecated: Use Logger.InfoContext. -func (l *Logger) InfoCtx(ctx context.Context, msg string, args ...any) { - l.log(ctx, LevelInfo, msg, args...) -} - -// Warn logs at LevelWarn. -func (l *Logger) Warn(msg string, args ...any) { - l.log(nil, LevelWarn, msg, args...) -} - -// WarnContext logs at LevelWarn with the given context. -func (l *Logger) WarnContext(ctx context.Context, msg string, args ...any) { - l.log(ctx, LevelWarn, msg, args...) -} - -// WarnCtx logs at LevelWarn with the given context. -// Deprecated: Use Logger.WarnContext. -func (l *Logger) WarnCtx(ctx context.Context, msg string, args ...any) { - l.log(ctx, LevelWarn, msg, args...) -} - -// Error logs at LevelError. -func (l *Logger) Error(msg string, args ...any) { - l.log(nil, LevelError, msg, args...) -} - -// ErrorContext logs at LevelError with the given context. -func (l *Logger) ErrorContext(ctx context.Context, msg string, args ...any) { - l.log(ctx, LevelError, msg, args...) -} - -// ErrorCtx logs at LevelError with the given context. -// Deprecated: Use Logger.ErrorContext. -func (l *Logger) ErrorCtx(ctx context.Context, msg string, args ...any) { - l.log(ctx, LevelError, msg, args...) -} - -// log is the low-level logging method for methods that take ...any. -// It must always be called directly by an exported logging method -// or function, because it uses a fixed call depth to obtain the pc. -func (l *Logger) log(ctx context.Context, level Level, msg string, args ...any) { - if !l.Enabled(ctx, level) { - return - } - var pc uintptr - if !internal.IgnorePC { - var pcs [1]uintptr - // skip [runtime.Callers, this function, this function's caller] - runtime.Callers(3, pcs[:]) - pc = pcs[0] - } - r := NewRecord(time.Now(), level, msg, pc) - r.Add(args...) - if ctx == nil { - ctx = context.Background() - } - _ = l.Handler().Handle(ctx, r) -} - -// logAttrs is like [Logger.log], but for methods that take ...Attr. -func (l *Logger) logAttrs(ctx context.Context, level Level, msg string, attrs ...Attr) { - if !l.Enabled(ctx, level) { - return - } - var pc uintptr - if !internal.IgnorePC { - var pcs [1]uintptr - // skip [runtime.Callers, this function, this function's caller] - runtime.Callers(3, pcs[:]) - pc = pcs[0] - } - r := NewRecord(time.Now(), level, msg, pc) - r.AddAttrs(attrs...) - if ctx == nil { - ctx = context.Background() - } - _ = l.Handler().Handle(ctx, r) -} - -// Debug calls Logger.Debug on the default logger. -func Debug(msg string, args ...any) { - Default().log(nil, LevelDebug, msg, args...) -} - -// DebugContext calls Logger.DebugContext on the default logger. -func DebugContext(ctx context.Context, msg string, args ...any) { - Default().log(ctx, LevelDebug, msg, args...) -} - -// Info calls Logger.Info on the default logger. -func Info(msg string, args ...any) { - Default().log(nil, LevelInfo, msg, args...) -} - -// InfoContext calls Logger.InfoContext on the default logger. -func InfoContext(ctx context.Context, msg string, args ...any) { - Default().log(ctx, LevelInfo, msg, args...) -} - -// Warn calls Logger.Warn on the default logger. -func Warn(msg string, args ...any) { - Default().log(nil, LevelWarn, msg, args...) -} - -// WarnContext calls Logger.WarnContext on the default logger. -func WarnContext(ctx context.Context, msg string, args ...any) { - Default().log(ctx, LevelWarn, msg, args...) -} - -// Error calls Logger.Error on the default logger. -func Error(msg string, args ...any) { - Default().log(nil, LevelError, msg, args...) -} - -// ErrorContext calls Logger.ErrorContext on the default logger. -func ErrorContext(ctx context.Context, msg string, args ...any) { - Default().log(ctx, LevelError, msg, args...) -} - -// DebugCtx calls Logger.DebugContext on the default logger. -// Deprecated: call DebugContext. -func DebugCtx(ctx context.Context, msg string, args ...any) { - Default().log(ctx, LevelDebug, msg, args...) -} - -// InfoCtx calls Logger.InfoContext on the default logger. -// Deprecated: call InfoContext. -func InfoCtx(ctx context.Context, msg string, args ...any) { - Default().log(ctx, LevelInfo, msg, args...) -} - -// WarnCtx calls Logger.WarnContext on the default logger. -// Deprecated: call WarnContext. -func WarnCtx(ctx context.Context, msg string, args ...any) { - Default().log(ctx, LevelWarn, msg, args...) -} - -// ErrorCtx calls Logger.ErrorContext on the default logger. -// Deprecated: call ErrorContext. -func ErrorCtx(ctx context.Context, msg string, args ...any) { - Default().log(ctx, LevelError, msg, args...) -} - -// Log calls Logger.Log on the default logger. -func Log(ctx context.Context, level Level, msg string, args ...any) { - Default().log(ctx, level, msg, args...) -} - -// LogAttrs calls Logger.LogAttrs on the default logger. -func LogAttrs(ctx context.Context, level Level, msg string, attrs ...Attr) { - Default().logAttrs(ctx, level, msg, attrs...) -} diff --git a/vendor/golang.org/x/exp/slog/noplog.bench b/vendor/golang.org/x/exp/slog/noplog.bench deleted file mode 100644 index ed9296ff61..0000000000 --- a/vendor/golang.org/x/exp/slog/noplog.bench +++ /dev/null @@ -1,36 +0,0 @@ -goos: linux -goarch: amd64 -pkg: golang.org/x/exp/slog -cpu: Intel(R) Xeon(R) CPU @ 2.20GHz -BenchmarkNopLog/attrs-8 1000000 1090 ns/op 0 B/op 0 allocs/op -BenchmarkNopLog/attrs-8 1000000 1097 ns/op 0 B/op 0 allocs/op -BenchmarkNopLog/attrs-8 1000000 1078 ns/op 0 B/op 0 allocs/op -BenchmarkNopLog/attrs-8 1000000 1095 ns/op 0 B/op 0 allocs/op -BenchmarkNopLog/attrs-8 1000000 1096 ns/op 0 B/op 0 allocs/op -BenchmarkNopLog/attrs-parallel-8 4007268 308.2 ns/op 0 B/op 0 allocs/op -BenchmarkNopLog/attrs-parallel-8 4016138 299.7 ns/op 0 B/op 0 allocs/op -BenchmarkNopLog/attrs-parallel-8 4020529 305.9 ns/op 0 B/op 0 allocs/op -BenchmarkNopLog/attrs-parallel-8 3977829 303.4 ns/op 0 B/op 0 allocs/op -BenchmarkNopLog/attrs-parallel-8 3225438 318.5 ns/op 0 B/op 0 allocs/op -BenchmarkNopLog/keys-values-8 1179256 994.2 ns/op 0 B/op 0 allocs/op -BenchmarkNopLog/keys-values-8 1000000 1002 ns/op 0 B/op 0 allocs/op -BenchmarkNopLog/keys-values-8 1216710 993.2 ns/op 0 B/op 0 allocs/op -BenchmarkNopLog/keys-values-8 1000000 1013 ns/op 0 B/op 0 allocs/op -BenchmarkNopLog/keys-values-8 1000000 1016 ns/op 0 B/op 0 allocs/op -BenchmarkNopLog/WithContext-8 989066 1163 ns/op 0 B/op 0 allocs/op -BenchmarkNopLog/WithContext-8 994116 1163 ns/op 0 B/op 0 allocs/op -BenchmarkNopLog/WithContext-8 1000000 1152 ns/op 0 B/op 0 allocs/op -BenchmarkNopLog/WithContext-8 991675 1165 ns/op 0 B/op 0 allocs/op -BenchmarkNopLog/WithContext-8 965268 1166 ns/op 0 B/op 0 allocs/op -BenchmarkNopLog/WithContext-parallel-8 3955503 303.3 ns/op 0 B/op 0 allocs/op -BenchmarkNopLog/WithContext-parallel-8 3861188 307.8 ns/op 0 B/op 0 allocs/op -BenchmarkNopLog/WithContext-parallel-8 3967752 303.9 ns/op 0 B/op 0 allocs/op -BenchmarkNopLog/WithContext-parallel-8 3955203 302.7 ns/op 0 B/op 0 allocs/op -BenchmarkNopLog/WithContext-parallel-8 3948278 301.1 ns/op 0 B/op 0 allocs/op -BenchmarkNopLog/Ctx-8 940622 1247 ns/op 0 B/op 0 allocs/op -BenchmarkNopLog/Ctx-8 936381 1257 ns/op 0 B/op 0 allocs/op -BenchmarkNopLog/Ctx-8 959730 1266 ns/op 0 B/op 0 allocs/op -BenchmarkNopLog/Ctx-8 943473 1290 ns/op 0 B/op 0 allocs/op -BenchmarkNopLog/Ctx-8 919414 1259 ns/op 0 B/op 0 allocs/op -PASS -ok golang.org/x/exp/slog 40.566s diff --git a/vendor/golang.org/x/exp/slog/record.go b/vendor/golang.org/x/exp/slog/record.go deleted file mode 100644 index 38b3440f77..0000000000 --- a/vendor/golang.org/x/exp/slog/record.go +++ /dev/null @@ -1,207 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package slog - -import ( - "runtime" - "time" - - "golang.org/x/exp/slices" -) - -const nAttrsInline = 5 - -// A Record holds information about a log event. -// Copies of a Record share state. -// Do not modify a Record after handing out a copy to it. -// Use [Record.Clone] to create a copy with no shared state. -type Record struct { - // The time at which the output method (Log, Info, etc.) was called. - Time time.Time - - // The log message. - Message string - - // The level of the event. - Level Level - - // The program counter at the time the record was constructed, as determined - // by runtime.Callers. If zero, no program counter is available. - // - // The only valid use for this value is as an argument to - // [runtime.CallersFrames]. In particular, it must not be passed to - // [runtime.FuncForPC]. - PC uintptr - - // Allocation optimization: an inline array sized to hold - // the majority of log calls (based on examination of open-source - // code). It holds the start of the list of Attrs. - front [nAttrsInline]Attr - - // The number of Attrs in front. - nFront int - - // The list of Attrs except for those in front. - // Invariants: - // - len(back) > 0 iff nFront == len(front) - // - Unused array elements are zero. Used to detect mistakes. - back []Attr -} - -// NewRecord creates a Record from the given arguments. -// Use [Record.AddAttrs] to add attributes to the Record. -// -// NewRecord is intended for logging APIs that want to support a [Handler] as -// a backend. -func NewRecord(t time.Time, level Level, msg string, pc uintptr) Record { - return Record{ - Time: t, - Message: msg, - Level: level, - PC: pc, - } -} - -// Clone returns a copy of the record with no shared state. -// The original record and the clone can both be modified -// without interfering with each other. -func (r Record) Clone() Record { - r.back = slices.Clip(r.back) // prevent append from mutating shared array - return r -} - -// NumAttrs returns the number of attributes in the Record. -func (r Record) NumAttrs() int { - return r.nFront + len(r.back) -} - -// Attrs calls f on each Attr in the Record. -// Iteration stops if f returns false. -func (r Record) Attrs(f func(Attr) bool) { - for i := 0; i < r.nFront; i++ { - if !f(r.front[i]) { - return - } - } - for _, a := range r.back { - if !f(a) { - return - } - } -} - -// AddAttrs appends the given Attrs to the Record's list of Attrs. -func (r *Record) AddAttrs(attrs ...Attr) { - n := copy(r.front[r.nFront:], attrs) - r.nFront += n - // Check if a copy was modified by slicing past the end - // and seeing if the Attr there is non-zero. - if cap(r.back) > len(r.back) { - end := r.back[:len(r.back)+1][len(r.back)] - if !end.isEmpty() { - panic("copies of a slog.Record were both modified") - } - } - r.back = append(r.back, attrs[n:]...) -} - -// Add converts the args to Attrs as described in [Logger.Log], -// then appends the Attrs to the Record's list of Attrs. -func (r *Record) Add(args ...any) { - var a Attr - for len(args) > 0 { - a, args = argsToAttr(args) - if r.nFront < len(r.front) { - r.front[r.nFront] = a - r.nFront++ - } else { - if r.back == nil { - r.back = make([]Attr, 0, countAttrs(args)) - } - r.back = append(r.back, a) - } - } - -} - -// countAttrs returns the number of Attrs that would be created from args. -func countAttrs(args []any) int { - n := 0 - for i := 0; i < len(args); i++ { - n++ - if _, ok := args[i].(string); ok { - i++ - } - } - return n -} - -const badKey = "!BADKEY" - -// argsToAttr turns a prefix of the nonempty args slice into an Attr -// and returns the unconsumed portion of the slice. -// If args[0] is an Attr, it returns it. -// If args[0] is a string, it treats the first two elements as -// a key-value pair. -// Otherwise, it treats args[0] as a value with a missing key. -func argsToAttr(args []any) (Attr, []any) { - switch x := args[0].(type) { - case string: - if len(args) == 1 { - return String(badKey, x), nil - } - return Any(x, args[1]), args[2:] - - case Attr: - return x, args[1:] - - default: - return Any(badKey, x), args[1:] - } -} - -// Source describes the location of a line of source code. -type Source struct { - // Function is the package path-qualified function name containing the - // source line. If non-empty, this string uniquely identifies a single - // function in the program. This may be the empty string if not known. - Function string `json:"function"` - // File and Line are the file name and line number (1-based) of the source - // line. These may be the empty string and zero, respectively, if not known. - File string `json:"file"` - Line int `json:"line"` -} - -// attrs returns the non-zero fields of s as a slice of attrs. -// It is similar to a LogValue method, but we don't want Source -// to implement LogValuer because it would be resolved before -// the ReplaceAttr function was called. -func (s *Source) group() Value { - var as []Attr - if s.Function != "" { - as = append(as, String("function", s.Function)) - } - if s.File != "" { - as = append(as, String("file", s.File)) - } - if s.Line != 0 { - as = append(as, Int("line", s.Line)) - } - return GroupValue(as...) -} - -// source returns a Source for the log event. -// If the Record was created without the necessary information, -// or if the location is unavailable, it returns a non-nil *Source -// with zero fields. -func (r Record) source() *Source { - fs := runtime.CallersFrames([]uintptr{r.PC}) - f, _ := fs.Next() - return &Source{ - Function: f.Function, - File: f.File, - Line: f.Line, - } -} diff --git a/vendor/golang.org/x/exp/slog/text_handler.go b/vendor/golang.org/x/exp/slog/text_handler.go deleted file mode 100644 index 75b66b716f..0000000000 --- a/vendor/golang.org/x/exp/slog/text_handler.go +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package slog - -import ( - "context" - "encoding" - "fmt" - "io" - "reflect" - "strconv" - "unicode" - "unicode/utf8" -) - -// TextHandler is a Handler that writes Records to an io.Writer as a -// sequence of key=value pairs separated by spaces and followed by a newline. -type TextHandler struct { - *commonHandler -} - -// NewTextHandler creates a TextHandler that writes to w, -// using the given options. -// If opts is nil, the default options are used. -func NewTextHandler(w io.Writer, opts *HandlerOptions) *TextHandler { - if opts == nil { - opts = &HandlerOptions{} - } - return &TextHandler{ - &commonHandler{ - json: false, - w: w, - opts: *opts, - }, - } -} - -// Enabled reports whether the handler handles records at the given level. -// The handler ignores records whose level is lower. -func (h *TextHandler) Enabled(_ context.Context, level Level) bool { - return h.commonHandler.enabled(level) -} - -// WithAttrs returns a new TextHandler whose attributes consists -// of h's attributes followed by attrs. -func (h *TextHandler) WithAttrs(attrs []Attr) Handler { - return &TextHandler{commonHandler: h.commonHandler.withAttrs(attrs)} -} - -func (h *TextHandler) WithGroup(name string) Handler { - return &TextHandler{commonHandler: h.commonHandler.withGroup(name)} -} - -// Handle formats its argument Record as a single line of space-separated -// key=value items. -// -// If the Record's time is zero, the time is omitted. -// Otherwise, the key is "time" -// and the value is output in RFC3339 format with millisecond precision. -// -// If the Record's level is zero, the level is omitted. -// Otherwise, the key is "level" -// and the value of [Level.String] is output. -// -// If the AddSource option is set and source information is available, -// the key is "source" and the value is output as FILE:LINE. -// -// The message's key is "msg". -// -// To modify these or other attributes, or remove them from the output, use -// [HandlerOptions.ReplaceAttr]. -// -// If a value implements [encoding.TextMarshaler], the result of MarshalText is -// written. Otherwise, the result of fmt.Sprint is written. -// -// Keys and values are quoted with [strconv.Quote] if they contain Unicode space -// characters, non-printing characters, '"' or '='. -// -// Keys inside groups consist of components (keys or group names) separated by -// dots. No further escaping is performed. -// Thus there is no way to determine from the key "a.b.c" whether there -// are two groups "a" and "b" and a key "c", or a single group "a.b" and a key "c", -// or single group "a" and a key "b.c". -// If it is necessary to reconstruct the group structure of a key -// even in the presence of dots inside components, use -// [HandlerOptions.ReplaceAttr] to encode that information in the key. -// -// Each call to Handle results in a single serialized call to -// io.Writer.Write. -func (h *TextHandler) Handle(_ context.Context, r Record) error { - return h.commonHandler.handle(r) -} - -func appendTextValue(s *handleState, v Value) error { - switch v.Kind() { - case KindString: - s.appendString(v.str()) - case KindTime: - s.appendTime(v.time()) - case KindAny: - if tm, ok := v.any.(encoding.TextMarshaler); ok { - data, err := tm.MarshalText() - if err != nil { - return err - } - // TODO: avoid the conversion to string. - s.appendString(string(data)) - return nil - } - if bs, ok := byteSlice(v.any); ok { - // As of Go 1.19, this only allocates for strings longer than 32 bytes. - s.buf.WriteString(strconv.Quote(string(bs))) - return nil - } - s.appendString(fmt.Sprintf("%+v", v.Any())) - default: - *s.buf = v.append(*s.buf) - } - return nil -} - -// byteSlice returns its argument as a []byte if the argument's -// underlying type is []byte, along with a second return value of true. -// Otherwise it returns nil, false. -func byteSlice(a any) ([]byte, bool) { - if bs, ok := a.([]byte); ok { - return bs, true - } - // Like Printf's %s, we allow both the slice type and the byte element type to be named. - t := reflect.TypeOf(a) - if t != nil && t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8 { - return reflect.ValueOf(a).Bytes(), true - } - return nil, false -} - -func needsQuoting(s string) bool { - if len(s) == 0 { - return true - } - for i := 0; i < len(s); { - b := s[i] - if b < utf8.RuneSelf { - // Quote anything except a backslash that would need quoting in a - // JSON string, as well as space and '=' - if b != '\\' && (b == ' ' || b == '=' || !safeSet[b]) { - return true - } - i++ - continue - } - r, size := utf8.DecodeRuneInString(s[i:]) - if r == utf8.RuneError || unicode.IsSpace(r) || !unicode.IsPrint(r) { - return true - } - i += size - } - return false -} diff --git a/vendor/golang.org/x/exp/slog/value.go b/vendor/golang.org/x/exp/slog/value.go deleted file mode 100644 index 3550c46fc0..0000000000 --- a/vendor/golang.org/x/exp/slog/value.go +++ /dev/null @@ -1,456 +0,0 @@ -// Copyright 2022 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -package slog - -import ( - "fmt" - "math" - "runtime" - "strconv" - "strings" - "time" - "unsafe" - - "golang.org/x/exp/slices" -) - -// A Value can represent any Go value, but unlike type any, -// it can represent most small values without an allocation. -// The zero Value corresponds to nil. -type Value struct { - _ [0]func() // disallow == - // num holds the value for Kinds Int64, Uint64, Float64, Bool and Duration, - // the string length for KindString, and nanoseconds since the epoch for KindTime. - num uint64 - // If any is of type Kind, then the value is in num as described above. - // If any is of type *time.Location, then the Kind is Time and time.Time value - // can be constructed from the Unix nanos in num and the location (monotonic time - // is not preserved). - // If any is of type stringptr, then the Kind is String and the string value - // consists of the length in num and the pointer in any. - // Otherwise, the Kind is Any and any is the value. - // (This implies that Attrs cannot store values of type Kind, *time.Location - // or stringptr.) - any any -} - -// Kind is the kind of a Value. -type Kind int - -// The following list is sorted alphabetically, but it's also important that -// KindAny is 0 so that a zero Value represents nil. - -const ( - KindAny Kind = iota - KindBool - KindDuration - KindFloat64 - KindInt64 - KindString - KindTime - KindUint64 - KindGroup - KindLogValuer -) - -var kindStrings = []string{ - "Any", - "Bool", - "Duration", - "Float64", - "Int64", - "String", - "Time", - "Uint64", - "Group", - "LogValuer", -} - -func (k Kind) String() string { - if k >= 0 && int(k) < len(kindStrings) { - return kindStrings[k] - } - return "" -} - -// Unexported version of Kind, just so we can store Kinds in Values. -// (No user-provided value has this type.) -type kind Kind - -// Kind returns v's Kind. -func (v Value) Kind() Kind { - switch x := v.any.(type) { - case Kind: - return x - case stringptr: - return KindString - case timeLocation: - return KindTime - case groupptr: - return KindGroup - case LogValuer: - return KindLogValuer - case kind: // a kind is just a wrapper for a Kind - return KindAny - default: - return KindAny - } -} - -//////////////// Constructors - -// IntValue returns a Value for an int. -func IntValue(v int) Value { - return Int64Value(int64(v)) -} - -// Int64Value returns a Value for an int64. -func Int64Value(v int64) Value { - return Value{num: uint64(v), any: KindInt64} -} - -// Uint64Value returns a Value for a uint64. -func Uint64Value(v uint64) Value { - return Value{num: v, any: KindUint64} -} - -// Float64Value returns a Value for a floating-point number. -func Float64Value(v float64) Value { - return Value{num: math.Float64bits(v), any: KindFloat64} -} - -// BoolValue returns a Value for a bool. -func BoolValue(v bool) Value { - u := uint64(0) - if v { - u = 1 - } - return Value{num: u, any: KindBool} -} - -// Unexported version of *time.Location, just so we can store *time.Locations in -// Values. (No user-provided value has this type.) -type timeLocation *time.Location - -// TimeValue returns a Value for a time.Time. -// It discards the monotonic portion. -func TimeValue(v time.Time) Value { - if v.IsZero() { - // UnixNano on the zero time is undefined, so represent the zero time - // with a nil *time.Location instead. time.Time.Location method never - // returns nil, so a Value with any == timeLocation(nil) cannot be - // mistaken for any other Value, time.Time or otherwise. - return Value{any: timeLocation(nil)} - } - return Value{num: uint64(v.UnixNano()), any: timeLocation(v.Location())} -} - -// DurationValue returns a Value for a time.Duration. -func DurationValue(v time.Duration) Value { - return Value{num: uint64(v.Nanoseconds()), any: KindDuration} -} - -// AnyValue returns a Value for the supplied value. -// -// If the supplied value is of type Value, it is returned -// unmodified. -// -// Given a value of one of Go's predeclared string, bool, or -// (non-complex) numeric types, AnyValue returns a Value of kind -// String, Bool, Uint64, Int64, or Float64. The width of the -// original numeric type is not preserved. -// -// Given a time.Time or time.Duration value, AnyValue returns a Value of kind -// KindTime or KindDuration. The monotonic time is not preserved. -// -// For nil, or values of all other types, including named types whose -// underlying type is numeric, AnyValue returns a value of kind KindAny. -func AnyValue(v any) Value { - switch v := v.(type) { - case string: - return StringValue(v) - case int: - return Int64Value(int64(v)) - case uint: - return Uint64Value(uint64(v)) - case int64: - return Int64Value(v) - case uint64: - return Uint64Value(v) - case bool: - return BoolValue(v) - case time.Duration: - return DurationValue(v) - case time.Time: - return TimeValue(v) - case uint8: - return Uint64Value(uint64(v)) - case uint16: - return Uint64Value(uint64(v)) - case uint32: - return Uint64Value(uint64(v)) - case uintptr: - return Uint64Value(uint64(v)) - case int8: - return Int64Value(int64(v)) - case int16: - return Int64Value(int64(v)) - case int32: - return Int64Value(int64(v)) - case float64: - return Float64Value(v) - case float32: - return Float64Value(float64(v)) - case []Attr: - return GroupValue(v...) - case Kind: - return Value{any: kind(v)} - case Value: - return v - default: - return Value{any: v} - } -} - -//////////////// Accessors - -// Any returns v's value as an any. -func (v Value) Any() any { - switch v.Kind() { - case KindAny: - if k, ok := v.any.(kind); ok { - return Kind(k) - } - return v.any - case KindLogValuer: - return v.any - case KindGroup: - return v.group() - case KindInt64: - return int64(v.num) - case KindUint64: - return v.num - case KindFloat64: - return v.float() - case KindString: - return v.str() - case KindBool: - return v.bool() - case KindDuration: - return v.duration() - case KindTime: - return v.time() - default: - panic(fmt.Sprintf("bad kind: %s", v.Kind())) - } -} - -// Int64 returns v's value as an int64. It panics -// if v is not a signed integer. -func (v Value) Int64() int64 { - if g, w := v.Kind(), KindInt64; g != w { - panic(fmt.Sprintf("Value kind is %s, not %s", g, w)) - } - return int64(v.num) -} - -// Uint64 returns v's value as a uint64. It panics -// if v is not an unsigned integer. -func (v Value) Uint64() uint64 { - if g, w := v.Kind(), KindUint64; g != w { - panic(fmt.Sprintf("Value kind is %s, not %s", g, w)) - } - return v.num -} - -// Bool returns v's value as a bool. It panics -// if v is not a bool. -func (v Value) Bool() bool { - if g, w := v.Kind(), KindBool; g != w { - panic(fmt.Sprintf("Value kind is %s, not %s", g, w)) - } - return v.bool() -} - -func (v Value) bool() bool { - return v.num == 1 -} - -// Duration returns v's value as a time.Duration. It panics -// if v is not a time.Duration. -func (v Value) Duration() time.Duration { - if g, w := v.Kind(), KindDuration; g != w { - panic(fmt.Sprintf("Value kind is %s, not %s", g, w)) - } - - return v.duration() -} - -func (v Value) duration() time.Duration { - return time.Duration(int64(v.num)) -} - -// Float64 returns v's value as a float64. It panics -// if v is not a float64. -func (v Value) Float64() float64 { - if g, w := v.Kind(), KindFloat64; g != w { - panic(fmt.Sprintf("Value kind is %s, not %s", g, w)) - } - - return v.float() -} - -func (v Value) float() float64 { - return math.Float64frombits(v.num) -} - -// Time returns v's value as a time.Time. It panics -// if v is not a time.Time. -func (v Value) Time() time.Time { - if g, w := v.Kind(), KindTime; g != w { - panic(fmt.Sprintf("Value kind is %s, not %s", g, w)) - } - return v.time() -} - -func (v Value) time() time.Time { - loc := v.any.(timeLocation) - if loc == nil { - return time.Time{} - } - return time.Unix(0, int64(v.num)).In(loc) -} - -// LogValuer returns v's value as a LogValuer. It panics -// if v is not a LogValuer. -func (v Value) LogValuer() LogValuer { - return v.any.(LogValuer) -} - -// Group returns v's value as a []Attr. -// It panics if v's Kind is not KindGroup. -func (v Value) Group() []Attr { - if sp, ok := v.any.(groupptr); ok { - return unsafe.Slice((*Attr)(sp), v.num) - } - panic("Group: bad kind") -} - -func (v Value) group() []Attr { - return unsafe.Slice((*Attr)(v.any.(groupptr)), v.num) -} - -//////////////// Other - -// Equal reports whether v and w represent the same Go value. -func (v Value) Equal(w Value) bool { - k1 := v.Kind() - k2 := w.Kind() - if k1 != k2 { - return false - } - switch k1 { - case KindInt64, KindUint64, KindBool, KindDuration: - return v.num == w.num - case KindString: - return v.str() == w.str() - case KindFloat64: - return v.float() == w.float() - case KindTime: - return v.time().Equal(w.time()) - case KindAny, KindLogValuer: - return v.any == w.any // may panic if non-comparable - case KindGroup: - return slices.EqualFunc(v.group(), w.group(), Attr.Equal) - default: - panic(fmt.Sprintf("bad kind: %s", k1)) - } -} - -// append appends a text representation of v to dst. -// v is formatted as with fmt.Sprint. -func (v Value) append(dst []byte) []byte { - switch v.Kind() { - case KindString: - return append(dst, v.str()...) - case KindInt64: - return strconv.AppendInt(dst, int64(v.num), 10) - case KindUint64: - return strconv.AppendUint(dst, v.num, 10) - case KindFloat64: - return strconv.AppendFloat(dst, v.float(), 'g', -1, 64) - case KindBool: - return strconv.AppendBool(dst, v.bool()) - case KindDuration: - return append(dst, v.duration().String()...) - case KindTime: - return append(dst, v.time().String()...) - case KindGroup: - return fmt.Append(dst, v.group()) - case KindAny, KindLogValuer: - return fmt.Append(dst, v.any) - default: - panic(fmt.Sprintf("bad kind: %s", v.Kind())) - } -} - -// A LogValuer is any Go value that can convert itself into a Value for logging. -// -// This mechanism may be used to defer expensive operations until they are -// needed, or to expand a single value into a sequence of components. -type LogValuer interface { - LogValue() Value -} - -const maxLogValues = 100 - -// Resolve repeatedly calls LogValue on v while it implements LogValuer, -// and returns the result. -// If v resolves to a group, the group's attributes' values are not recursively -// resolved. -// If the number of LogValue calls exceeds a threshold, a Value containing an -// error is returned. -// Resolve's return value is guaranteed not to be of Kind KindLogValuer. -func (v Value) Resolve() (rv Value) { - orig := v - defer func() { - if r := recover(); r != nil { - rv = AnyValue(fmt.Errorf("LogValue panicked\n%s", stack(3, 5))) - } - }() - - for i := 0; i < maxLogValues; i++ { - if v.Kind() != KindLogValuer { - return v - } - v = v.LogValuer().LogValue() - } - err := fmt.Errorf("LogValue called too many times on Value of type %T", orig.Any()) - return AnyValue(err) -} - -func stack(skip, nFrames int) string { - pcs := make([]uintptr, nFrames+1) - n := runtime.Callers(skip+1, pcs) - if n == 0 { - return "(no stack)" - } - frames := runtime.CallersFrames(pcs[:n]) - var b strings.Builder - i := 0 - for { - frame, more := frames.Next() - fmt.Fprintf(&b, "called from %s (%s:%d)\n", frame.Function, frame.File, frame.Line) - if !more { - break - } - i++ - if i >= nFrames { - fmt.Fprintf(&b, "(rest of stack elided)\n") - break - } - } - return b.String() -} diff --git a/vendor/golang.org/x/exp/slog/value_119.go b/vendor/golang.org/x/exp/slog/value_119.go deleted file mode 100644 index 29b0d73292..0000000000 --- a/vendor/golang.org/x/exp/slog/value_119.go +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2022 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. - -//go:build go1.19 && !go1.20 - -package slog - -import ( - "reflect" - "unsafe" -) - -type ( - stringptr unsafe.Pointer // used in Value.any when the Value is a string - groupptr unsafe.Pointer // used in Value.any when the Value is a []Attr -) - -// StringValue returns a new Value for a string. -func StringValue(value string) Value { - hdr := (*reflect.StringHeader)(unsafe.Pointer(&value)) - return Value{num: uint64(hdr.Len), any: stringptr(hdr.Data)} -} - -func (v Value) str() string { - var s string - hdr := (*reflect.StringHeader)(unsafe.Pointer(&s)) - hdr.Data = uintptr(v.any.(stringptr)) - hdr.Len = int(v.num) - return s -} - -// String returns Value's value as a string, formatted like fmt.Sprint. Unlike -// the methods Int64, Float64, and so on, which panic if v is of the -// wrong kind, String never panics. -func (v Value) String() string { - if sp, ok := v.any.(stringptr); ok { - // Inlining this code makes a huge difference. - var s string - hdr := (*reflect.StringHeader)(unsafe.Pointer(&s)) - hdr.Data = uintptr(sp) - hdr.Len = int(v.num) - return s - } - return string(v.append(nil)) -} - -// GroupValue returns a new Value for a list of Attrs. -// The caller must not subsequently mutate the argument slice. -func GroupValue(as ...Attr) Value { - hdr := (*reflect.SliceHeader)(unsafe.Pointer(&as)) - return Value{num: uint64(hdr.Len), any: groupptr(hdr.Data)} -} diff --git a/vendor/golang.org/x/exp/slog/value_120.go b/vendor/golang.org/x/exp/slog/value_120.go deleted file mode 100644 index f7d4c09325..0000000000 --- a/vendor/golang.org/x/exp/slog/value_120.go +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2022 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. - -//go:build go1.20 - -package slog - -import "unsafe" - -type ( - stringptr *byte // used in Value.any when the Value is a string - groupptr *Attr // used in Value.any when the Value is a []Attr -) - -// StringValue returns a new Value for a string. -func StringValue(value string) Value { - return Value{num: uint64(len(value)), any: stringptr(unsafe.StringData(value))} -} - -// GroupValue returns a new Value for a list of Attrs. -// The caller must not subsequently mutate the argument slice. -func GroupValue(as ...Attr) Value { - return Value{num: uint64(len(as)), any: groupptr(unsafe.SliceData(as))} -} - -// String returns Value's value as a string, formatted like fmt.Sprint. Unlike -// the methods Int64, Float64, and so on, which panic if v is of the -// wrong kind, String never panics. -func (v Value) String() string { - if sp, ok := v.any.(stringptr); ok { - return unsafe.String(sp, v.num) - } - return string(v.append(nil)) -} - -func (v Value) str() string { - return unsafe.String(v.any.(stringptr), v.num) -} diff --git a/vendor/golang.org/x/net/context/ctxhttp/ctxhttp.go b/vendor/golang.org/x/net/context/ctxhttp/ctxhttp.go index 37dc0cfdb5..e0df203cea 100644 --- a/vendor/golang.org/x/net/context/ctxhttp/ctxhttp.go +++ b/vendor/golang.org/x/net/context/ctxhttp/ctxhttp.go @@ -3,7 +3,7 @@ // license that can be found in the LICENSE file. // Package ctxhttp provides helper functions for performing context-aware HTTP requests. -package ctxhttp // import "golang.org/x/net/context/ctxhttp" +package ctxhttp import ( "context" diff --git a/vendor/golang.org/x/net/http2/server.go b/vendor/golang.org/x/net/http2/server.go index 7434b87843..b640deb0e0 100644 --- a/vendor/golang.org/x/net/http2/server.go +++ b/vendor/golang.org/x/net/http2/server.go @@ -2233,25 +2233,25 @@ func (sc *serverConn) newStream(id, pusherID uint32, state streamState) *stream func (sc *serverConn) newWriterAndRequest(st *stream, f *MetaHeadersFrame) (*responseWriter, *http.Request, error) { sc.serveG.check() - rp := requestParam{ - method: f.PseudoValue("method"), - scheme: f.PseudoValue("scheme"), - authority: f.PseudoValue("authority"), - path: f.PseudoValue("path"), - protocol: f.PseudoValue("protocol"), + rp := httpcommon.ServerRequestParam{ + Method: f.PseudoValue("method"), + Scheme: f.PseudoValue("scheme"), + Authority: f.PseudoValue("authority"), + Path: f.PseudoValue("path"), + Protocol: f.PseudoValue("protocol"), } // extended connect is disabled, so we should not see :protocol - if disableExtendedConnectProtocol && rp.protocol != "" { + if disableExtendedConnectProtocol && rp.Protocol != "" { return nil, nil, sc.countError("bad_connect", streamError(f.StreamID, ErrCodeProtocol)) } - isConnect := rp.method == "CONNECT" + isConnect := rp.Method == "CONNECT" if isConnect { - if rp.protocol == "" && (rp.path != "" || rp.scheme != "" || rp.authority == "") { + if rp.Protocol == "" && (rp.Path != "" || rp.Scheme != "" || rp.Authority == "") { return nil, nil, sc.countError("bad_connect", streamError(f.StreamID, ErrCodeProtocol)) } - } else if rp.method == "" || rp.path == "" || (rp.scheme != "https" && rp.scheme != "http") { + } else if rp.Method == "" || rp.Path == "" || (rp.Scheme != "https" && rp.Scheme != "http") { // See 8.1.2.6 Malformed Requests and Responses: // // Malformed requests or responses that are detected @@ -2265,15 +2265,16 @@ func (sc *serverConn) newWriterAndRequest(st *stream, f *MetaHeadersFrame) (*res return nil, nil, sc.countError("bad_path_method", streamError(f.StreamID, ErrCodeProtocol)) } - rp.header = make(http.Header) + header := make(http.Header) + rp.Header = header for _, hf := range f.RegularFields() { - rp.header.Add(sc.canonicalHeader(hf.Name), hf.Value) + header.Add(sc.canonicalHeader(hf.Name), hf.Value) } - if rp.authority == "" { - rp.authority = rp.header.Get("Host") + if rp.Authority == "" { + rp.Authority = header.Get("Host") } - if rp.protocol != "" { - rp.header.Set(":protocol", rp.protocol) + if rp.Protocol != "" { + header.Set(":protocol", rp.Protocol) } rw, req, err := sc.newWriterAndRequestNoBody(st, rp) @@ -2282,7 +2283,7 @@ func (sc *serverConn) newWriterAndRequest(st *stream, f *MetaHeadersFrame) (*res } bodyOpen := !f.StreamEnded() if bodyOpen { - if vv, ok := rp.header["Content-Length"]; ok { + if vv, ok := rp.Header["Content-Length"]; ok { if cl, err := strconv.ParseUint(vv[0], 10, 63); err == nil { req.ContentLength = int64(cl) } else { @@ -2298,84 +2299,38 @@ func (sc *serverConn) newWriterAndRequest(st *stream, f *MetaHeadersFrame) (*res return rw, req, nil } -type requestParam struct { - method string - scheme, authority, path string - protocol string - header http.Header -} - -func (sc *serverConn) newWriterAndRequestNoBody(st *stream, rp requestParam) (*responseWriter, *http.Request, error) { +func (sc *serverConn) newWriterAndRequestNoBody(st *stream, rp httpcommon.ServerRequestParam) (*responseWriter, *http.Request, error) { sc.serveG.check() var tlsState *tls.ConnectionState // nil if not scheme https - if rp.scheme == "https" { + if rp.Scheme == "https" { tlsState = sc.tlsState } - needsContinue := httpguts.HeaderValuesContainsToken(rp.header["Expect"], "100-continue") - if needsContinue { - rp.header.Del("Expect") - } - // Merge Cookie headers into one "; "-delimited value. - if cookies := rp.header["Cookie"]; len(cookies) > 1 { - rp.header.Set("Cookie", strings.Join(cookies, "; ")) - } - - // Setup Trailers - var trailer http.Header - for _, v := range rp.header["Trailer"] { - for _, key := range strings.Split(v, ",") { - key = http.CanonicalHeaderKey(textproto.TrimString(key)) - switch key { - case "Transfer-Encoding", "Trailer", "Content-Length": - // Bogus. (copy of http1 rules) - // Ignore. - default: - if trailer == nil { - trailer = make(http.Header) - } - trailer[key] = nil - } - } - } - delete(rp.header, "Trailer") - - var url_ *url.URL - var requestURI string - if rp.method == "CONNECT" && rp.protocol == "" { - url_ = &url.URL{Host: rp.authority} - requestURI = rp.authority // mimic HTTP/1 server behavior - } else { - var err error - url_, err = url.ParseRequestURI(rp.path) - if err != nil { - return nil, nil, sc.countError("bad_path", streamError(st.id, ErrCodeProtocol)) - } - requestURI = rp.path + res := httpcommon.NewServerRequest(rp) + if res.InvalidReason != "" { + return nil, nil, sc.countError(res.InvalidReason, streamError(st.id, ErrCodeProtocol)) } body := &requestBody{ conn: sc, stream: st, - needsContinue: needsContinue, + needsContinue: res.NeedsContinue, } - req := &http.Request{ - Method: rp.method, - URL: url_, + req := (&http.Request{ + Method: rp.Method, + URL: res.URL, RemoteAddr: sc.remoteAddrStr, - Header: rp.header, - RequestURI: requestURI, + Header: rp.Header, + RequestURI: res.RequestURI, Proto: "HTTP/2.0", ProtoMajor: 2, ProtoMinor: 0, TLS: tlsState, - Host: rp.authority, + Host: rp.Authority, Body: body, - Trailer: trailer, - } - req = req.WithContext(st.ctx) - + Trailer: res.Trailer, + }).WithContext(st.ctx) rw := sc.newResponseWriter(st, req) return rw, req, nil } @@ -3270,12 +3225,12 @@ func (sc *serverConn) startPush(msg *startPushRequest) { // we start in "half closed (remote)" for simplicity. // See further comments at the definition of stateHalfClosedRemote. promised := sc.newStream(promisedID, msg.parent.id, stateHalfClosedRemote) - rw, req, err := sc.newWriterAndRequestNoBody(promised, requestParam{ - method: msg.method, - scheme: msg.url.Scheme, - authority: msg.url.Host, - path: msg.url.RequestURI(), - header: cloneHeader(msg.header), // clone since handler runs concurrently with writing the PUSH_PROMISE + rw, req, err := sc.newWriterAndRequestNoBody(promised, httpcommon.ServerRequestParam{ + Method: msg.method, + Scheme: msg.url.Scheme, + Authority: msg.url.Host, + Path: msg.url.RequestURI(), + Header: cloneHeader(msg.header), // clone since handler runs concurrently with writing the PUSH_PROMISE }) if err != nil { // Should not happen, since we've already validated msg.url. diff --git a/vendor/golang.org/x/net/http2/transport.go b/vendor/golang.org/x/net/http2/transport.go index f2c166b615..f26356b9cd 100644 --- a/vendor/golang.org/x/net/http2/transport.go +++ b/vendor/golang.org/x/net/http2/transport.go @@ -1286,6 +1286,19 @@ func (cc *ClientConn) responseHeaderTimeout() time.Duration { return 0 } +// actualContentLength returns a sanitized version of +// req.ContentLength, where 0 actually means zero (not unknown) and -1 +// means unknown. +func actualContentLength(req *http.Request) int64 { + if req.Body == nil || req.Body == http.NoBody { + return 0 + } + if req.ContentLength != 0 { + return req.ContentLength + } + return -1 +} + func (cc *ClientConn) decrStreamReservations() { cc.mu.Lock() defer cc.mu.Unlock() @@ -1310,7 +1323,7 @@ func (cc *ClientConn) roundTrip(req *http.Request, streamf func(*clientStream)) reqCancel: req.Cancel, isHead: req.Method == "HEAD", reqBody: req.Body, - reqBodyContentLength: httpcommon.ActualContentLength(req), + reqBodyContentLength: actualContentLength(req), trace: httptrace.ContextClientTrace(ctx), peerClosed: make(chan struct{}), abort: make(chan struct{}), @@ -1318,7 +1331,7 @@ func (cc *ClientConn) roundTrip(req *http.Request, streamf func(*clientStream)) donec: make(chan struct{}), } - cs.requestedGzip = httpcommon.IsRequestGzip(req, cc.t.disableCompression()) + cs.requestedGzip = httpcommon.IsRequestGzip(req.Method, req.Header, cc.t.disableCompression()) go cs.doRequest(req, streamf) @@ -1349,7 +1362,7 @@ func (cc *ClientConn) roundTrip(req *http.Request, streamf func(*clientStream)) } res.Request = req res.TLS = cc.tlsState - if res.Body == noBody && httpcommon.ActualContentLength(req) == 0 { + if res.Body == noBody && actualContentLength(req) == 0 { // If there isn't a request or response body still being // written, then wait for the stream to be closed before // RoundTrip returns. @@ -1596,12 +1609,7 @@ func (cs *clientStream) encodeAndWriteHeaders(req *http.Request) error { // sent by writeRequestBody below, along with any Trailers, // again in form HEADERS{1}, CONTINUATION{0,}) cc.hbuf.Reset() - res, err := httpcommon.EncodeHeaders(httpcommon.EncodeHeadersParam{ - Request: req, - AddGzipHeader: cs.requestedGzip, - PeerMaxHeaderListSize: cc.peerMaxHeaderListSize, - DefaultUserAgent: defaultUserAgent, - }, func(name, value string) { + res, err := encodeRequestHeaders(req, cs.requestedGzip, cc.peerMaxHeaderListSize, func(name, value string) { cc.writeHeader(name, value) }) if err != nil { @@ -1617,6 +1625,22 @@ func (cs *clientStream) encodeAndWriteHeaders(req *http.Request) error { return err } +func encodeRequestHeaders(req *http.Request, addGzipHeader bool, peerMaxHeaderListSize uint64, headerf func(name, value string)) (httpcommon.EncodeHeadersResult, error) { + return httpcommon.EncodeHeaders(req.Context(), httpcommon.EncodeHeadersParam{ + Request: httpcommon.Request{ + Header: req.Header, + Trailer: req.Trailer, + URL: req.URL, + Host: req.Host, + Method: req.Method, + ActualContentLength: actualContentLength(req), + }, + AddGzipHeader: addGzipHeader, + PeerMaxHeaderListSize: peerMaxHeaderListSize, + DefaultUserAgent: defaultUserAgent, + }, headerf) +} + // cleanupWriteRequest performs post-request tasks. // // If err (the result of writeRequest) is non-nil and the stream is not closed, @@ -2186,6 +2210,13 @@ func (rl *clientConnReadLoop) cleanup() { } cc.cond.Broadcast() cc.mu.Unlock() + + if !cc.seenSettings { + // If we have a pending request that wants extended CONNECT, + // let it continue and fail with the connection error. + cc.extendedConnectAllowed = true + close(cc.seenSettingsChan) + } } // countReadFrameError calls Transport.CountError with a string @@ -2278,9 +2309,6 @@ func (rl *clientConnReadLoop) run() error { if VerboseLogs { cc.vlogf("http2: Transport conn %p received error from processing frame %v: %v", cc, summarizeFrame(f), err) } - if !cc.seenSettings { - close(cc.seenSettingsChan) - } return err } } diff --git a/vendor/golang.org/x/net/internal/httpcommon/headermap.go b/vendor/golang.org/x/net/internal/httpcommon/headermap.go index ad3fbacd60..92483d8e41 100644 --- a/vendor/golang.org/x/net/internal/httpcommon/headermap.go +++ b/vendor/golang.org/x/net/internal/httpcommon/headermap.go @@ -5,7 +5,7 @@ package httpcommon import ( - "net/http" + "net/textproto" "sync" ) @@ -82,7 +82,7 @@ func buildCommonHeaderMaps() { commonLowerHeader = make(map[string]string, len(common)) commonCanonHeader = make(map[string]string, len(common)) for _, v := range common { - chk := http.CanonicalHeaderKey(v) + chk := textproto.CanonicalMIMEHeaderKey(v) commonLowerHeader[chk] = v commonCanonHeader[v] = chk } @@ -104,7 +104,7 @@ func CanonicalHeader(v string) string { if s, ok := commonCanonHeader[v]; ok { return s } - return http.CanonicalHeaderKey(v) + return textproto.CanonicalMIMEHeaderKey(v) } // CachedCanonicalHeader returns the canonical form of a well-known header name. diff --git a/vendor/golang.org/x/net/internal/httpcommon/request.go b/vendor/golang.org/x/net/internal/httpcommon/request.go index 3439147738..4b70553179 100644 --- a/vendor/golang.org/x/net/internal/httpcommon/request.go +++ b/vendor/golang.org/x/net/internal/httpcommon/request.go @@ -5,10 +5,12 @@ package httpcommon import ( + "context" "errors" "fmt" - "net/http" "net/http/httptrace" + "net/textproto" + "net/url" "sort" "strconv" "strings" @@ -21,9 +23,21 @@ var ( ErrRequestHeaderListSize = errors.New("request header list larger than peer's advertised limit") ) +// Request is a subset of http.Request. +// It'd be simpler to pass an *http.Request, of course, but we can't depend on net/http +// without creating a dependency cycle. +type Request struct { + URL *url.URL + Method string + Host string + Header map[string][]string + Trailer map[string][]string + ActualContentLength int64 // 0 means 0, -1 means unknown +} + // EncodeHeadersParam is parameters to EncodeHeaders. type EncodeHeadersParam struct { - Request *http.Request + Request Request // AddGzipHeader indicates that an "accept-encoding: gzip" header should be // added to the request. @@ -47,11 +61,11 @@ type EncodeHeadersResult struct { // It validates a request and calls headerf with each pseudo-header and header // for the request. // The headerf function is called with the validated, canonicalized header name. -func EncodeHeaders(param EncodeHeadersParam, headerf func(name, value string)) (res EncodeHeadersResult, _ error) { +func EncodeHeaders(ctx context.Context, param EncodeHeadersParam, headerf func(name, value string)) (res EncodeHeadersResult, _ error) { req := param.Request // Check for invalid connection-level headers. - if err := checkConnHeaders(req); err != nil { + if err := checkConnHeaders(req.Header); err != nil { return res, err } @@ -73,7 +87,10 @@ func EncodeHeaders(param EncodeHeadersParam, headerf func(name, value string)) ( // isNormalConnect is true if this is a non-extended CONNECT request. isNormalConnect := false - protocol := req.Header.Get(":protocol") + var protocol string + if vv := req.Header[":protocol"]; len(vv) > 0 { + protocol = vv[0] + } if req.Method == "CONNECT" && protocol == "" { isNormalConnect = true } else if protocol != "" && req.Method != "CONNECT" { @@ -107,9 +124,7 @@ func EncodeHeaders(param EncodeHeadersParam, headerf func(name, value string)) ( return res, fmt.Errorf("invalid HTTP trailer %s", err) } - contentLength := ActualContentLength(req) - - trailers, err := commaSeparatedTrailers(req) + trailers, err := commaSeparatedTrailers(req.Trailer) if err != nil { return res, err } @@ -123,7 +138,7 @@ func EncodeHeaders(param EncodeHeadersParam, headerf func(name, value string)) ( f(":authority", host) m := req.Method if m == "" { - m = http.MethodGet + m = "GET" } f(":method", m) if !isNormalConnect { @@ -198,8 +213,8 @@ func EncodeHeaders(param EncodeHeadersParam, headerf func(name, value string)) ( f(k, v) } } - if shouldSendReqContentLength(req.Method, contentLength) { - f("content-length", strconv.FormatInt(contentLength, 10)) + if shouldSendReqContentLength(req.Method, req.ActualContentLength) { + f("content-length", strconv.FormatInt(req.ActualContentLength, 10)) } if param.AddGzipHeader { f("accept-encoding", "gzip") @@ -225,7 +240,7 @@ func EncodeHeaders(param EncodeHeadersParam, headerf func(name, value string)) ( } } - trace := httptrace.ContextClientTrace(req.Context()) + trace := httptrace.ContextClientTrace(ctx) // Header list size is ok. Write the headers. enumerateHeaders(func(name, value string) { @@ -243,19 +258,19 @@ func EncodeHeaders(param EncodeHeadersParam, headerf func(name, value string)) ( } }) - res.HasBody = contentLength != 0 + res.HasBody = req.ActualContentLength != 0 res.HasTrailers = trailers != "" return res, nil } // IsRequestGzip reports whether we should add an Accept-Encoding: gzip header // for a request. -func IsRequestGzip(req *http.Request, disableCompression bool) bool { +func IsRequestGzip(method string, header map[string][]string, disableCompression bool) bool { // TODO(bradfitz): this is a copy of the logic in net/http. Unify somewhere? if !disableCompression && - req.Header.Get("Accept-Encoding") == "" && - req.Header.Get("Range") == "" && - req.Method != "HEAD" { + len(header["Accept-Encoding"]) == 0 && + len(header["Range"]) == 0 && + method != "HEAD" { // Request gzip only, not deflate. Deflate is ambiguous and // not as universally supported anyway. // See: https://zlib.net/zlib_faq.html#faq39 @@ -280,22 +295,22 @@ func IsRequestGzip(req *http.Request, disableCompression bool) bool { // // Certain headers are special-cased as okay but not transmitted later. // For example, we allow "Transfer-Encoding: chunked", but drop the header when encoding. -func checkConnHeaders(req *http.Request) error { - if v := req.Header.Get("Upgrade"); v != "" { - return fmt.Errorf("invalid Upgrade request header: %q", req.Header["Upgrade"]) +func checkConnHeaders(h map[string][]string) error { + if vv := h["Upgrade"]; len(vv) > 0 && (vv[0] != "" && vv[0] != "chunked") { + return fmt.Errorf("invalid Upgrade request header: %q", vv) } - if vv := req.Header["Transfer-Encoding"]; len(vv) > 0 && (len(vv) > 1 || vv[0] != "" && vv[0] != "chunked") { + if vv := h["Transfer-Encoding"]; len(vv) > 0 && (len(vv) > 1 || vv[0] != "" && vv[0] != "chunked") { return fmt.Errorf("invalid Transfer-Encoding request header: %q", vv) } - if vv := req.Header["Connection"]; len(vv) > 0 && (len(vv) > 1 || vv[0] != "" && !asciiEqualFold(vv[0], "close") && !asciiEqualFold(vv[0], "keep-alive")) { + if vv := h["Connection"]; len(vv) > 0 && (len(vv) > 1 || vv[0] != "" && !asciiEqualFold(vv[0], "close") && !asciiEqualFold(vv[0], "keep-alive")) { return fmt.Errorf("invalid Connection request header: %q", vv) } return nil } -func commaSeparatedTrailers(req *http.Request) (string, error) { - keys := make([]string, 0, len(req.Trailer)) - for k := range req.Trailer { +func commaSeparatedTrailers(trailer map[string][]string) (string, error) { + keys := make([]string, 0, len(trailer)) + for k := range trailer { k = CanonicalHeader(k) switch k { case "Transfer-Encoding", "Trailer", "Content-Length": @@ -310,19 +325,6 @@ func commaSeparatedTrailers(req *http.Request) (string, error) { return "", nil } -// ActualContentLength returns a sanitized version of -// req.ContentLength, where 0 actually means zero (not unknown) and -1 -// means unknown. -func ActualContentLength(req *http.Request) int64 { - if req.Body == nil || req.Body == http.NoBody { - return 0 - } - if req.ContentLength != 0 { - return req.ContentLength - } - return -1 -} - // validPseudoPath reports whether v is a valid :path pseudo-header // value. It must be either: // @@ -340,7 +342,7 @@ func validPseudoPath(v string) bool { return (len(v) > 0 && v[0] == '/') || v == "*" } -func validateHeaders(hdrs http.Header) string { +func validateHeaders(hdrs map[string][]string) string { for k, vv := range hdrs { if !httpguts.ValidHeaderFieldName(k) && k != ":protocol" { return fmt.Sprintf("name %q", k) @@ -377,3 +379,89 @@ func shouldSendReqContentLength(method string, contentLength int64) bool { return false } } + +// ServerRequestParam is parameters to NewServerRequest. +type ServerRequestParam struct { + Method string + Scheme, Authority, Path string + Protocol string + Header map[string][]string +} + +// ServerRequestResult is the result of NewServerRequest. +type ServerRequestResult struct { + // Various http.Request fields. + URL *url.URL + RequestURI string + Trailer map[string][]string + + NeedsContinue bool // client provided an "Expect: 100-continue" header + + // If the request should be rejected, this is a short string suitable for passing + // to the http2 package's CountError function. + // It might be a bit odd to return errors this way rather than returing an error, + // but this ensures we don't forget to include a CountError reason. + InvalidReason string +} + +func NewServerRequest(rp ServerRequestParam) ServerRequestResult { + needsContinue := httpguts.HeaderValuesContainsToken(rp.Header["Expect"], "100-continue") + if needsContinue { + delete(rp.Header, "Expect") + } + // Merge Cookie headers into one "; "-delimited value. + if cookies := rp.Header["Cookie"]; len(cookies) > 1 { + rp.Header["Cookie"] = []string{strings.Join(cookies, "; ")} + } + + // Setup Trailers + var trailer map[string][]string + for _, v := range rp.Header["Trailer"] { + for _, key := range strings.Split(v, ",") { + key = textproto.CanonicalMIMEHeaderKey(textproto.TrimString(key)) + switch key { + case "Transfer-Encoding", "Trailer", "Content-Length": + // Bogus. (copy of http1 rules) + // Ignore. + default: + if trailer == nil { + trailer = make(map[string][]string) + } + trailer[key] = nil + } + } + } + delete(rp.Header, "Trailer") + + // "':authority' MUST NOT include the deprecated userinfo subcomponent + // for "http" or "https" schemed URIs." + // https://www.rfc-editor.org/rfc/rfc9113.html#section-8.3.1-2.3.8 + if strings.IndexByte(rp.Authority, '@') != -1 && (rp.Scheme == "http" || rp.Scheme == "https") { + return ServerRequestResult{ + InvalidReason: "userinfo_in_authority", + } + } + + var url_ *url.URL + var requestURI string + if rp.Method == "CONNECT" && rp.Protocol == "" { + url_ = &url.URL{Host: rp.Authority} + requestURI = rp.Authority // mimic HTTP/1 server behavior + } else { + var err error + url_, err = url.ParseRequestURI(rp.Path) + if err != nil { + return ServerRequestResult{ + InvalidReason: "bad_path", + } + } + requestURI = rp.Path + } + + return ServerRequestResult{ + URL: url_, + NeedsContinue: needsContinue, + RequestURI: requestURI, + Trailer: trailer, + } +} diff --git a/vendor/golang.org/x/net/proxy/per_host.go b/vendor/golang.org/x/net/proxy/per_host.go index d7d4b8b6e3..32bdf435ec 100644 --- a/vendor/golang.org/x/net/proxy/per_host.go +++ b/vendor/golang.org/x/net/proxy/per_host.go @@ -7,6 +7,7 @@ package proxy import ( "context" "net" + "net/netip" "strings" ) @@ -57,7 +58,8 @@ func (p *PerHost) DialContext(ctx context.Context, network, addr string) (c net. } func (p *PerHost) dialerForRequest(host string) Dialer { - if ip := net.ParseIP(host); ip != nil { + if nip, err := netip.ParseAddr(host); err == nil { + ip := net.IP(nip.AsSlice()) for _, net := range p.bypassNetworks { if net.Contains(ip) { return p.bypass @@ -108,8 +110,8 @@ func (p *PerHost) AddFromString(s string) { } continue } - if ip := net.ParseIP(host); ip != nil { - p.AddIP(ip) + if nip, err := netip.ParseAddr(host); err == nil { + p.AddIP(net.IP(nip.AsSlice())) continue } if strings.HasPrefix(host, "*.") { diff --git a/vendor/golang.org/x/oauth2/oauth2.go b/vendor/golang.org/x/oauth2/oauth2.go index 74f052aa9f..eacdd7fd93 100644 --- a/vendor/golang.org/x/oauth2/oauth2.go +++ b/vendor/golang.org/x/oauth2/oauth2.go @@ -288,7 +288,7 @@ func (tf *tokenRefresher) Token() (*Token, error) { if tf.refreshToken != tk.RefreshToken { tf.refreshToken = tk.RefreshToken } - return tk, err + return tk, nil } // reuseTokenSource is a TokenSource that holds a single token in memory @@ -356,11 +356,15 @@ func NewClient(ctx context.Context, src TokenSource) *http.Client { if src == nil { return internal.ContextClient(ctx) } + cc := internal.ContextClient(ctx) return &http.Client{ Transport: &Transport{ - Base: internal.ContextClient(ctx).Transport, + Base: cc.Transport, Source: ReuseTokenSource(nil, src), }, + CheckRedirect: cc.CheckRedirect, + Jar: cc.Jar, + Timeout: cc.Timeout, } } diff --git a/vendor/golang.org/x/oauth2/pkce.go b/vendor/golang.org/x/oauth2/pkce.go index 50593b6dfe..6a95da975c 100644 --- a/vendor/golang.org/x/oauth2/pkce.go +++ b/vendor/golang.org/x/oauth2/pkce.go @@ -21,7 +21,7 @@ const ( // // A fresh verifier should be generated for each authorization. // S256ChallengeOption(verifier) should then be passed to Config.AuthCodeURL -// (or Config.DeviceAccess) and VerifierOption(verifier) to Config.Exchange +// (or Config.DeviceAuth) and VerifierOption(verifier) to Config.Exchange // (or Config.DeviceAccessToken). func GenerateVerifier() string { // "RECOMMENDED that the output of a suitable random number generator be @@ -51,7 +51,7 @@ func S256ChallengeFromVerifier(verifier string) string { } // S256ChallengeOption derives a PKCE code challenge derived from verifier with -// method S256. It should be passed to Config.AuthCodeURL or Config.DeviceAccess +// method S256. It should be passed to Config.AuthCodeURL or Config.DeviceAuth // only. func S256ChallengeOption(verifier string) AuthCodeOption { return challengeOption{ diff --git a/vendor/golang.org/x/sync/errgroup/errgroup.go b/vendor/golang.org/x/sync/errgroup/errgroup.go index b8322598ae..a4ea5d14f1 100644 --- a/vendor/golang.org/x/sync/errgroup/errgroup.go +++ b/vendor/golang.org/x/sync/errgroup/errgroup.go @@ -46,7 +46,7 @@ func (g *Group) done() { // returns a non-nil error or the first time Wait returns, whichever occurs // first. func WithContext(ctx context.Context) (*Group, context.Context) { - ctx, cancel := withCancelCause(ctx) + ctx, cancel := context.WithCancelCause(ctx) return &Group{cancel: cancel}, ctx } diff --git a/vendor/golang.org/x/sync/errgroup/go120.go b/vendor/golang.org/x/sync/errgroup/go120.go deleted file mode 100644 index f93c740b63..0000000000 --- a/vendor/golang.org/x/sync/errgroup/go120.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2023 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. - -//go:build go1.20 - -package errgroup - -import "context" - -func withCancelCause(parent context.Context) (context.Context, func(error)) { - return context.WithCancelCause(parent) -} diff --git a/vendor/golang.org/x/sync/errgroup/pre_go120.go b/vendor/golang.org/x/sync/errgroup/pre_go120.go deleted file mode 100644 index 88ce33434e..0000000000 --- a/vendor/golang.org/x/sync/errgroup/pre_go120.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2023 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. - -//go:build !go1.20 - -package errgroup - -import "context" - -func withCancelCause(parent context.Context) (context.Context, func(error)) { - ctx, cancel := context.WithCancel(parent) - return ctx, func(error) { cancel() } -} diff --git a/vendor/golang.org/x/time/rate/rate.go b/vendor/golang.org/x/time/rate/rate.go index ec5f0cdd0c..794b2e32bf 100644 --- a/vendor/golang.org/x/time/rate/rate.go +++ b/vendor/golang.org/x/time/rate/rate.go @@ -85,7 +85,7 @@ func (lim *Limiter) Burst() int { // TokensAt returns the number of tokens available at time t. func (lim *Limiter) TokensAt(t time.Time) float64 { lim.mu.Lock() - _, tokens := lim.advance(t) // does not mutate lim + tokens := lim.advance(t) // does not mutate lim lim.mu.Unlock() return tokens } @@ -186,7 +186,7 @@ func (r *Reservation) CancelAt(t time.Time) { return } // advance time to now - t, tokens := r.lim.advance(t) + tokens := r.lim.advance(t) // calculate new number of tokens tokens += restoreTokens if burst := float64(r.lim.burst); tokens > burst { @@ -307,7 +307,7 @@ func (lim *Limiter) SetLimitAt(t time.Time, newLimit Limit) { lim.mu.Lock() defer lim.mu.Unlock() - t, tokens := lim.advance(t) + tokens := lim.advance(t) lim.last = t lim.tokens = tokens @@ -324,7 +324,7 @@ func (lim *Limiter) SetBurstAt(t time.Time, newBurst int) { lim.mu.Lock() defer lim.mu.Unlock() - t, tokens := lim.advance(t) + tokens := lim.advance(t) lim.last = t lim.tokens = tokens @@ -347,7 +347,7 @@ func (lim *Limiter) reserveN(t time.Time, n int, maxFutureReserve time.Duration) } } - t, tokens := lim.advance(t) + tokens := lim.advance(t) // Calculate the remaining number of tokens resulting from the request. tokens -= float64(n) @@ -380,10 +380,11 @@ func (lim *Limiter) reserveN(t time.Time, n int, maxFutureReserve time.Duration) return r } -// advance calculates and returns an updated state for lim resulting from the passage of time. +// advance calculates and returns an updated number of tokens for lim +// resulting from the passage of time. // lim is not changed. // advance requires that lim.mu is held. -func (lim *Limiter) advance(t time.Time) (newT time.Time, newTokens float64) { +func (lim *Limiter) advance(t time.Time) (newTokens float64) { last := lim.last if t.Before(last) { last = t @@ -396,7 +397,7 @@ func (lim *Limiter) advance(t time.Time) (newT time.Time, newTokens float64) { if burst := float64(lim.burst); tokens > burst { tokens = burst } - return t, tokens + return tokens } // durationFromTokens is a unit conversion function from the number of tokens to the duration diff --git a/vendor/google.golang.org/genproto/googleapis/api/annotations/annotations.pb.go b/vendor/google.golang.org/genproto/googleapis/api/annotations/annotations.pb.go index 8b462f3dfe..0b789e2c5e 100644 --- a/vendor/google.golang.org/genproto/googleapis/api/annotations/annotations.pb.go +++ b/vendor/google.golang.org/genproto/googleapis/api/annotations/annotations.pb.go @@ -1,4 +1,4 @@ -// Copyright 2024 Google LLC +// Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vendor/google.golang.org/genproto/googleapis/api/annotations/client.pb.go b/vendor/google.golang.org/genproto/googleapis/api/annotations/client.pb.go index db7806cb99..f840481726 100644 --- a/vendor/google.golang.org/genproto/googleapis/api/annotations/client.pb.go +++ b/vendor/google.golang.org/genproto/googleapis/api/annotations/client.pb.go @@ -1,4 +1,4 @@ -// Copyright 2024 Google LLC +// Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vendor/google.golang.org/genproto/googleapis/api/annotations/field_behavior.pb.go b/vendor/google.golang.org/genproto/googleapis/api/annotations/field_behavior.pb.go index 08505ba3fe..5d583b8660 100644 --- a/vendor/google.golang.org/genproto/googleapis/api/annotations/field_behavior.pb.go +++ b/vendor/google.golang.org/genproto/googleapis/api/annotations/field_behavior.pb.go @@ -1,4 +1,4 @@ -// Copyright 2024 Google LLC +// Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vendor/google.golang.org/genproto/googleapis/api/annotations/field_info.pb.go b/vendor/google.golang.org/genproto/googleapis/api/annotations/field_info.pb.go index a462e7d013..53e9dd1e99 100644 --- a/vendor/google.golang.org/genproto/googleapis/api/annotations/field_info.pb.go +++ b/vendor/google.golang.org/genproto/googleapis/api/annotations/field_info.pb.go @@ -1,4 +1,4 @@ -// Copyright 2024 Google LLC +// Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vendor/google.golang.org/genproto/googleapis/api/annotations/http.pb.go b/vendor/google.golang.org/genproto/googleapis/api/annotations/http.pb.go index c93b4f5248..d30fcee4ce 100644 --- a/vendor/google.golang.org/genproto/googleapis/api/annotations/http.pb.go +++ b/vendor/google.golang.org/genproto/googleapis/api/annotations/http.pb.go @@ -1,4 +1,4 @@ -// Copyright 2024 Google LLC +// Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vendor/google.golang.org/genproto/googleapis/api/annotations/resource.pb.go b/vendor/google.golang.org/genproto/googleapis/api/annotations/resource.pb.go index a1c543a948..175974a869 100644 --- a/vendor/google.golang.org/genproto/googleapis/api/annotations/resource.pb.go +++ b/vendor/google.golang.org/genproto/googleapis/api/annotations/resource.pb.go @@ -1,4 +1,4 @@ -// Copyright 2024 Google LLC +// Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vendor/google.golang.org/genproto/googleapis/api/annotations/routing.pb.go b/vendor/google.golang.org/genproto/googleapis/api/annotations/routing.pb.go index 2b54db3045..b8c4aa71f2 100644 --- a/vendor/google.golang.org/genproto/googleapis/api/annotations/routing.pb.go +++ b/vendor/google.golang.org/genproto/googleapis/api/annotations/routing.pb.go @@ -1,4 +1,4 @@ -// Copyright 2024 Google LLC +// Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/checked.pb.go b/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/checked.pb.go index 9f81dbcd86..af9c44d93e 100644 --- a/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/checked.pb.go +++ b/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/checked.pb.go @@ -1,4 +1,4 @@ -// Copyright 2024 Google LLC +// Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/eval.pb.go b/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/eval.pb.go index 0a2ffb5955..4b4f15477f 100644 --- a/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/eval.pb.go +++ b/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/eval.pb.go @@ -1,4 +1,4 @@ -// Copyright 2024 Google LLC +// Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/explain.pb.go b/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/explain.pb.go index 57aaa2c9f5..ef27e878b9 100644 --- a/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/explain.pb.go +++ b/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/explain.pb.go @@ -1,4 +1,4 @@ -// Copyright 2024 Google LLC +// Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/syntax.pb.go b/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/syntax.pb.go index c90c6015d2..7b973217ed 100644 --- a/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/syntax.pb.go +++ b/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/syntax.pb.go @@ -1,4 +1,4 @@ -// Copyright 2024 Google LLC +// Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/value.pb.go b/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/value.pb.go index 0a5ca6a1b9..4ba3c7b2a8 100644 --- a/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/value.pb.go +++ b/vendor/google.golang.org/genproto/googleapis/api/expr/v1alpha1/value.pb.go @@ -1,4 +1,4 @@ -// Copyright 2024 Google LLC +// Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vendor/google.golang.org/genproto/googleapis/api/launch_stage.pb.go b/vendor/google.golang.org/genproto/googleapis/api/launch_stage.pb.go index 498020e33c..a69c1d4734 100644 --- a/vendor/google.golang.org/genproto/googleapis/api/launch_stage.pb.go +++ b/vendor/google.golang.org/genproto/googleapis/api/launch_stage.pb.go @@ -1,4 +1,4 @@ -// Copyright 2024 Google LLC +// Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vendor/google.golang.org/genproto/googleapis/rpc/status/status.pb.go b/vendor/google.golang.org/genproto/googleapis/rpc/status/status.pb.go index 6ad1b1c1df..06a3f71063 100644 --- a/vendor/google.golang.org/genproto/googleapis/rpc/status/status.pb.go +++ b/vendor/google.golang.org/genproto/googleapis/rpc/status/status.pb.go @@ -1,4 +1,4 @@ -// Copyright 2024 Google LLC +// Copyright 2025 Google LLC // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. diff --git a/vendor/google.golang.org/grpc/balancer/balancer.go b/vendor/google.golang.org/grpc/balancer/balancer.go index 382ad69411..c9b343c715 100644 --- a/vendor/google.golang.org/grpc/balancer/balancer.go +++ b/vendor/google.golang.org/grpc/balancer/balancer.go @@ -129,6 +129,13 @@ type State struct { // brand new implementation of this interface. For the situations like // testing, the new implementation should embed this interface. This allows // gRPC to add new methods to this interface. +// +// NOTICE: This interface is intended to be implemented by gRPC, or intercepted +// by custom load balancing polices. Users should not need their own complete +// implementation of this interface -- they should always delegate to a +// ClientConn passed to Builder.Build() by embedding it in their +// implementations. An embedded ClientConn must never be nil, or runtime panics +// will occur. type ClientConn interface { // NewSubConn is called by balancer to create a new SubConn. // It doesn't block and wait for the connections to be established. @@ -167,6 +174,17 @@ type ClientConn interface { // // Deprecated: Use the Target field in the BuildOptions instead. Target() string + + // MetricsRecorder provides the metrics recorder that balancers can use to + // record metrics. Balancer implementations which do not register metrics on + // metrics registry and record on them can ignore this method. The returned + // MetricsRecorder is guaranteed to never be nil. + MetricsRecorder() estats.MetricsRecorder + + // EnforceClientConnEmbedding is included to force implementers to embed + // another implementation of this interface, allowing gRPC to add methods + // without breaking users. + internal.EnforceClientConnEmbedding } // BuildOptions contains additional information for Build. @@ -198,10 +216,6 @@ type BuildOptions struct { // same resolver.Target as passed to the resolver. See the documentation for // the resolver.Target type for details about what it contains. Target resolver.Target - // MetricsRecorder is the metrics recorder that balancers can use to record - // metrics. Balancer implementations which do not register metrics on - // metrics registry and record on them can ignore this field. - MetricsRecorder estats.MetricsRecorder } // Builder creates a balancer. diff --git a/vendor/google.golang.org/grpc/balancer/endpointsharding/endpointsharding.go b/vendor/google.golang.org/grpc/balancer/endpointsharding/endpointsharding.go new file mode 100644 index 0000000000..421c4fecc9 --- /dev/null +++ b/vendor/google.golang.org/grpc/balancer/endpointsharding/endpointsharding.go @@ -0,0 +1,358 @@ +/* + * + * Copyright 2024 gRPC authors. + * + * 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 endpointsharding implements a load balancing policy that manages +// homogeneous child policies each owning a single endpoint. +// +// # Experimental +// +// Notice: This package is EXPERIMENTAL and may be changed or removed in a +// later release. +package endpointsharding + +import ( + "errors" + rand "math/rand/v2" + "sync" + "sync/atomic" + + "google.golang.org/grpc/balancer" + "google.golang.org/grpc/balancer/base" + "google.golang.org/grpc/connectivity" + "google.golang.org/grpc/resolver" +) + +// ChildState is the balancer state of a child along with the endpoint which +// identifies the child balancer. +type ChildState struct { + Endpoint resolver.Endpoint + State balancer.State + + // Balancer exposes only the ExitIdler interface of the child LB policy. + // Other methods of the child policy are called only by endpointsharding. + Balancer balancer.ExitIdler +} + +// Options are the options to configure the behaviour of the +// endpointsharding balancer. +type Options struct { + // DisableAutoReconnect allows the balancer to keep child balancer in the + // IDLE state until they are explicitly triggered to exit using the + // ChildState obtained from the endpointsharding picker. When set to false, + // the endpointsharding balancer will automatically call ExitIdle on child + // connections that report IDLE. + DisableAutoReconnect bool +} + +// ChildBuilderFunc creates a new balancer with the ClientConn. It has the same +// type as the balancer.Builder.Build method. +type ChildBuilderFunc func(cc balancer.ClientConn, opts balancer.BuildOptions) balancer.Balancer + +// NewBalancer returns a load balancing policy that manages homogeneous child +// policies each owning a single endpoint. The endpointsharding balancer +// forwards the LoadBalancingConfig in ClientConn state updates to its children. +func NewBalancer(cc balancer.ClientConn, opts balancer.BuildOptions, childBuilder ChildBuilderFunc, esOpts Options) balancer.Balancer { + es := &endpointSharding{ + cc: cc, + bOpts: opts, + esOpts: esOpts, + childBuilder: childBuilder, + } + es.children.Store(resolver.NewEndpointMap()) + return es +} + +// endpointSharding is a balancer that wraps child balancers. It creates a child +// balancer with child config for every unique Endpoint received. It updates the +// child states on any update from parent or child. +type endpointSharding struct { + cc balancer.ClientConn + bOpts balancer.BuildOptions + esOpts Options + childBuilder ChildBuilderFunc + + // childMu synchronizes calls to any single child. It must be held for all + // calls into a child. To avoid deadlocks, do not acquire childMu while + // holding mu. + childMu sync.Mutex + children atomic.Pointer[resolver.EndpointMap] // endpoint -> *balancerWrapper + + // inhibitChildUpdates is set during UpdateClientConnState/ResolverError + // calls (calls to children will each produce an update, only want one + // update). + inhibitChildUpdates atomic.Bool + + // mu synchronizes access to the state stored in balancerWrappers in the + // children field. mu must not be held during calls into a child since + // synchronous calls back from the child may require taking mu, causing a + // deadlock. To avoid deadlocks, do not acquire childMu while holding mu. + mu sync.Mutex +} + +// UpdateClientConnState creates a child for new endpoints and deletes children +// for endpoints that are no longer present. It also updates all the children, +// and sends a single synchronous update of the childrens' aggregated state at +// the end of the UpdateClientConnState operation. If any endpoint has no +// addresses it will ignore that endpoint. Otherwise, returns first error found +// from a child, but fully processes the new update. +func (es *endpointSharding) UpdateClientConnState(state balancer.ClientConnState) error { + es.childMu.Lock() + defer es.childMu.Unlock() + + es.inhibitChildUpdates.Store(true) + defer func() { + es.inhibitChildUpdates.Store(false) + es.updateState() + }() + var ret error + + children := es.children.Load() + newChildren := resolver.NewEndpointMap() + + // Update/Create new children. + for _, endpoint := range state.ResolverState.Endpoints { + if _, ok := newChildren.Get(endpoint); ok { + // Endpoint child was already created, continue to avoid duplicate + // update. + continue + } + var childBalancer *balancerWrapper + if val, ok := children.Get(endpoint); ok { + childBalancer = val.(*balancerWrapper) + // Endpoint attributes may have changed, update the stored endpoint. + es.mu.Lock() + childBalancer.childState.Endpoint = endpoint + es.mu.Unlock() + } else { + childBalancer = &balancerWrapper{ + childState: ChildState{Endpoint: endpoint}, + ClientConn: es.cc, + es: es, + } + childBalancer.childState.Balancer = childBalancer + childBalancer.child = es.childBuilder(childBalancer, es.bOpts) + } + newChildren.Set(endpoint, childBalancer) + if err := childBalancer.updateClientConnStateLocked(balancer.ClientConnState{ + BalancerConfig: state.BalancerConfig, + ResolverState: resolver.State{ + Endpoints: []resolver.Endpoint{endpoint}, + Attributes: state.ResolverState.Attributes, + }, + }); err != nil && ret == nil { + // Return first error found, and always commit full processing of + // updating children. If desired to process more specific errors + // across all endpoints, caller should make these specific + // validations, this is a current limitation for simplicity sake. + ret = err + } + } + // Delete old children that are no longer present. + for _, e := range children.Keys() { + child, _ := children.Get(e) + if _, ok := newChildren.Get(e); !ok { + child.(*balancerWrapper).closeLocked() + } + } + es.children.Store(newChildren) + if newChildren.Len() == 0 { + return balancer.ErrBadResolverState + } + return ret +} + +// ResolverError forwards the resolver error to all of the endpointSharding's +// children and sends a single synchronous update of the childStates at the end +// of the ResolverError operation. +func (es *endpointSharding) ResolverError(err error) { + es.childMu.Lock() + defer es.childMu.Unlock() + es.inhibitChildUpdates.Store(true) + defer func() { + es.inhibitChildUpdates.Store(false) + es.updateState() + }() + children := es.children.Load() + for _, child := range children.Values() { + child.(*balancerWrapper).resolverErrorLocked(err) + } +} + +func (es *endpointSharding) UpdateSubConnState(balancer.SubConn, balancer.SubConnState) { + // UpdateSubConnState is deprecated. +} + +func (es *endpointSharding) Close() { + es.childMu.Lock() + defer es.childMu.Unlock() + children := es.children.Load() + for _, child := range children.Values() { + child.(*balancerWrapper).closeLocked() + } +} + +// updateState updates this component's state. It sends the aggregated state, +// and a picker with round robin behavior with all the child states present if +// needed. +func (es *endpointSharding) updateState() { + if es.inhibitChildUpdates.Load() { + return + } + var readyPickers, connectingPickers, idlePickers, transientFailurePickers []balancer.Picker + + es.mu.Lock() + defer es.mu.Unlock() + + children := es.children.Load() + childStates := make([]ChildState, 0, children.Len()) + + for _, child := range children.Values() { + bw := child.(*balancerWrapper) + childState := bw.childState + childStates = append(childStates, childState) + childPicker := childState.State.Picker + switch childState.State.ConnectivityState { + case connectivity.Ready: + readyPickers = append(readyPickers, childPicker) + case connectivity.Connecting: + connectingPickers = append(connectingPickers, childPicker) + case connectivity.Idle: + idlePickers = append(idlePickers, childPicker) + case connectivity.TransientFailure: + transientFailurePickers = append(transientFailurePickers, childPicker) + // connectivity.Shutdown shouldn't appear. + } + } + + // Construct the round robin picker based off the aggregated state. Whatever + // the aggregated state, use the pickers present that are currently in that + // state only. + var aggState connectivity.State + var pickers []balancer.Picker + if len(readyPickers) >= 1 { + aggState = connectivity.Ready + pickers = readyPickers + } else if len(connectingPickers) >= 1 { + aggState = connectivity.Connecting + pickers = connectingPickers + } else if len(idlePickers) >= 1 { + aggState = connectivity.Idle + pickers = idlePickers + } else if len(transientFailurePickers) >= 1 { + aggState = connectivity.TransientFailure + pickers = transientFailurePickers + } else { + aggState = connectivity.TransientFailure + pickers = []balancer.Picker{base.NewErrPicker(errors.New("no children to pick from"))} + } // No children (resolver error before valid update). + p := &pickerWithChildStates{ + pickers: pickers, + childStates: childStates, + next: uint32(rand.IntN(len(pickers))), + } + es.cc.UpdateState(balancer.State{ + ConnectivityState: aggState, + Picker: p, + }) +} + +// pickerWithChildStates delegates to the pickers it holds in a round robin +// fashion. It also contains the childStates of all the endpointSharding's +// children. +type pickerWithChildStates struct { + pickers []balancer.Picker + childStates []ChildState + next uint32 +} + +func (p *pickerWithChildStates) Pick(info balancer.PickInfo) (balancer.PickResult, error) { + nextIndex := atomic.AddUint32(&p.next, 1) + picker := p.pickers[nextIndex%uint32(len(p.pickers))] + return picker.Pick(info) +} + +// ChildStatesFromPicker returns the state of all the children managed by the +// endpoint sharding balancer that created this picker. +func ChildStatesFromPicker(picker balancer.Picker) []ChildState { + p, ok := picker.(*pickerWithChildStates) + if !ok { + return nil + } + return p.childStates +} + +// balancerWrapper is a wrapper of a balancer. It ID's a child balancer by +// endpoint, and persists recent child balancer state. +type balancerWrapper struct { + // The following fields are initialized at build time and read-only after + // that and therefore do not need to be guarded by a mutex. + + // child contains the wrapped balancer. Access its methods only through + // methods on balancerWrapper to ensure proper synchronization + child balancer.Balancer + balancer.ClientConn // embed to intercept UpdateState, doesn't deal with SubConns + + es *endpointSharding + + // Access to the following fields is guarded by es.mu. + + childState ChildState + isClosed bool +} + +func (bw *balancerWrapper) UpdateState(state balancer.State) { + bw.es.mu.Lock() + bw.childState.State = state + bw.es.mu.Unlock() + if state.ConnectivityState == connectivity.Idle && !bw.es.esOpts.DisableAutoReconnect { + bw.ExitIdle() + } + bw.es.updateState() +} + +// ExitIdle pings an IDLE child balancer to exit idle in a new goroutine to +// avoid deadlocks due to synchronous balancer state updates. +func (bw *balancerWrapper) ExitIdle() { + if ei, ok := bw.child.(balancer.ExitIdler); ok { + go func() { + bw.es.childMu.Lock() + if !bw.isClosed { + ei.ExitIdle() + } + bw.es.childMu.Unlock() + }() + } +} + +// updateClientConnStateLocked delivers the ClientConnState to the child +// balancer. Callers must hold the child mutex of the parent endpointsharding +// balancer. +func (bw *balancerWrapper) updateClientConnStateLocked(ccs balancer.ClientConnState) error { + return bw.child.UpdateClientConnState(ccs) +} + +// closeLocked closes the child balancer. Callers must hold the child mutext of +// the parent endpointsharding balancer. +func (bw *balancerWrapper) closeLocked() { + bw.child.Close() + bw.isClosed = true +} + +func (bw *balancerWrapper) resolverErrorLocked(err error) { + bw.child.ResolverError(err) +} diff --git a/vendor/google.golang.org/grpc/balancer/pickfirst/pickfirstleaf/pickfirstleaf.go b/vendor/google.golang.org/grpc/balancer/pickfirst/pickfirstleaf/pickfirstleaf.go index 76fa5fea95..113181e6b3 100644 --- a/vendor/google.golang.org/grpc/balancer/pickfirst/pickfirstleaf/pickfirstleaf.go +++ b/vendor/google.golang.org/grpc/balancer/pickfirst/pickfirstleaf/pickfirstleaf.go @@ -120,7 +120,7 @@ func (pickfirstBuilder) Build(cc balancer.ClientConn, bo balancer.BuildOptions) b := &pickfirstBalancer{ cc: cc, target: bo.Target.String(), - metricsRecorder: bo.MetricsRecorder, // ClientConn will always create a Metrics Recorder. + metricsRecorder: cc.MetricsRecorder(), subConns: resolver.NewAddressMap(), state: connectivity.Connecting, diff --git a/vendor/google.golang.org/grpc/balancer/roundrobin/roundrobin.go b/vendor/google.golang.org/grpc/balancer/roundrobin/roundrobin.go index 80a42d2251..35da5d1ec9 100644 --- a/vendor/google.golang.org/grpc/balancer/roundrobin/roundrobin.go +++ b/vendor/google.golang.org/grpc/balancer/roundrobin/roundrobin.go @@ -22,12 +22,13 @@ package roundrobin import ( - rand "math/rand/v2" - "sync/atomic" + "fmt" "google.golang.org/grpc/balancer" - "google.golang.org/grpc/balancer/base" + "google.golang.org/grpc/balancer/endpointsharding" + "google.golang.org/grpc/balancer/pickfirst/pickfirstleaf" "google.golang.org/grpc/grpclog" + internalgrpclog "google.golang.org/grpc/internal/grpclog" ) // Name is the name of round_robin balancer. @@ -35,47 +36,44 @@ const Name = "round_robin" var logger = grpclog.Component("roundrobin") -// newBuilder creates a new roundrobin balancer builder. -func newBuilder() balancer.Builder { - return base.NewBalancerBuilder(Name, &rrPickerBuilder{}, base.Config{HealthCheck: true}) -} - func init() { - balancer.Register(newBuilder()) + balancer.Register(builder{}) } -type rrPickerBuilder struct{} +type builder struct{} -func (*rrPickerBuilder) Build(info base.PickerBuildInfo) balancer.Picker { - logger.Infof("roundrobinPicker: Build called with info: %v", info) - if len(info.ReadySCs) == 0 { - return base.NewErrPicker(balancer.ErrNoSubConnAvailable) - } - scs := make([]balancer.SubConn, 0, len(info.ReadySCs)) - for sc := range info.ReadySCs { - scs = append(scs, sc) - } - return &rrPicker{ - subConns: scs, - // Start at a random index, as the same RR balancer rebuilds a new - // picker when SubConn states change, and we don't want to apply excess - // load to the first server in the list. - next: uint32(rand.IntN(len(scs))), +func (bb builder) Name() string { + return Name +} + +func (bb builder) Build(cc balancer.ClientConn, opts balancer.BuildOptions) balancer.Balancer { + childBuilder := balancer.Get(pickfirstleaf.Name).Build + bal := &rrBalancer{ + cc: cc, + Balancer: endpointsharding.NewBalancer(cc, opts, childBuilder, endpointsharding.Options{}), } + bal.logger = internalgrpclog.NewPrefixLogger(logger, fmt.Sprintf("[%p] ", bal)) + bal.logger.Infof("Created") + return bal } -type rrPicker struct { - // subConns is the snapshot of the roundrobin balancer when this picker was - // created. The slice is immutable. Each Get() will do a round robin - // selection from it and return the selected SubConn. - subConns []balancer.SubConn - next uint32 +type rrBalancer struct { + balancer.Balancer + cc balancer.ClientConn + logger *internalgrpclog.PrefixLogger } -func (p *rrPicker) Pick(balancer.PickInfo) (balancer.PickResult, error) { - subConnsLen := uint32(len(p.subConns)) - nextIndex := atomic.AddUint32(&p.next, 1) +func (b *rrBalancer) UpdateClientConnState(ccs balancer.ClientConnState) error { + return b.Balancer.UpdateClientConnState(balancer.ClientConnState{ + // Enable the health listener in pickfirst children for client side health + // checks and outlier detection, if configured. + ResolverState: pickfirstleaf.EnableHealthListener(ccs.ResolverState), + }) +} - sc := p.subConns[nextIndex%subConnsLen] - return balancer.PickResult{SubConn: sc}, nil +func (b *rrBalancer) ExitIdle() { + // Should always be ok, as child is endpoint sharding. + if ei, ok := b.Balancer.(balancer.ExitIdler); ok { + ei.ExitIdle() + } } diff --git a/vendor/google.golang.org/grpc/balancer/subconn.go b/vendor/google.golang.org/grpc/balancer/subconn.go index ea27c4fa76..9ee44d4af0 100644 --- a/vendor/google.golang.org/grpc/balancer/subconn.go +++ b/vendor/google.golang.org/grpc/balancer/subconn.go @@ -44,7 +44,7 @@ import ( // should only use a single address. // // NOTICE: This interface is intended to be implemented by gRPC, or intercepted -// by custom load balancing poilices. Users should not need their own complete +// by custom load balancing polices. Users should not need their own complete // implementation of this interface -- they should always delegate to a SubConn // returned by ClientConn.NewSubConn() by embedding it in their implementations. // An embedded SubConn must never be nil, or runtime panics will occur. diff --git a/vendor/google.golang.org/grpc/balancer_wrapper.go b/vendor/google.golang.org/grpc/balancer_wrapper.go index c2688376ae..948a21ef68 100644 --- a/vendor/google.golang.org/grpc/balancer_wrapper.go +++ b/vendor/google.golang.org/grpc/balancer_wrapper.go @@ -26,6 +26,7 @@ import ( "google.golang.org/grpc/balancer" "google.golang.org/grpc/codes" "google.golang.org/grpc/connectivity" + "google.golang.org/grpc/experimental/stats" "google.golang.org/grpc/internal" "google.golang.org/grpc/internal/balancer/gracefulswitch" "google.golang.org/grpc/internal/channelz" @@ -59,6 +60,7 @@ var ( // It uses the gracefulswitch.Balancer internally to ensure that balancer // switches happen in a graceful manner. type ccBalancerWrapper struct { + internal.EnforceClientConnEmbedding // The following fields are initialized when the wrapper is created and are // read-only afterwards, and therefore can be accessed without a mutex. cc *ClientConn @@ -92,7 +94,6 @@ func newCCBalancerWrapper(cc *ClientConn) *ccBalancerWrapper { CustomUserAgent: cc.dopts.copts.UserAgent, ChannelzParent: cc.channelz, Target: cc.parsedTarget, - MetricsRecorder: cc.metricsRecorderList, }, serializer: grpcsync.NewCallbackSerializer(ctx), serializerCancel: cancel, @@ -101,6 +102,10 @@ func newCCBalancerWrapper(cc *ClientConn) *ccBalancerWrapper { return ccb } +func (ccb *ccBalancerWrapper) MetricsRecorder() stats.MetricsRecorder { + return ccb.cc.metricsRecorderList +} + // updateClientConnState is invoked by grpc to push a ClientConnState update to // the underlying balancer. This is always executed from the serializer, so // it is safe to call into the balancer here. @@ -415,7 +420,7 @@ func (acbw *acBalancerWrapper) GetOrBuildProducer(pb balancer.ProducerBuilder) ( } acbw.producersMu.Unlock() } - return pData.producer, grpcsync.OnceFunc(unref) + return pData.producer, sync.OnceFunc(unref) } func (acbw *acBalancerWrapper) closeProducers() { diff --git a/vendor/google.golang.org/grpc/binarylog/grpc_binarylog_v1/binarylog.pb.go b/vendor/google.golang.org/grpc/binarylog/grpc_binarylog_v1/binarylog.pb.go index 21dd72969a..b2f8fc7f43 100644 --- a/vendor/google.golang.org/grpc/binarylog/grpc_binarylog_v1/binarylog.pb.go +++ b/vendor/google.golang.org/grpc/binarylog/grpc_binarylog_v1/binarylog.pb.go @@ -18,7 +18,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.35.2 +// protoc-gen-go v1.36.4 // protoc v5.27.1 // source: grpc/binlog/v1/binarylog.proto @@ -31,6 +31,7 @@ import ( timestamppb "google.golang.org/protobuf/types/known/timestamppb" reflect "reflect" sync "sync" + unsafe "unsafe" ) const ( @@ -233,10 +234,7 @@ func (Address_Type) EnumDescriptor() ([]byte, []int) { // Log entry we store in binary logs type GrpcLogEntry struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - + state protoimpl.MessageState `protogen:"open.v1"` // The timestamp of the binary log message Timestamp *timestamppb.Timestamp `protobuf:"bytes,1,opt,name=timestamp,proto3" json:"timestamp,omitempty"` // Uniquely identifies a call. The value must not be 0 in order to disambiguate @@ -255,7 +253,7 @@ type GrpcLogEntry struct { // The logger uses one of the following fields to record the payload, // according to the type of the log entry. // - // Types that are assignable to Payload: + // Types that are valid to be assigned to Payload: // // *GrpcLogEntry_ClientHeader // *GrpcLogEntry_ServerHeader @@ -269,7 +267,9 @@ type GrpcLogEntry struct { // EVENT_TYPE_SERVER_HEADER normally or EVENT_TYPE_SERVER_TRAILER in // the case of trailers-only. On server side, peer is always // logged on EVENT_TYPE_CLIENT_HEADER. - Peer *Address `protobuf:"bytes,11,opt,name=peer,proto3" json:"peer,omitempty"` + Peer *Address `protobuf:"bytes,11,opt,name=peer,proto3" json:"peer,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *GrpcLogEntry) Reset() { @@ -337,37 +337,45 @@ func (x *GrpcLogEntry) GetLogger() GrpcLogEntry_Logger { return GrpcLogEntry_LOGGER_UNKNOWN } -func (m *GrpcLogEntry) GetPayload() isGrpcLogEntry_Payload { - if m != nil { - return m.Payload +func (x *GrpcLogEntry) GetPayload() isGrpcLogEntry_Payload { + if x != nil { + return x.Payload } return nil } func (x *GrpcLogEntry) GetClientHeader() *ClientHeader { - if x, ok := x.GetPayload().(*GrpcLogEntry_ClientHeader); ok { - return x.ClientHeader + if x != nil { + if x, ok := x.Payload.(*GrpcLogEntry_ClientHeader); ok { + return x.ClientHeader + } } return nil } func (x *GrpcLogEntry) GetServerHeader() *ServerHeader { - if x, ok := x.GetPayload().(*GrpcLogEntry_ServerHeader); ok { - return x.ServerHeader + if x != nil { + if x, ok := x.Payload.(*GrpcLogEntry_ServerHeader); ok { + return x.ServerHeader + } } return nil } func (x *GrpcLogEntry) GetMessage() *Message { - if x, ok := x.GetPayload().(*GrpcLogEntry_Message); ok { - return x.Message + if x != nil { + if x, ok := x.Payload.(*GrpcLogEntry_Message); ok { + return x.Message + } } return nil } func (x *GrpcLogEntry) GetTrailer() *Trailer { - if x, ok := x.GetPayload().(*GrpcLogEntry_Trailer); ok { - return x.Trailer + if x != nil { + if x, ok := x.Payload.(*GrpcLogEntry_Trailer); ok { + return x.Trailer + } } return nil } @@ -416,10 +424,7 @@ func (*GrpcLogEntry_Message) isGrpcLogEntry_Payload() {} func (*GrpcLogEntry_Trailer) isGrpcLogEntry_Payload() {} type ClientHeader struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - + state protoimpl.MessageState `protogen:"open.v1"` // This contains only the metadata from the application. Metadata *Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` // The name of the RPC method, which looks something like: @@ -433,7 +438,9 @@ type ClientHeader struct { // or : . Authority string `protobuf:"bytes,3,opt,name=authority,proto3" json:"authority,omitempty"` // the RPC timeout - Timeout *durationpb.Duration `protobuf:"bytes,4,opt,name=timeout,proto3" json:"timeout,omitempty"` + Timeout *durationpb.Duration `protobuf:"bytes,4,opt,name=timeout,proto3" json:"timeout,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *ClientHeader) Reset() { @@ -495,12 +502,11 @@ func (x *ClientHeader) GetTimeout() *durationpb.Duration { } type ServerHeader struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - + state protoimpl.MessageState `protogen:"open.v1"` // This contains only the metadata from the application. - Metadata *Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` + Metadata *Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *ServerHeader) Reset() { @@ -541,10 +547,7 @@ func (x *ServerHeader) GetMetadata() *Metadata { } type Trailer struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - + state protoimpl.MessageState `protogen:"open.v1"` // This contains only the metadata from the application. Metadata *Metadata `protobuf:"bytes,1,opt,name=metadata,proto3" json:"metadata,omitempty"` // The gRPC status code. @@ -555,6 +558,8 @@ type Trailer struct { // The value of the 'grpc-status-details-bin' metadata key. If // present, this is always an encoded 'google.rpc.Status' message. StatusDetails []byte `protobuf:"bytes,4,opt,name=status_details,json=statusDetails,proto3" json:"status_details,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *Trailer) Reset() { @@ -617,15 +622,14 @@ func (x *Trailer) GetStatusDetails() []byte { // Message payload, used by CLIENT_MESSAGE and SERVER_MESSAGE type Message struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - + state protoimpl.MessageState `protogen:"open.v1"` // Length of the message. It may not be the same as the length of the // data field, as the logging payload can be truncated or omitted. Length uint32 `protobuf:"varint,1,opt,name=length,proto3" json:"length,omitempty"` // May be truncated or omitted. - Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` + Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *Message) Reset() { @@ -694,11 +698,10 @@ func (x *Message) GetData() []byte { // header is just a normal metadata key. // The pair will not count towards the size limit. type Metadata struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Entry []*MetadataEntry `protobuf:"bytes,1,rep,name=entry,proto3" json:"entry,omitempty"` unknownFields protoimpl.UnknownFields - - Entry []*MetadataEntry `protobuf:"bytes,1,rep,name=entry,proto3" json:"entry,omitempty"` + sizeCache protoimpl.SizeCache } func (x *Metadata) Reset() { @@ -740,12 +743,11 @@ func (x *Metadata) GetEntry() []*MetadataEntry { // A metadata key value pair type MetadataEntry struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache + state protoimpl.MessageState `protogen:"open.v1"` + Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` + Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` unknownFields protoimpl.UnknownFields - - Key string `protobuf:"bytes,1,opt,name=key,proto3" json:"key,omitempty"` - Value []byte `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` + sizeCache protoimpl.SizeCache } func (x *MetadataEntry) Reset() { @@ -794,14 +796,13 @@ func (x *MetadataEntry) GetValue() []byte { // Address information type Address struct { - state protoimpl.MessageState - sizeCache protoimpl.SizeCache - unknownFields protoimpl.UnknownFields - - Type Address_Type `protobuf:"varint,1,opt,name=type,proto3,enum=grpc.binarylog.v1.Address_Type" json:"type,omitempty"` - Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` + state protoimpl.MessageState `protogen:"open.v1"` + Type Address_Type `protobuf:"varint,1,opt,name=type,proto3,enum=grpc.binarylog.v1.Address_Type" json:"type,omitempty"` + Address string `protobuf:"bytes,2,opt,name=address,proto3" json:"address,omitempty"` // only for TYPE_IPV4 and TYPE_IPV6 - IpPort uint32 `protobuf:"varint,3,opt,name=ip_port,json=ipPort,proto3" json:"ip_port,omitempty"` + IpPort uint32 `protobuf:"varint,3,opt,name=ip_port,json=ipPort,proto3" json:"ip_port,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *Address) Reset() { @@ -857,7 +858,7 @@ func (x *Address) GetIpPort() uint32 { var File_grpc_binlog_v1_binarylog_proto protoreflect.FileDescriptor -var file_grpc_binlog_v1_binarylog_proto_rawDesc = []byte{ +var file_grpc_binlog_v1_binarylog_proto_rawDesc = string([]byte{ 0x0a, 0x1e, 0x67, 0x72, 0x70, 0x63, 0x2f, 0x62, 0x69, 0x6e, 0x6c, 0x6f, 0x67, 0x2f, 0x76, 0x31, 0x2f, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x6c, 0x6f, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x11, 0x67, 0x72, 0x70, 0x63, 0x2e, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x6c, 0x6f, 0x67, @@ -983,16 +984,16 @@ var file_grpc_binlog_v1_binarylog_proto_rawDesc = []byte{ 0x69, 0x6e, 0x61, 0x72, 0x79, 0x6c, 0x6f, 0x67, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x62, 0x69, 0x6e, 0x61, 0x72, 0x79, 0x6c, 0x6f, 0x67, 0x5f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, -} +}) var ( file_grpc_binlog_v1_binarylog_proto_rawDescOnce sync.Once - file_grpc_binlog_v1_binarylog_proto_rawDescData = file_grpc_binlog_v1_binarylog_proto_rawDesc + file_grpc_binlog_v1_binarylog_proto_rawDescData []byte ) func file_grpc_binlog_v1_binarylog_proto_rawDescGZIP() []byte { file_grpc_binlog_v1_binarylog_proto_rawDescOnce.Do(func() { - file_grpc_binlog_v1_binarylog_proto_rawDescData = protoimpl.X.CompressGZIP(file_grpc_binlog_v1_binarylog_proto_rawDescData) + file_grpc_binlog_v1_binarylog_proto_rawDescData = protoimpl.X.CompressGZIP(unsafe.Slice(unsafe.StringData(file_grpc_binlog_v1_binarylog_proto_rawDesc), len(file_grpc_binlog_v1_binarylog_proto_rawDesc))) }) return file_grpc_binlog_v1_binarylog_proto_rawDescData } @@ -1051,7 +1052,7 @@ func file_grpc_binlog_v1_binarylog_proto_init() { out := protoimpl.TypeBuilder{ File: protoimpl.DescBuilder{ GoPackagePath: reflect.TypeOf(x{}).PkgPath(), - RawDescriptor: file_grpc_binlog_v1_binarylog_proto_rawDesc, + RawDescriptor: unsafe.Slice(unsafe.StringData(file_grpc_binlog_v1_binarylog_proto_rawDesc), len(file_grpc_binlog_v1_binarylog_proto_rawDesc)), NumEnums: 3, NumMessages: 8, NumExtensions: 0, @@ -1063,7 +1064,6 @@ func file_grpc_binlog_v1_binarylog_proto_init() { MessageInfos: file_grpc_binlog_v1_binarylog_proto_msgTypes, }.Build() File_grpc_binlog_v1_binarylog_proto = out.File - file_grpc_binlog_v1_binarylog_proto_rawDesc = nil file_grpc_binlog_v1_binarylog_proto_goTypes = nil file_grpc_binlog_v1_binarylog_proto_depIdxs = nil } diff --git a/vendor/google.golang.org/grpc/clientconn.go b/vendor/google.golang.org/grpc/clientconn.go index 4f57b55434..a319ef9794 100644 --- a/vendor/google.golang.org/grpc/clientconn.go +++ b/vendor/google.golang.org/grpc/clientconn.go @@ -118,12 +118,26 @@ func (dcs *defaultConfigSelector) SelectConfig(rpcInfo iresolver.RPCInfo) (*ires // NewClient creates a new gRPC "channel" for the target URI provided. No I/O // is performed. Use of the ClientConn for RPCs will automatically cause it to -// connect. Connect may be used to manually create a connection, but for most -// users this is unnecessary. +// connect. The Connect method may be called to manually create a connection, +// but for most users this should be unnecessary. // // The target name syntax is defined in -// https://github.com/grpc/grpc/blob/master/doc/naming.md. e.g. to use dns -// resolver, a "dns:///" prefix should be applied to the target. +// https://github.com/grpc/grpc/blob/master/doc/naming.md. E.g. to use the dns +// name resolver, a "dns:///" prefix may be applied to the target. The default +// name resolver will be used if no scheme is detected, or if the parsed scheme +// is not a registered name resolver. The default resolver is "dns" but can be +// overridden using the resolver package's SetDefaultScheme. +// +// Examples: +// +// - "foo.googleapis.com:8080" +// - "dns:///foo.googleapis.com:8080" +// - "dns:///foo.googleapis.com" +// - "dns:///10.0.0.213:8080" +// - "dns:///%5B2001:db8:85a3:8d3:1319:8a2e:370:7348%5D:443" +// - "dns://8.8.8.8/foo.googleapis.com:8080" +// - "dns://8.8.8.8/foo.googleapis.com" +// - "zookeeper://zk.example.com:9900/example_service" // // The DialOptions returned by WithBlock, WithTimeout, // WithReturnConnectionError, and FailOnNonTempDialError are ignored by this @@ -181,7 +195,7 @@ func NewClient(target string, opts ...DialOption) (conn *ClientConn, err error) } cc.dopts.defaultServiceConfig, _ = scpr.Config.(*ServiceConfig) } - cc.mkp = cc.dopts.copts.KeepaliveParams + cc.keepaliveParams = cc.dopts.copts.KeepaliveParams if err = cc.initAuthority(); err != nil { return nil, err @@ -225,7 +239,12 @@ func Dial(target string, opts ...DialOption) (*ClientConn, error) { func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *ClientConn, err error) { // At the end of this method, we kick the channel out of idle, rather than // waiting for the first rpc. - opts = append([]DialOption{withDefaultScheme("passthrough")}, opts...) + // + // WithLocalDNSResolution dial option in `grpc.Dial` ensures that it + // preserves behavior: when default scheme passthrough is used, skip + // hostname resolution, when "dns" is used for resolution, perform + // resolution on the client. + opts = append([]DialOption{withDefaultScheme("passthrough"), WithLocalDNSResolution()}, opts...) cc, err := NewClient(target, opts...) if err != nil { return nil, err @@ -618,7 +637,7 @@ type ClientConn struct { balancerWrapper *ccBalancerWrapper // Always recreated whenever entering idle to simplify Close. sc *ServiceConfig // Latest service config received from the resolver. conns map[*addrConn]struct{} // Set to nil on close. - mkp keepalive.ClientParameters // May be updated upon receipt of a GoAway. + keepaliveParams keepalive.ClientParameters // May be updated upon receipt of a GoAway. // firstResolveEvent is used to track whether the name resolver sent us at // least one update. RPCs block on this event. May be accessed without mu // if we know we cannot be asked to enter idle mode while accessing it (e.g. @@ -867,7 +886,13 @@ func (cc *ClientConn) Target() string { return cc.target } -// CanonicalTarget returns the canonical target string of the ClientConn. +// CanonicalTarget returns the canonical target string used when creating cc. +// +// This always has the form "://[authority]/". For example: +// +// - "dns:///example.com:42" +// - "dns://8.8.8.8/example.com:42" +// - "unix:///path/to/socket" func (cc *ClientConn) CanonicalTarget() string { return cc.parsedTarget.String() } @@ -1210,8 +1235,8 @@ func (ac *addrConn) adjustParams(r transport.GoAwayReason) { case transport.GoAwayTooManyPings: v := 2 * ac.dopts.copts.KeepaliveParams.Time ac.cc.mu.Lock() - if v > ac.cc.mkp.Time { - ac.cc.mkp.Time = v + if v > ac.cc.keepaliveParams.Time { + ac.cc.keepaliveParams.Time = v } ac.cc.mu.Unlock() } @@ -1307,7 +1332,7 @@ func (ac *addrConn) tryAllAddrs(ctx context.Context, addrs []resolver.Address, c ac.mu.Lock() ac.cc.mu.RLock() - ac.dopts.copts.KeepaliveParams = ac.cc.mkp + ac.dopts.copts.KeepaliveParams = ac.cc.keepaliveParams ac.cc.mu.RUnlock() copts := ac.dopts.copts diff --git a/vendor/google.golang.org/grpc/dialoptions.go b/vendor/google.golang.org/grpc/dialoptions.go index f3a045296a..405a2ffeb3 100644 --- a/vendor/google.golang.org/grpc/dialoptions.go +++ b/vendor/google.golang.org/grpc/dialoptions.go @@ -73,7 +73,7 @@ type dialOptions struct { chainUnaryInts []UnaryClientInterceptor chainStreamInts []StreamClientInterceptor - cp Compressor + compressorV0 Compressor dc Decompressor bs internalbackoff.Strategy block bool @@ -94,6 +94,8 @@ type dialOptions struct { idleTimeout time.Duration defaultScheme string maxCallAttempts int + enableLocalDNSResolution bool // Specifies if target hostnames should be resolved when proxying is enabled. + useProxy bool // Specifies if a server should be connected via proxy. } // DialOption configures how we set up the connection. @@ -256,7 +258,7 @@ func WithCodec(c Codec) DialOption { // Deprecated: use UseCompressor instead. Will be supported throughout 1.x. func WithCompressor(cp Compressor) DialOption { return newFuncDialOption(func(o *dialOptions) { - o.cp = cp + o.compressorV0 = cp }) } @@ -377,7 +379,22 @@ func WithInsecure() DialOption { // later release. func WithNoProxy() DialOption { return newFuncDialOption(func(o *dialOptions) { - o.copts.UseProxy = false + o.useProxy = false + }) +} + +// WithLocalDNSResolution forces local DNS name resolution even when a proxy is +// specified in the environment. By default, the server name is provided +// directly to the proxy as part of the CONNECT handshake. This is ignored if +// WithNoProxy is used. +// +// # Experimental +// +// Notice: This API is EXPERIMENTAL and may be changed or removed in a +// later release. +func WithLocalDNSResolution() DialOption { + return newFuncDialOption(func(o *dialOptions) { + o.enableLocalDNSResolution = true }) } @@ -667,14 +684,15 @@ func defaultDialOptions() dialOptions { copts: transport.ConnectOptions{ ReadBufferSize: defaultReadBufSize, WriteBufferSize: defaultWriteBufSize, - UseProxy: true, UserAgent: grpcUA, BufferPool: mem.DefaultBufferPool(), }, - bs: internalbackoff.DefaultExponential, - idleTimeout: 30 * time.Minute, - defaultScheme: "dns", - maxCallAttempts: defaultMaxCallAttempts, + bs: internalbackoff.DefaultExponential, + idleTimeout: 30 * time.Minute, + defaultScheme: "dns", + maxCallAttempts: defaultMaxCallAttempts, + useProxy: true, + enableLocalDNSResolution: false, } } diff --git a/vendor/google.golang.org/grpc/internal/balancer/gracefulswitch/gracefulswitch.go b/vendor/google.golang.org/grpc/internal/balancer/gracefulswitch/gracefulswitch.go index 73bb4c4ee9..fbc1ca356a 100644 --- a/vendor/google.golang.org/grpc/internal/balancer/gracefulswitch/gracefulswitch.go +++ b/vendor/google.golang.org/grpc/internal/balancer/gracefulswitch/gracefulswitch.go @@ -109,8 +109,9 @@ func (gsb *Balancer) switchTo(builder balancer.Builder) (*balancerWrapper, error return nil, errBalancerClosed } bw := &balancerWrapper{ - builder: builder, - gsb: gsb, + ClientConn: gsb.cc, + builder: builder, + gsb: gsb, lastState: balancer.State{ ConnectivityState: connectivity.Connecting, Picker: base.NewErrPicker(balancer.ErrNoSubConnAvailable), @@ -293,6 +294,7 @@ func (gsb *Balancer) Close() { // State updates from the wrapped balancer can result in invocation of the // graceful switch logic. type balancerWrapper struct { + balancer.ClientConn balancer.Balancer gsb *Balancer builder balancer.Builder @@ -413,7 +415,3 @@ func (bw *balancerWrapper) UpdateAddresses(sc balancer.SubConn, addrs []resolver bw.gsb.mu.Unlock() bw.gsb.cc.UpdateAddresses(sc, addrs) } - -func (bw *balancerWrapper) Target() string { - return bw.gsb.cc.Target() -} diff --git a/vendor/google.golang.org/grpc/internal/envconfig/xds.go b/vendor/google.golang.org/grpc/internal/envconfig/xds.go index 9afeb444d4..2eb97f832b 100644 --- a/vendor/google.golang.org/grpc/internal/envconfig/xds.go +++ b/vendor/google.golang.org/grpc/internal/envconfig/xds.go @@ -56,7 +56,11 @@ var ( // XDSDualstackEndpointsEnabled is true if gRPC should read the // "additional addresses" in the xDS endpoint resource. - // TODO: https://github.com/grpc/grpc-go/issues/7866 - Control this using - // an env variable when all LB policies handle endpoints. - XDSDualstackEndpointsEnabled = false + XDSDualstackEndpointsEnabled = boolFromEnv("GRPC_EXPERIMENTAL_XDS_DUALSTACK_ENDPOINTS", true) + + // XDSSystemRootCertsEnabled is true when xDS enabled gRPC clients can use + // the system's default root certificates for TLS certificate validation. + // For more details, see: + // https://github.com/grpc/proposal/blob/master/A82-xds-system-root-certs.md. + XDSSystemRootCertsEnabled = boolFromEnv("GRPC_EXPERIMENTAL_XDS_SYSTEM_ROOT_CERTS", false) ) diff --git a/vendor/google.golang.org/grpc/internal/grpcsync/oncefunc.go b/vendor/google.golang.org/grpc/internal/grpcsync/oncefunc.go deleted file mode 100644 index 6635f7bca9..0000000000 --- a/vendor/google.golang.org/grpc/internal/grpcsync/oncefunc.go +++ /dev/null @@ -1,32 +0,0 @@ -/* - * - * Copyright 2022 gRPC authors. - * - * 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 grpcsync - -import ( - "sync" -) - -// OnceFunc returns a function wrapping f which ensures f is only executed -// once even if the returned function is executed multiple times. -func OnceFunc(f func()) func() { - var once sync.Once - return func() { - once.Do(f) - } -} diff --git a/vendor/google.golang.org/grpc/internal/internal.go b/vendor/google.golang.org/grpc/internal/internal.go index c17b98194b..13e1f386b1 100644 --- a/vendor/google.golang.org/grpc/internal/internal.go +++ b/vendor/google.golang.org/grpc/internal/internal.go @@ -64,6 +64,9 @@ var ( // gRPC server. An xDS-enabled server needs to know what type of credentials // is configured on the underlying gRPC server. This is set by server.go. GetServerCredentials any // func (*grpc.Server) credentials.TransportCredentials + // MetricsRecorderForServer returns the MetricsRecorderList derived from a + // server's stats handlers. + MetricsRecorderForServer any // func (*grpc.Server) estats.MetricsRecorder // CanonicalString returns the canonical string of the code defined here: // https://github.com/grpc/grpc/blob/master/doc/statuscodes.md. // @@ -151,6 +154,20 @@ var ( // other features, including the CSDS service. NewXDSResolverWithConfigForTesting any // func([]byte) (resolver.Builder, error) + // NewXDSResolverWithPoolForTesting creates a new xDS resolver builder + // using the provided xDS pool instead of creating a new one using the + // bootstrap configuration specified by the supported environment variables. + // The resolver.Builder is meant to be used in conjunction with the + // grpc.WithResolvers DialOption. The resolver.Builder does not take + // ownership of the provided xDS client and it is the responsibility of the + // caller to close the client when no longer required. + // + // Testing Only + // + // This function should ONLY be used for testing and may not work with some + // other features, including the CSDS service. + NewXDSResolverWithPoolForTesting any // func(*xdsclient.Pool) (resolver.Builder, error) + // NewXDSResolverWithClientForTesting creates a new xDS resolver builder // using the provided xDS client instead of creating a new one using the // bootstrap configuration specified by the supported environment variables. @@ -277,3 +294,9 @@ const RLSLoadBalancingPolicyName = "rls_experimental" type EnforceSubConnEmbedding interface { enforceSubConnEmbedding() } + +// EnforceClientConnEmbedding is used to enforce proper ClientConn implementation +// embedding. +type EnforceClientConnEmbedding interface { + enforceClientConnEmbedding() +} diff --git a/vendor/google.golang.org/grpc/internal/proxyattributes/proxyattributes.go b/vendor/google.golang.org/grpc/internal/proxyattributes/proxyattributes.go new file mode 100644 index 0000000000..1f61f1a49d --- /dev/null +++ b/vendor/google.golang.org/grpc/internal/proxyattributes/proxyattributes.go @@ -0,0 +1,54 @@ +/* + * + * Copyright 2024 gRPC authors. + * + * 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 proxyattributes contains functions for getting and setting proxy +// attributes like the CONNECT address and user info. +package proxyattributes + +import ( + "net/url" + + "google.golang.org/grpc/resolver" +) + +type keyType string + +const proxyOptionsKey = keyType("grpc.resolver.delegatingresolver.proxyOptions") + +// Options holds the proxy connection details needed during the CONNECT +// handshake. +type Options struct { + User *url.Userinfo + ConnectAddr string +} + +// Set returns a copy of addr with opts set in its attributes. +func Set(addr resolver.Address, opts Options) resolver.Address { + addr.Attributes = addr.Attributes.WithValue(proxyOptionsKey, opts) + return addr +} + +// Get returns the Options for the proxy [resolver.Address] and a boolean +// value representing if the attribute is present or not. The returned data +// should not be mutated. +func Get(addr resolver.Address) (Options, bool) { + if a := addr.Attributes.Value(proxyOptionsKey); a != nil { + return a.(Options), true + } + return Options{}, false +} diff --git a/vendor/google.golang.org/grpc/internal/resolver/delegatingresolver/delegatingresolver.go b/vendor/google.golang.org/grpc/internal/resolver/delegatingresolver/delegatingresolver.go new file mode 100644 index 0000000000..a6c6470133 --- /dev/null +++ b/vendor/google.golang.org/grpc/internal/resolver/delegatingresolver/delegatingresolver.go @@ -0,0 +1,329 @@ +/* + * + * Copyright 2024 gRPC authors. + * + * 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 delegatingresolver implements a resolver capable of resolving both +// target URIs and proxy addresses. +package delegatingresolver + +import ( + "fmt" + "net/http" + "net/url" + "sync" + + "google.golang.org/grpc/grpclog" + "google.golang.org/grpc/internal/proxyattributes" + "google.golang.org/grpc/resolver" + "google.golang.org/grpc/serviceconfig" +) + +var ( + logger = grpclog.Component("delegating-resolver") + // HTTPSProxyFromEnvironment will be overwritten in the tests + HTTPSProxyFromEnvironment = http.ProxyFromEnvironment +) + +// delegatingResolver manages both target URI and proxy address resolution by +// delegating these tasks to separate child resolvers. Essentially, it acts as +// a intermediary between the gRPC ClientConn and the child resolvers. +// +// It implements the [resolver.Resolver] interface. +type delegatingResolver struct { + target resolver.Target // parsed target URI to be resolved + cc resolver.ClientConn // gRPC ClientConn + targetResolver resolver.Resolver // resolver for the target URI, based on its scheme + proxyResolver resolver.Resolver // resolver for the proxy URI; nil if no proxy is configured + proxyURL *url.URL // proxy URL, derived from proxy environment and target + + mu sync.Mutex // protects all the fields below + targetResolverState *resolver.State // state of the target resolver + proxyAddrs []resolver.Address // resolved proxy addresses; empty if no proxy is configured +} + +// nopResolver is a resolver that does nothing. +type nopResolver struct{} + +func (nopResolver) ResolveNow(resolver.ResolveNowOptions) {} + +func (nopResolver) Close() {} + +// proxyURLForTarget determines the proxy URL for the given address based on +// the environment. It can return the following: +// - nil URL, nil error: No proxy is configured or the address is excluded +// using the `NO_PROXY` environment variable or if req.URL.Host is +// "localhost" (with or without // a port number) +// - nil URL, non-nil error: An error occurred while retrieving the proxy URL. +// - non-nil URL, nil error: A proxy is configured, and the proxy URL was +// retrieved successfully without any errors. +func proxyURLForTarget(address string) (*url.URL, error) { + req := &http.Request{URL: &url.URL{ + Scheme: "https", + Host: address, + }} + return HTTPSProxyFromEnvironment(req) +} + +// New creates a new delegating resolver that can create up to two child +// resolvers: +// - one to resolve the proxy address specified using the supported +// environment variables. This uses the registered resolver for the "dns" +// scheme. +// - one to resolve the target URI using the resolver specified by the scheme +// in the target URI or specified by the user using the WithResolvers dial +// option. As a special case, if the target URI's scheme is "dns" and a +// proxy is specified using the supported environment variables, the target +// URI's path portion is used as the resolved address unless target +// resolution is enabled using the dial option. +func New(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOptions, targetResolverBuilder resolver.Builder, targetResolutionEnabled bool) (resolver.Resolver, error) { + r := &delegatingResolver{ + target: target, + cc: cc, + } + + var err error + r.proxyURL, err = proxyURLForTarget(target.Endpoint()) + if err != nil { + return nil, fmt.Errorf("delegating_resolver: failed to determine proxy URL for target %s: %v", target, err) + } + + // proxy is not configured or proxy address excluded using `NO_PROXY` env + // var, so only target resolver is used. + if r.proxyURL == nil { + return targetResolverBuilder.Build(target, cc, opts) + } + + if logger.V(2) { + logger.Infof("Proxy URL detected : %s", r.proxyURL) + } + + // When the scheme is 'dns' and target resolution on client is not enabled, + // resolution should be handled by the proxy, not the client. Therefore, we + // bypass the target resolver and store the unresolved target address. + if target.URL.Scheme == "dns" && !targetResolutionEnabled { + state := resolver.State{ + Addresses: []resolver.Address{{Addr: target.Endpoint()}}, + Endpoints: []resolver.Endpoint{{Addresses: []resolver.Address{{Addr: target.Endpoint()}}}}, + } + r.targetResolverState = &state + } else { + wcc := &wrappingClientConn{ + stateListener: r.updateTargetResolverState, + parent: r, + } + if r.targetResolver, err = targetResolverBuilder.Build(target, wcc, opts); err != nil { + return nil, fmt.Errorf("delegating_resolver: unable to build the resolver for target %s: %v", target, err) + } + } + + if r.proxyResolver, err = r.proxyURIResolver(opts); err != nil { + return nil, fmt.Errorf("delegating_resolver: failed to build resolver for proxy URL %q: %v", r.proxyURL, err) + } + + if r.targetResolver == nil { + r.targetResolver = nopResolver{} + } + if r.proxyResolver == nil { + r.proxyResolver = nopResolver{} + } + return r, nil +} + +// proxyURIResolver creates a resolver for resolving proxy URIs using the +// "dns" scheme. It adjusts the proxyURL to conform to the "dns:///" format and +// builds a resolver with a wrappingClientConn to capture resolved addresses. +func (r *delegatingResolver) proxyURIResolver(opts resolver.BuildOptions) (resolver.Resolver, error) { + proxyBuilder := resolver.Get("dns") + if proxyBuilder == nil { + panic("delegating_resolver: resolver for proxy not found for scheme dns") + } + url := *r.proxyURL + url.Scheme = "dns" + url.Path = "/" + r.proxyURL.Host + url.Host = "" // Clear the Host field to conform to the "dns:///" format + + proxyTarget := resolver.Target{URL: url} + wcc := &wrappingClientConn{ + stateListener: r.updateProxyResolverState, + parent: r, + } + return proxyBuilder.Build(proxyTarget, wcc, opts) +} + +func (r *delegatingResolver) ResolveNow(o resolver.ResolveNowOptions) { + r.targetResolver.ResolveNow(o) + r.proxyResolver.ResolveNow(o) +} + +func (r *delegatingResolver) Close() { + r.targetResolver.Close() + r.targetResolver = nil + + r.proxyResolver.Close() + r.proxyResolver = nil +} + +// updateClientConnStateLocked creates a list of combined addresses by +// pairing each proxy address with every target address. For each pair, it +// generates a new [resolver.Address] using the proxy address, and adding the +// target address as the attribute along with user info. It returns nil if +// either resolver has not sent update even once and returns the error from +// ClientConn update once both resolvers have sent update atleast once. +func (r *delegatingResolver) updateClientConnStateLocked() error { + if r.targetResolverState == nil || r.proxyAddrs == nil { + return nil + } + + curState := *r.targetResolverState + // If multiple resolved proxy addresses are present, we send only the + // unresolved proxy host and let net.Dial handle the proxy host name + // resolution when creating the transport. Sending all resolved addresses + // would increase the number of addresses passed to the ClientConn and + // subsequently to load balancing (LB) policies like Round Robin, leading + // to additional TCP connections. However, if there's only one resolved + // proxy address, we send it directly, as it doesn't affect the address + // count returned by the target resolver and the address count sent to the + // ClientConn. + var proxyAddr resolver.Address + if len(r.proxyAddrs) == 1 { + proxyAddr = r.proxyAddrs[0] + } else { + proxyAddr = resolver.Address{Addr: r.proxyURL.Host} + } + var addresses []resolver.Address + for _, targetAddr := range (*r.targetResolverState).Addresses { + addresses = append(addresses, proxyattributes.Set(proxyAddr, proxyattributes.Options{ + User: r.proxyURL.User, + ConnectAddr: targetAddr.Addr, + })) + } + + // Create a list of combined endpoints by pairing all proxy endpoints + // with every target endpoint. Each time, it constructs a new + // [resolver.Endpoint] using the all addresses from all the proxy endpoint + // and the target addresses from one endpoint. The target address and user + // information from the proxy URL are added as attributes to the proxy + // address.The resulting list of addresses is then grouped into endpoints, + // covering all combinations of proxy and target endpoints. + var endpoints []resolver.Endpoint + for _, endpt := range (*r.targetResolverState).Endpoints { + var addrs []resolver.Address + for _, proxyAddr := range r.proxyAddrs { + for _, targetAddr := range endpt.Addresses { + addrs = append(addrs, proxyattributes.Set(proxyAddr, proxyattributes.Options{ + User: r.proxyURL.User, + ConnectAddr: targetAddr.Addr, + })) + } + } + endpoints = append(endpoints, resolver.Endpoint{Addresses: addrs}) + } + // Use the targetResolverState for its service config and attributes + // contents. The state update is only sent after both the target and proxy + // resolvers have sent their updates, and curState has been updated with + // the combined addresses. + curState.Addresses = addresses + curState.Endpoints = endpoints + return r.cc.UpdateState(curState) +} + +// updateProxyResolverState updates the proxy resolver state by storing proxy +// addresses and endpoints, marking the resolver as ready, and triggering a +// state update if both proxy and target resolvers are ready. If the ClientConn +// returns a non-nil error, it calls `ResolveNow()` on the target resolver. It +// is a StateListener function of wrappingClientConn passed to the proxy resolver. +func (r *delegatingResolver) updateProxyResolverState(state resolver.State) error { + r.mu.Lock() + defer r.mu.Unlock() + if logger.V(2) { + logger.Infof("Addresses received from proxy resolver: %s", state.Addresses) + } + if len(state.Endpoints) > 0 { + // We expect exactly one address per endpoint because the proxy + // resolver uses "dns" resolution. + r.proxyAddrs = make([]resolver.Address, 0, len(state.Endpoints)) + for _, endpoint := range state.Endpoints { + r.proxyAddrs = append(r.proxyAddrs, endpoint.Addresses...) + } + } else if state.Addresses != nil { + r.proxyAddrs = state.Addresses + } else { + r.proxyAddrs = []resolver.Address{} // ensure proxyAddrs is non-nil to indicate an update has been received + } + err := r.updateClientConnStateLocked() + // Another possible approach was to block until updates are received from + // both resolvers. But this is not used because calling `New()` triggers + // `Build()` for the first resolver, which calls `UpdateState()`. And the + // second resolver hasn't sent an update yet, so it would cause `New()` to + // block indefinitely. + if err != nil { + r.targetResolver.ResolveNow(resolver.ResolveNowOptions{}) + } + return err +} + +// updateTargetResolverState updates the target resolver state by storing target +// addresses, endpoints, and service config, marking the resolver as ready, and +// triggering a state update if both resolvers are ready. If the ClientConn +// returns a non-nil error, it calls `ResolveNow()` on the proxy resolver. It +// is a StateListener function of wrappingClientConn passed to the target resolver. +func (r *delegatingResolver) updateTargetResolverState(state resolver.State) error { + r.mu.Lock() + defer r.mu.Unlock() + + if logger.V(2) { + logger.Infof("Addresses received from target resolver: %v", state.Addresses) + } + r.targetResolverState = &state + err := r.updateClientConnStateLocked() + if err != nil { + r.proxyResolver.ResolveNow(resolver.ResolveNowOptions{}) + } + return nil +} + +// wrappingClientConn serves as an intermediary between the parent ClientConn +// and the child resolvers created here. It implements the resolver.ClientConn +// interface and is passed in that capacity to the child resolvers. +type wrappingClientConn struct { + // Callback to deliver resolver state updates + stateListener func(state resolver.State) error + parent *delegatingResolver +} + +// UpdateState receives resolver state updates and forwards them to the +// appropriate listener function (either for the proxy or target resolver). +func (wcc *wrappingClientConn) UpdateState(state resolver.State) error { + return wcc.stateListener(state) +} + +// ReportError intercepts errors from the child resolvers and passes them to ClientConn. +func (wcc *wrappingClientConn) ReportError(err error) { + wcc.parent.cc.ReportError(err) +} + +// NewAddress intercepts the new resolved address from the child resolvers and +// passes them to ClientConn. +func (wcc *wrappingClientConn) NewAddress(addrs []resolver.Address) { + wcc.UpdateState(resolver.State{Addresses: addrs}) +} + +// ParseServiceConfig parses the provided service config and returns an +// object that provides the parsed config. +func (wcc *wrappingClientConn) ParseServiceConfig(serviceConfigJSON string) *serviceconfig.ParseResult { + return wcc.parent.cc.ParseServiceConfig(serviceConfigJSON) +} diff --git a/vendor/google.golang.org/grpc/internal/transport/http2_client.go b/vendor/google.golang.org/grpc/internal/transport/http2_client.go index f323ab7f45..513dbb93d5 100644 --- a/vendor/google.golang.org/grpc/internal/transport/http2_client.go +++ b/vendor/google.golang.org/grpc/internal/transport/http2_client.go @@ -43,6 +43,7 @@ import ( "google.golang.org/grpc/internal/grpcsync" "google.golang.org/grpc/internal/grpcutil" imetadata "google.golang.org/grpc/internal/metadata" + "google.golang.org/grpc/internal/proxyattributes" istatus "google.golang.org/grpc/internal/status" isyscall "google.golang.org/grpc/internal/syscall" "google.golang.org/grpc/internal/transport/networktype" @@ -153,7 +154,7 @@ type http2Client struct { logger *grpclog.PrefixLogger } -func dial(ctx context.Context, fn func(context.Context, string) (net.Conn, error), addr resolver.Address, useProxy bool, grpcUA string) (net.Conn, error) { +func dial(ctx context.Context, fn func(context.Context, string) (net.Conn, error), addr resolver.Address, grpcUA string) (net.Conn, error) { address := addr.Addr networkType, ok := networktype.Get(addr) if fn != nil { @@ -177,8 +178,8 @@ func dial(ctx context.Context, fn func(context.Context, string) (net.Conn, error if !ok { networkType, address = parseDialTarget(address) } - if networkType == "tcp" && useProxy { - return proxyDial(ctx, address, grpcUA) + if opts, present := proxyattributes.Get(addr); present { + return proxyDial(ctx, addr, grpcUA, opts) } return internal.NetDialerWithTCPKeepalive().DialContext(ctx, networkType, address) } @@ -217,7 +218,7 @@ func NewHTTP2Client(connectCtx, ctx context.Context, addr resolver.Address, opts // address specific arbitrary data to reach custom dialers and credential handshakers. connectCtx = icredentials.NewClientHandshakeInfoContext(connectCtx, credentials.ClientHandshakeInfo{Attributes: addr.Attributes}) - conn, err := dial(connectCtx, opts.Dialer, addr, opts.UseProxy, opts.UserAgent) + conn, err := dial(connectCtx, opts.Dialer, addr, opts.UserAgent) if err != nil { if opts.FailOnNonTempDialError { return nil, connectionErrorf(isTemporary(err), err, "transport: error while dialing: %v", err) diff --git a/vendor/google.golang.org/grpc/internal/transport/proxy.go b/vendor/google.golang.org/grpc/internal/transport/proxy.go index 54b2244365..d773845955 100644 --- a/vendor/google.golang.org/grpc/internal/transport/proxy.go +++ b/vendor/google.golang.org/grpc/internal/transport/proxy.go @@ -30,34 +30,16 @@ import ( "net/url" "google.golang.org/grpc/internal" + "google.golang.org/grpc/internal/proxyattributes" + "google.golang.org/grpc/resolver" ) const proxyAuthHeaderKey = "Proxy-Authorization" -var ( - // The following variable will be overwritten in the tests. - httpProxyFromEnvironment = http.ProxyFromEnvironment -) - -func mapAddress(address string) (*url.URL, error) { - req := &http.Request{ - URL: &url.URL{ - Scheme: "https", - Host: address, - }, - } - url, err := httpProxyFromEnvironment(req) - if err != nil { - return nil, err - } - return url, nil -} - // To read a response from a net.Conn, http.ReadResponse() takes a bufio.Reader. -// It's possible that this reader reads more than what's need for the response and stores -// those bytes in the buffer. -// bufConn wraps the original net.Conn and the bufio.Reader to make sure we don't lose the -// bytes in the buffer. +// It's possible that this reader reads more than what's need for the response +// and stores those bytes in the buffer. bufConn wraps the original net.Conn +// and the bufio.Reader to make sure we don't lose the bytes in the buffer. type bufConn struct { net.Conn r io.Reader @@ -72,7 +54,7 @@ func basicAuth(username, password string) string { return base64.StdEncoding.EncodeToString([]byte(auth)) } -func doHTTPConnectHandshake(ctx context.Context, conn net.Conn, backendAddr string, proxyURL *url.URL, grpcUA string) (_ net.Conn, err error) { +func doHTTPConnectHandshake(ctx context.Context, conn net.Conn, grpcUA string, opts proxyattributes.Options) (_ net.Conn, err error) { defer func() { if err != nil { conn.Close() @@ -81,15 +63,14 @@ func doHTTPConnectHandshake(ctx context.Context, conn net.Conn, backendAddr stri req := &http.Request{ Method: http.MethodConnect, - URL: &url.URL{Host: backendAddr}, + URL: &url.URL{Host: opts.ConnectAddr}, Header: map[string][]string{"User-Agent": {grpcUA}}, } - if t := proxyURL.User; t != nil { - u := t.Username() - p, _ := t.Password() + if user := opts.User; user != nil { + u := user.Username() + p, _ := user.Password() req.Header.Add(proxyAuthHeaderKey, "Basic "+basicAuth(u, p)) } - if err := sendHTTPRequest(ctx, req, conn); err != nil { return nil, fmt.Errorf("failed to write the HTTP request: %v", err) } @@ -117,28 +98,13 @@ func doHTTPConnectHandshake(ctx context.Context, conn net.Conn, backendAddr stri return conn, nil } -// proxyDial dials, connecting to a proxy first if necessary. Checks if a proxy -// is necessary, dials, does the HTTP CONNECT handshake, and returns the -// connection. -func proxyDial(ctx context.Context, addr string, grpcUA string) (net.Conn, error) { - newAddr := addr - proxyURL, err := mapAddress(addr) - if err != nil { - return nil, err - } - if proxyURL != nil { - newAddr = proxyURL.Host - } - - conn, err := internal.NetDialerWithTCPKeepalive().DialContext(ctx, "tcp", newAddr) +// proxyDial establishes a TCP connection to the specified address and performs an HTTP CONNECT handshake. +func proxyDial(ctx context.Context, addr resolver.Address, grpcUA string, opts proxyattributes.Options) (net.Conn, error) { + conn, err := internal.NetDialerWithTCPKeepalive().DialContext(ctx, "tcp", addr.Addr) if err != nil { return nil, err } - if proxyURL == nil { - // proxy is disabled if proxyURL is nil. - return conn, err - } - return doHTTPConnectHandshake(ctx, conn, addr, proxyURL, grpcUA) + return doHTTPConnectHandshake(ctx, conn, grpcUA, opts) } func sendHTTPRequest(ctx context.Context, req *http.Request, conn net.Conn) error { diff --git a/vendor/google.golang.org/grpc/internal/transport/transport.go b/vendor/google.golang.org/grpc/internal/transport/transport.go index 2859b87755..af4a4aeab1 100644 --- a/vendor/google.golang.org/grpc/internal/transport/transport.go +++ b/vendor/google.golang.org/grpc/internal/transport/transport.go @@ -502,8 +502,6 @@ type ConnectOptions struct { ChannelzParent *channelz.SubChannel // MaxHeaderListSize sets the max (uncompressed) size of header list that is prepared to be received. MaxHeaderListSize *uint32 - // UseProxy specifies if a proxy should be used. - UseProxy bool // The mem.BufferPool to use when reading/writing to the wire. BufferPool mem.BufferPool } diff --git a/vendor/google.golang.org/grpc/picker_wrapper.go b/vendor/google.golang.org/grpc/picker_wrapper.go index bdaa2130e4..a2d2a798d4 100644 --- a/vendor/google.golang.org/grpc/picker_wrapper.go +++ b/vendor/google.golang.org/grpc/picker_wrapper.go @@ -123,7 +123,7 @@ func (pw *pickerWrapper) pick(ctx context.Context, failfast bool, info balancer. if lastPickErr != nil { errStr = "latest balancer error: " + lastPickErr.Error() } else { - errStr = fmt.Sprintf("received context error while waiting for new LB policy update: %s", ctx.Err().Error()) + errStr = fmt.Sprintf("%v while waiting for connections to become ready", ctx.Err()) } switch ctx.Err() { case context.DeadlineExceeded: diff --git a/vendor/google.golang.org/grpc/resolver/resolver.go b/vendor/google.golang.org/grpc/resolver/resolver.go index 8eb1cf3bcf..b84ef26d46 100644 --- a/vendor/google.golang.org/grpc/resolver/resolver.go +++ b/vendor/google.golang.org/grpc/resolver/resolver.go @@ -30,6 +30,7 @@ import ( "google.golang.org/grpc/attributes" "google.golang.org/grpc/credentials" + "google.golang.org/grpc/experimental/stats" "google.golang.org/grpc/internal" "google.golang.org/grpc/serviceconfig" ) @@ -175,6 +176,8 @@ type BuildOptions struct { // Authority is the effective authority of the clientconn for which the // resolver is built. Authority string + // MetricsRecorder is the metrics recorder to do recording. + MetricsRecorder stats.MetricsRecorder } // An Endpoint is one network endpoint, or server, which may have multiple diff --git a/vendor/google.golang.org/grpc/resolver_wrapper.go b/vendor/google.golang.org/grpc/resolver_wrapper.go index 23bb3fb258..945e24ff83 100644 --- a/vendor/google.golang.org/grpc/resolver_wrapper.go +++ b/vendor/google.golang.org/grpc/resolver_wrapper.go @@ -26,6 +26,7 @@ import ( "google.golang.org/grpc/internal/channelz" "google.golang.org/grpc/internal/grpcsync" "google.golang.org/grpc/internal/pretty" + "google.golang.org/grpc/internal/resolver/delegatingresolver" "google.golang.org/grpc/resolver" "google.golang.org/grpc/serviceconfig" ) @@ -76,9 +77,19 @@ func (ccr *ccResolverWrapper) start() error { CredsBundle: ccr.cc.dopts.copts.CredsBundle, Dialer: ccr.cc.dopts.copts.Dialer, Authority: ccr.cc.authority, + MetricsRecorder: ccr.cc.metricsRecorderList, } var err error - ccr.resolver, err = ccr.cc.resolverBuilder.Build(ccr.cc.parsedTarget, ccr, opts) + // The delegating resolver is used unless: + // - A custom dialer is provided via WithContextDialer dialoption or + // - Proxy usage is disabled through WithNoProxy dialoption. + // In these cases, the resolver is built based on the scheme of target, + // using the appropriate resolver builder. + if ccr.cc.dopts.copts.Dialer != nil || !ccr.cc.dopts.useProxy { + ccr.resolver, err = ccr.cc.resolverBuilder.Build(ccr.cc.parsedTarget, ccr, opts) + } else { + ccr.resolver, err = delegatingresolver.New(ccr.cc.parsedTarget, ccr, opts, ccr.cc.resolverBuilder, ccr.cc.dopts.enableLocalDNSResolution) + } errCh <- err }) return <-errCh diff --git a/vendor/google.golang.org/grpc/rpc_util.go b/vendor/google.golang.org/grpc/rpc_util.go index 9fac2b08b4..a8ddb0af52 100644 --- a/vendor/google.golang.org/grpc/rpc_util.go +++ b/vendor/google.golang.org/grpc/rpc_util.go @@ -151,7 +151,7 @@ func (d *gzipDecompressor) Type() string { // callInfo contains all related configuration and information about an RPC. type callInfo struct { - compressorType string + compressorName string failFast bool maxReceiveMessageSize *int maxSendMessageSize *int @@ -222,7 +222,7 @@ type HeaderCallOption struct { func (o HeaderCallOption) before(*callInfo) error { return nil } func (o HeaderCallOption) after(_ *callInfo, attempt *csAttempt) { - *o.HeaderAddr, _ = attempt.s.Header() + *o.HeaderAddr, _ = attempt.transportStream.Header() } // Trailer returns a CallOptions that retrieves the trailer metadata @@ -244,7 +244,7 @@ type TrailerCallOption struct { func (o TrailerCallOption) before(*callInfo) error { return nil } func (o TrailerCallOption) after(_ *callInfo, attempt *csAttempt) { - *o.TrailerAddr = attempt.s.Trailer() + *o.TrailerAddr = attempt.transportStream.Trailer() } // Peer returns a CallOption that retrieves peer information for a unary RPC. @@ -266,7 +266,7 @@ type PeerCallOption struct { func (o PeerCallOption) before(*callInfo) error { return nil } func (o PeerCallOption) after(_ *callInfo, attempt *csAttempt) { - if x, ok := peer.FromContext(attempt.s.Context()); ok { + if x, ok := peer.FromContext(attempt.transportStream.Context()); ok { *o.PeerAddr = *x } } @@ -435,7 +435,7 @@ type CompressorCallOption struct { } func (o CompressorCallOption) before(c *callInfo) error { - c.compressorType = o.CompressorType + c.compressorName = o.CompressorType return nil } func (o CompressorCallOption) after(*callInfo, *csAttempt) {} @@ -692,9 +692,9 @@ func encode(c baseCodec, msg any) (mem.BufferSlice, error) { if err != nil { return nil, status.Errorf(codes.Internal, "grpc: error while marshaling: %v", err.Error()) } - if uint(b.Len()) > math.MaxUint32 { + if bufSize := uint(b.Len()); bufSize > math.MaxUint32 { b.Free() - return nil, status.Errorf(codes.ResourceExhausted, "grpc: message too large (%d bytes)", len(b)) + return nil, status.Errorf(codes.ResourceExhausted, "grpc: message too large (%d bytes)", bufSize) } return b, nil } @@ -828,30 +828,13 @@ func recvAndDecompress(p *parser, s recvCompressor, dc Decompressor, maxReceiveM return nil, st.Err() } - var size int if pf.isCompressed() { defer compressed.Free() - // To match legacy behavior, if the decompressor is set by WithDecompressor or RPCDecompressor, // use this decompressor as the default. - if dc != nil { - var uncompressedBuf []byte - uncompressedBuf, err = dc.Do(compressed.Reader()) - if err == nil { - out = mem.BufferSlice{mem.SliceBuffer(uncompressedBuf)} - } - size = len(uncompressedBuf) - } else { - out, size, err = decompress(compressor, compressed, maxReceiveMessageSize, p.bufferPool) - } + out, err = decompress(compressor, compressed, dc, maxReceiveMessageSize, p.bufferPool) if err != nil { - return nil, status.Errorf(codes.Internal, "grpc: failed to decompress the received message: %v", err) - } - if size > maxReceiveMessageSize { - out.Free() - // TODO: Revisit the error code. Currently keep it consistent with java - // implementation. - return nil, status.Errorf(codes.ResourceExhausted, "grpc: received message after decompression larger than max (%d vs. %d)", size, maxReceiveMessageSize) + return nil, err } } else { out = compressed @@ -866,20 +849,46 @@ func recvAndDecompress(p *parser, s recvCompressor, dc Decompressor, maxReceiveM return out, nil } -// Using compressor, decompress d, returning data and size. -// Optionally, if data will be over maxReceiveMessageSize, just return the size. -func decompress(compressor encoding.Compressor, d mem.BufferSlice, maxReceiveMessageSize int, pool mem.BufferPool) (mem.BufferSlice, int, error) { - dcReader, err := compressor.Decompress(d.Reader()) - if err != nil { - return nil, 0, err +// decompress processes the given data by decompressing it using either a custom decompressor or a standard compressor. +// If a custom decompressor is provided, it takes precedence. The function validates that the decompressed data +// does not exceed the specified maximum size and returns an error if this limit is exceeded. +// On success, it returns the decompressed data. Otherwise, it returns an error if decompression fails or the data exceeds the size limit. +func decompress(compressor encoding.Compressor, d mem.BufferSlice, dc Decompressor, maxReceiveMessageSize int, pool mem.BufferPool) (mem.BufferSlice, error) { + if dc != nil { + uncompressed, err := dc.Do(d.Reader()) + if err != nil { + return nil, status.Errorf(codes.Internal, "grpc: failed to decompress the received message: %v", err) + } + if len(uncompressed) > maxReceiveMessageSize { + return nil, status.Errorf(codes.ResourceExhausted, "grpc: message after decompression larger than max (%d vs. %d)", len(uncompressed), maxReceiveMessageSize) + } + return mem.BufferSlice{mem.SliceBuffer(uncompressed)}, nil } + if compressor != nil { + dcReader, err := compressor.Decompress(d.Reader()) + if err != nil { + return nil, status.Errorf(codes.Internal, "grpc: failed to decompress the message: %v", err) + } - out, err := mem.ReadAll(io.LimitReader(dcReader, int64(maxReceiveMessageSize)+1), pool) - if err != nil { - out.Free() - return nil, 0, err + out, err := mem.ReadAll(io.LimitReader(dcReader, int64(maxReceiveMessageSize)), pool) + if err != nil { + out.Free() + return nil, status.Errorf(codes.Internal, "grpc: failed to read decompressed data: %v", err) + } + + if out.Len() == maxReceiveMessageSize && !atEOF(dcReader) { + out.Free() + return nil, status.Errorf(codes.ResourceExhausted, "grpc: received message after decompression larger than max %d", maxReceiveMessageSize) + } + return out, nil } - return out, out.Len(), nil + return nil, status.Errorf(codes.Internal, "grpc: no decompressor available for compressed payload") +} + +// atEOF reads data from r and returns true if zero bytes could be read and r.Read returns EOF. +func atEOF(dcReader io.Reader) bool { + n, err := dcReader.Read(make([]byte, 1)) + return n == 0 && err == io.EOF } type recvCompressor interface { diff --git a/vendor/google.golang.org/grpc/server.go b/vendor/google.golang.org/grpc/server.go index 9d5b2884d1..976e70ae06 100644 --- a/vendor/google.golang.org/grpc/server.go +++ b/vendor/google.golang.org/grpc/server.go @@ -37,12 +37,14 @@ import ( "google.golang.org/grpc/credentials" "google.golang.org/grpc/encoding" "google.golang.org/grpc/encoding/proto" + estats "google.golang.org/grpc/experimental/stats" "google.golang.org/grpc/grpclog" "google.golang.org/grpc/internal" "google.golang.org/grpc/internal/binarylog" "google.golang.org/grpc/internal/channelz" "google.golang.org/grpc/internal/grpcsync" "google.golang.org/grpc/internal/grpcutil" + istats "google.golang.org/grpc/internal/stats" "google.golang.org/grpc/internal/transport" "google.golang.org/grpc/keepalive" "google.golang.org/grpc/mem" @@ -82,6 +84,9 @@ func init() { internal.BinaryLogger = binaryLogger internal.JoinServerOptions = newJoinServerOption internal.BufferPool = bufferPool + internal.MetricsRecorderForServer = func(srv *Server) estats.MetricsRecorder { + return istats.NewMetricsRecorderList(srv.opts.statsHandlers) + } } var statusOK = status.New(codes.OK, "") @@ -643,7 +648,7 @@ func (s *Server) serverWorker() { // connections to reduce the time spent overall on runtime.morestack. func (s *Server) initServerWorkers() { s.serverWorkerChannel = make(chan func()) - s.serverWorkerChannelClose = grpcsync.OnceFunc(func() { + s.serverWorkerChannelClose = sync.OnceFunc(func() { close(s.serverWorkerChannel) }) for i := uint32(0); i < s.opts.numServerWorkers; i++ { @@ -1645,10 +1650,10 @@ func (s *Server) processStreamingRPC(ctx context.Context, stream *transport.Serv // If dc is set and matches the stream's compression, use it. Otherwise, try // to find a matching registered compressor for decomp. if rc := stream.RecvCompress(); s.opts.dc != nil && s.opts.dc.Type() == rc { - ss.dc = s.opts.dc + ss.decompressorV0 = s.opts.dc } else if rc != "" && rc != encoding.Identity { - ss.decomp = encoding.GetCompressor(rc) - if ss.decomp == nil { + ss.decompressorV1 = encoding.GetCompressor(rc) + if ss.decompressorV1 == nil { st := status.Newf(codes.Unimplemented, "grpc: Decompressor is not installed for grpc-encoding %q", rc) ss.s.WriteStatus(st) return st.Err() @@ -1660,12 +1665,12 @@ func (s *Server) processStreamingRPC(ctx context.Context, stream *transport.Serv // // NOTE: this needs to be ahead of all handling, https://github.com/grpc/grpc-go/issues/686. if s.opts.cp != nil { - ss.cp = s.opts.cp + ss.compressorV0 = s.opts.cp ss.sendCompressorName = s.opts.cp.Type() } else if rc := stream.RecvCompress(); rc != "" && rc != encoding.Identity { // Legacy compressor not specified; attempt to respond with same encoding. - ss.comp = encoding.GetCompressor(rc) - if ss.comp != nil { + ss.compressorV1 = encoding.GetCompressor(rc) + if ss.compressorV1 != nil { ss.sendCompressorName = rc } } @@ -1676,7 +1681,7 @@ func (s *Server) processStreamingRPC(ctx context.Context, stream *transport.Serv } } - ss.ctx = newContextWithRPCInfo(ss.ctx, false, ss.codec, ss.cp, ss.comp) + ss.ctx = newContextWithRPCInfo(ss.ctx, false, ss.codec, ss.compressorV0, ss.compressorV1) if trInfo != nil { trInfo.tr.LazyLog(&trInfo.firstLine, false) @@ -1930,7 +1935,7 @@ func (s *Server) stop(graceful bool) { s.conns = nil if s.opts.numServerWorkers > 0 { - // Closing the channel (only once, via grpcsync.OnceFunc) after all the + // Closing the channel (only once, via sync.OnceFunc) after all the // connections have been closed above ensures that there are no // goroutines executing the callback passed to st.HandleStreams (where // the channel is written to). diff --git a/vendor/google.golang.org/grpc/stream.go b/vendor/google.golang.org/grpc/stream.go index 54adbbced7..12163150ba 100644 --- a/vendor/google.golang.org/grpc/stream.go +++ b/vendor/google.golang.org/grpc/stream.go @@ -258,9 +258,9 @@ func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, meth } func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, mc serviceconfig.MethodConfig, onCommit, doneFunc func(), opts ...CallOption) (_ iresolver.ClientStream, err error) { - c := defaultCallInfo() + callInfo := defaultCallInfo() if mc.WaitForReady != nil { - c.failFast = !*mc.WaitForReady + callInfo.failFast = !*mc.WaitForReady } // Possible context leak: @@ -281,20 +281,20 @@ func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *Client }() for _, o := range opts { - if err := o.before(c); err != nil { + if err := o.before(callInfo); err != nil { return nil, toRPCErr(err) } } - c.maxSendMessageSize = getMaxSize(mc.MaxReqSize, c.maxSendMessageSize, defaultClientMaxSendMessageSize) - c.maxReceiveMessageSize = getMaxSize(mc.MaxRespSize, c.maxReceiveMessageSize, defaultClientMaxReceiveMessageSize) - if err := setCallInfoCodec(c); err != nil { + callInfo.maxSendMessageSize = getMaxSize(mc.MaxReqSize, callInfo.maxSendMessageSize, defaultClientMaxSendMessageSize) + callInfo.maxReceiveMessageSize = getMaxSize(mc.MaxRespSize, callInfo.maxReceiveMessageSize, defaultClientMaxReceiveMessageSize) + if err := setCallInfoCodec(callInfo); err != nil { return nil, err } callHdr := &transport.CallHdr{ Host: cc.authority, Method: method, - ContentSubtype: c.contentSubtype, + ContentSubtype: callInfo.contentSubtype, DoneFunc: doneFunc, } @@ -302,22 +302,22 @@ func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *Client // set. In that case, also find the compressor from the encoding package. // Otherwise, use the compressor configured by the WithCompressor DialOption, // if set. - var cp Compressor - var comp encoding.Compressor - if ct := c.compressorType; ct != "" { + var compressorV0 Compressor + var compressorV1 encoding.Compressor + if ct := callInfo.compressorName; ct != "" { callHdr.SendCompress = ct if ct != encoding.Identity { - comp = encoding.GetCompressor(ct) - if comp == nil { + compressorV1 = encoding.GetCompressor(ct) + if compressorV1 == nil { return nil, status.Errorf(codes.Internal, "grpc: Compressor is not installed for requested grpc-encoding %q", ct) } } - } else if cc.dopts.cp != nil { - callHdr.SendCompress = cc.dopts.cp.Type() - cp = cc.dopts.cp + } else if cc.dopts.compressorV0 != nil { + callHdr.SendCompress = cc.dopts.compressorV0.Type() + compressorV0 = cc.dopts.compressorV0 } - if c.creds != nil { - callHdr.Creds = c.creds + if callInfo.creds != nil { + callHdr.Creds = callInfo.creds } cs := &clientStream{ @@ -325,12 +325,12 @@ func newClientStreamWithParams(ctx context.Context, desc *StreamDesc, cc *Client ctx: ctx, methodConfig: &mc, opts: opts, - callInfo: c, + callInfo: callInfo, cc: cc, desc: desc, - codec: c.codec, - cp: cp, - comp: comp, + codec: callInfo.codec, + compressorV0: compressorV0, + compressorV1: compressorV1, cancel: cancel, firstAttempt: true, onCommit: onCommit, @@ -412,7 +412,7 @@ func (cs *clientStream) newAttemptLocked(isTransparent bool) (*csAttempt, error) return nil, ErrClientConnClosing } - ctx := newContextWithRPCInfo(cs.ctx, cs.callInfo.failFast, cs.callInfo.codec, cs.cp, cs.comp) + ctx := newContextWithRPCInfo(cs.ctx, cs.callInfo.failFast, cs.callInfo.codec, cs.compressorV0, cs.compressorV1) method := cs.callHdr.Method var beginTime time.Time shs := cs.cc.dopts.copts.StatsHandlers @@ -454,12 +454,12 @@ func (cs *clientStream) newAttemptLocked(isTransparent bool) (*csAttempt, error) } return &csAttempt{ - ctx: ctx, - beginTime: beginTime, - cs: cs, - dc: cs.cc.dopts.dc, - statsHandlers: shs, - trInfo: trInfo, + ctx: ctx, + beginTime: beginTime, + cs: cs, + decompressorV0: cs.cc.dopts.dc, + statsHandlers: shs, + trInfo: trInfo, }, nil } @@ -467,7 +467,7 @@ func (a *csAttempt) getTransport() error { cs := a.cs var err error - a.t, a.pickResult, err = cs.cc.getTransport(a.ctx, cs.callInfo.failFast, cs.callHdr.Method) + a.transport, a.pickResult, err = cs.cc.getTransport(a.ctx, cs.callInfo.failFast, cs.callHdr.Method) if err != nil { if de, ok := err.(dropError); ok { err = de.error @@ -476,7 +476,7 @@ func (a *csAttempt) getTransport() error { return err } if a.trInfo != nil { - a.trInfo.firstLine.SetRemoteAddr(a.t.RemoteAddr()) + a.trInfo.firstLine.SetRemoteAddr(a.transport.RemoteAddr()) } return nil } @@ -503,7 +503,7 @@ func (a *csAttempt) newStream() error { a.ctx = metadata.NewOutgoingContext(a.ctx, md) } - s, err := a.t.NewStream(a.ctx, cs.callHdr) + s, err := a.transport.NewStream(a.ctx, cs.callHdr) if err != nil { nse, ok := err.(*transport.NewStreamError) if !ok { @@ -518,9 +518,9 @@ func (a *csAttempt) newStream() error { // Unwrap and convert error. return toRPCErr(nse.Err) } - a.s = s + a.transportStream = s a.ctx = s.Context() - a.p = &parser{r: s, bufferPool: a.cs.cc.dopts.copts.BufferPool} + a.parser = &parser{r: s, bufferPool: a.cs.cc.dopts.copts.BufferPool} return nil } @@ -532,9 +532,9 @@ type clientStream struct { cc *ClientConn desc *StreamDesc - codec baseCodec - cp Compressor - comp encoding.Compressor + codec baseCodec + compressorV0 Compressor + compressorV1 encoding.Compressor cancel context.CancelFunc // cancels all attempts @@ -583,17 +583,17 @@ type replayOp struct { // csAttempt implements a single transport stream attempt within a // clientStream. type csAttempt struct { - ctx context.Context - cs *clientStream - t transport.ClientTransport - s *transport.ClientStream - p *parser - pickResult balancer.PickResult - - finished bool - dc Decompressor - decomp encoding.Compressor - decompSet bool + ctx context.Context + cs *clientStream + transport transport.ClientTransport + transportStream *transport.ClientStream + parser *parser + pickResult balancer.PickResult + + finished bool + decompressorV0 Decompressor + decompressorV1 encoding.Compressor + decompressorSet bool mu sync.Mutex // guards trInfo.tr // trInfo may be nil (if EnableTracing is false). @@ -639,14 +639,14 @@ func (a *csAttempt) shouldRetry(err error) (bool, error) { // RPC is finished or committed or was dropped by the picker; cannot retry. return false, err } - if a.s == nil && a.allowTransparentRetry { + if a.transportStream == nil && a.allowTransparentRetry { return true, nil } // Wait for the trailers. unprocessed := false - if a.s != nil { - <-a.s.Done() - unprocessed = a.s.Unprocessed() + if a.transportStream != nil { + <-a.transportStream.Done() + unprocessed = a.transportStream.Unprocessed() } if cs.firstAttempt && unprocessed { // First attempt, stream unprocessed: transparently retry. @@ -658,14 +658,14 @@ func (a *csAttempt) shouldRetry(err error) (bool, error) { pushback := 0 hasPushback := false - if a.s != nil { - if !a.s.TrailersOnly() { + if a.transportStream != nil { + if !a.transportStream.TrailersOnly() { return false, err } // TODO(retry): Move down if the spec changes to not check server pushback // before considering this a failure for throttling. - sps := a.s.Trailer()["grpc-retry-pushback-ms"] + sps := a.transportStream.Trailer()["grpc-retry-pushback-ms"] if len(sps) == 1 { var e error if pushback, e = strconv.Atoi(sps[0]); e != nil || pushback < 0 { @@ -682,8 +682,8 @@ func (a *csAttempt) shouldRetry(err error) (bool, error) { } var code codes.Code - if a.s != nil { - code = a.s.Status().Code() + if a.transportStream != nil { + code = a.transportStream.Status().Code() } else { code = status.Code(err) } @@ -756,8 +756,8 @@ func (cs *clientStream) Context() context.Context { cs.commitAttempt() // No need to lock before using attempt, since we know it is committed and // cannot change. - if cs.attempt.s != nil { - return cs.attempt.s.Context() + if cs.attempt.transportStream != nil { + return cs.attempt.transportStream.Context() } return cs.ctx } @@ -794,9 +794,9 @@ func (cs *clientStream) withRetry(op func(a *csAttempt) error, onSuccess func()) continue } if err == io.EOF { - <-a.s.Done() + <-a.transportStream.Done() } - if err == nil || (err == io.EOF && a.s.Status().Code() == codes.OK) { + if err == nil || (err == io.EOF && a.transportStream.Status().Code() == codes.OK) { onSuccess() cs.mu.Unlock() return err @@ -812,7 +812,7 @@ func (cs *clientStream) Header() (metadata.MD, error) { var m metadata.MD err := cs.withRetry(func(a *csAttempt) error { var err error - m, err = a.s.Header() + m, err = a.transportStream.Header() return toRPCErr(err) }, cs.commitAttemptLocked) @@ -856,10 +856,10 @@ func (cs *clientStream) Trailer() metadata.MD { // directions -- it will prevent races and should not meaningfully impact // performance. cs.commitAttempt() - if cs.attempt.s == nil { + if cs.attempt.transportStream == nil { return nil } - return cs.attempt.s.Trailer() + return cs.attempt.transportStream.Trailer() } func (cs *clientStream) replayBufferLocked(attempt *csAttempt) error { @@ -904,7 +904,7 @@ func (cs *clientStream) SendMsg(m any) (err error) { } // load hdr, payload, data - hdr, data, payload, pf, err := prepareMsg(m, cs.codec, cs.cp, cs.comp, cs.cc.dopts.copts.BufferPool) + hdr, data, payload, pf, err := prepareMsg(m, cs.codec, cs.compressorV0, cs.compressorV1, cs.cc.dopts.copts.BufferPool) if err != nil { return err } @@ -992,7 +992,7 @@ func (cs *clientStream) CloseSend() error { } cs.sentLast = true op := func(a *csAttempt) error { - a.s.Write(nil, nil, &transport.WriteOptions{Last: true}) + a.transportStream.Write(nil, nil, &transport.WriteOptions{Last: true}) // Always return nil; io.EOF is the only error that might make sense // instead, but there is no need to signal the client to call RecvMsg // as the only use left for the stream after CloseSend is to call @@ -1030,7 +1030,7 @@ func (cs *clientStream) finish(err error) { if cs.attempt != nil { cs.attempt.finish(err) // after functions all rely upon having a stream. - if cs.attempt.s != nil { + if cs.attempt.transportStream != nil { for _, o := range cs.opts { o.after(cs.callInfo, cs.attempt) } @@ -1084,7 +1084,7 @@ func (a *csAttempt) sendMsg(m any, hdr []byte, payld mem.BufferSlice, dataLength } a.mu.Unlock() } - if err := a.s.Write(hdr, payld, &transport.WriteOptions{Last: !cs.desc.ClientStreams}); err != nil { + if err := a.transportStream.Write(hdr, payld, &transport.WriteOptions{Last: !cs.desc.ClientStreams}); err != nil { if !cs.desc.ClientStreams { // For non-client-streaming RPCs, we return nil instead of EOF on error // because the generated code requires it. finish is not called; RecvMsg() @@ -1108,25 +1108,25 @@ func (a *csAttempt) recvMsg(m any, payInfo *payloadInfo) (err error) { defer payInfo.free() } - if !a.decompSet { + if !a.decompressorSet { // Block until we receive headers containing received message encoding. - if ct := a.s.RecvCompress(); ct != "" && ct != encoding.Identity { - if a.dc == nil || a.dc.Type() != ct { + if ct := a.transportStream.RecvCompress(); ct != "" && ct != encoding.Identity { + if a.decompressorV0 == nil || a.decompressorV0.Type() != ct { // No configured decompressor, or it does not match the incoming // message encoding; attempt to find a registered compressor that does. - a.dc = nil - a.decomp = encoding.GetCompressor(ct) + a.decompressorV0 = nil + a.decompressorV1 = encoding.GetCompressor(ct) } } else { // No compression is used; disable our decompressor. - a.dc = nil + a.decompressorV0 = nil } // Only initialize this state once per stream. - a.decompSet = true + a.decompressorSet = true } - if err := recv(a.p, cs.codec, a.s, a.dc, m, *cs.callInfo.maxReceiveMessageSize, payInfo, a.decomp, false); err != nil { + if err := recv(a.parser, cs.codec, a.transportStream, a.decompressorV0, m, *cs.callInfo.maxReceiveMessageSize, payInfo, a.decompressorV1, false); err != nil { if err == io.EOF { - if statusErr := a.s.Status().Err(); statusErr != nil { + if statusErr := a.transportStream.Status().Err(); statusErr != nil { return statusErr } return io.EOF // indicates successful end of stream. @@ -1157,8 +1157,8 @@ func (a *csAttempt) recvMsg(m any, payInfo *payloadInfo) (err error) { } // Special handling for non-server-stream rpcs. // This recv expects EOF or errors, so we don't collect inPayload. - if err := recv(a.p, cs.codec, a.s, a.dc, m, *cs.callInfo.maxReceiveMessageSize, nil, a.decomp, false); err == io.EOF { - return a.s.Status().Err() // non-server streaming Recv returns nil on success + if err := recv(a.parser, cs.codec, a.transportStream, a.decompressorV0, m, *cs.callInfo.maxReceiveMessageSize, nil, a.decompressorV1, false); err == io.EOF { + return a.transportStream.Status().Err() // non-server streaming Recv returns nil on success } else if err != nil { return toRPCErr(err) } @@ -1177,20 +1177,20 @@ func (a *csAttempt) finish(err error) { err = nil } var tr metadata.MD - if a.s != nil { - a.s.Close(err) - tr = a.s.Trailer() + if a.transportStream != nil { + a.transportStream.Close(err) + tr = a.transportStream.Trailer() } if a.pickResult.Done != nil { br := false - if a.s != nil { - br = a.s.BytesReceived() + if a.transportStream != nil { + br = a.transportStream.BytesReceived() } a.pickResult.Done(balancer.DoneInfo{ Err: err, Trailer: tr, - BytesSent: a.s != nil, + BytesSent: a.transportStream != nil, BytesReceived: br, ServerLoad: balancerload.Parse(tr), }) @@ -1272,7 +1272,7 @@ func newNonRetryClientStream(ctx context.Context, desc *StreamDesc, method strin // if set. var cp Compressor var comp encoding.Compressor - if ct := c.compressorType; ct != "" { + if ct := c.compressorName; ct != "" { callHdr.SendCompress = ct if ct != encoding.Identity { comp = encoding.GetCompressor(ct) @@ -1280,9 +1280,9 @@ func newNonRetryClientStream(ctx context.Context, desc *StreamDesc, method strin return nil, status.Errorf(codes.Internal, "grpc: Compressor is not installed for requested grpc-encoding %q", ct) } } - } else if ac.cc.dopts.cp != nil { - callHdr.SendCompress = ac.cc.dopts.cp.Type() - cp = ac.cc.dopts.cp + } else if ac.cc.dopts.compressorV0 != nil { + callHdr.SendCompress = ac.cc.dopts.compressorV0.Type() + cp = ac.cc.dopts.compressorV0 } if c.creds != nil { callHdr.Creds = c.creds @@ -1290,26 +1290,26 @@ func newNonRetryClientStream(ctx context.Context, desc *StreamDesc, method strin // Use a special addrConnStream to avoid retry. as := &addrConnStream{ - callHdr: callHdr, - ac: ac, - ctx: ctx, - cancel: cancel, - opts: opts, - callInfo: c, - desc: desc, - codec: c.codec, - cp: cp, - comp: comp, - t: t, - } - - s, err := as.t.NewStream(as.ctx, as.callHdr) + callHdr: callHdr, + ac: ac, + ctx: ctx, + cancel: cancel, + opts: opts, + callInfo: c, + desc: desc, + codec: c.codec, + sendCompressorV0: cp, + sendCompressorV1: comp, + transport: t, + } + + s, err := as.transport.NewStream(as.ctx, as.callHdr) if err != nil { err = toRPCErr(err) return nil, err } - as.s = s - as.p = &parser{r: s, bufferPool: ac.dopts.copts.BufferPool} + as.transportStream = s + as.parser = &parser{r: s, bufferPool: ac.dopts.copts.BufferPool} ac.incrCallsStarted() if desc != unaryStreamDesc { // Listen on stream context to cleanup when the stream context is @@ -1335,29 +1335,31 @@ func newNonRetryClientStream(ctx context.Context, desc *StreamDesc, method strin } type addrConnStream struct { - s *transport.ClientStream - ac *addrConn - callHdr *transport.CallHdr - cancel context.CancelFunc - opts []CallOption - callInfo *callInfo - t transport.ClientTransport - ctx context.Context - sentLast bool - desc *StreamDesc - codec baseCodec - cp Compressor - comp encoding.Compressor - decompSet bool - dc Decompressor - decomp encoding.Compressor - p *parser - mu sync.Mutex - finished bool + transportStream *transport.ClientStream + ac *addrConn + callHdr *transport.CallHdr + cancel context.CancelFunc + opts []CallOption + callInfo *callInfo + transport transport.ClientTransport + ctx context.Context + sentLast bool + desc *StreamDesc + codec baseCodec + sendCompressorV0 Compressor + sendCompressorV1 encoding.Compressor + decompressorSet bool + decompressorV0 Decompressor + decompressorV1 encoding.Compressor + parser *parser + + // mu guards finished and is held for the entire finish method. + mu sync.Mutex + finished bool } func (as *addrConnStream) Header() (metadata.MD, error) { - m, err := as.s.Header() + m, err := as.transportStream.Header() if err != nil { as.finish(toRPCErr(err)) } @@ -1365,7 +1367,7 @@ func (as *addrConnStream) Header() (metadata.MD, error) { } func (as *addrConnStream) Trailer() metadata.MD { - return as.s.Trailer() + return as.transportStream.Trailer() } func (as *addrConnStream) CloseSend() error { @@ -1375,7 +1377,7 @@ func (as *addrConnStream) CloseSend() error { } as.sentLast = true - as.s.Write(nil, nil, &transport.WriteOptions{Last: true}) + as.transportStream.Write(nil, nil, &transport.WriteOptions{Last: true}) // Always return nil; io.EOF is the only error that might make sense // instead, but there is no need to signal the client to call RecvMsg // as the only use left for the stream after CloseSend is to call @@ -1384,7 +1386,7 @@ func (as *addrConnStream) CloseSend() error { } func (as *addrConnStream) Context() context.Context { - return as.s.Context() + return as.transportStream.Context() } func (as *addrConnStream) SendMsg(m any) (err error) { @@ -1406,7 +1408,7 @@ func (as *addrConnStream) SendMsg(m any) (err error) { } // load hdr, payload, data - hdr, data, payload, pf, err := prepareMsg(m, as.codec, as.cp, as.comp, as.ac.dopts.copts.BufferPool) + hdr, data, payload, pf, err := prepareMsg(m, as.codec, as.sendCompressorV0, as.sendCompressorV1, as.ac.dopts.copts.BufferPool) if err != nil { return err } @@ -1425,7 +1427,7 @@ func (as *addrConnStream) SendMsg(m any) (err error) { return status.Errorf(codes.ResourceExhausted, "trying to send message larger than max (%d vs. %d)", payload.Len(), *as.callInfo.maxSendMessageSize) } - if err := as.s.Write(hdr, payload, &transport.WriteOptions{Last: !as.desc.ClientStreams}); err != nil { + if err := as.transportStream.Write(hdr, payload, &transport.WriteOptions{Last: !as.desc.ClientStreams}); err != nil { if !as.desc.ClientStreams { // For non-client-streaming RPCs, we return nil instead of EOF on error // because the generated code requires it. finish is not called; RecvMsg() @@ -1446,25 +1448,25 @@ func (as *addrConnStream) RecvMsg(m any) (err error) { } }() - if !as.decompSet { + if !as.decompressorSet { // Block until we receive headers containing received message encoding. - if ct := as.s.RecvCompress(); ct != "" && ct != encoding.Identity { - if as.dc == nil || as.dc.Type() != ct { + if ct := as.transportStream.RecvCompress(); ct != "" && ct != encoding.Identity { + if as.decompressorV0 == nil || as.decompressorV0.Type() != ct { // No configured decompressor, or it does not match the incoming // message encoding; attempt to find a registered compressor that does. - as.dc = nil - as.decomp = encoding.GetCompressor(ct) + as.decompressorV0 = nil + as.decompressorV1 = encoding.GetCompressor(ct) } } else { // No compression is used; disable our decompressor. - as.dc = nil + as.decompressorV0 = nil } // Only initialize this state once per stream. - as.decompSet = true + as.decompressorSet = true } - if err := recv(as.p, as.codec, as.s, as.dc, m, *as.callInfo.maxReceiveMessageSize, nil, as.decomp, false); err != nil { + if err := recv(as.parser, as.codec, as.transportStream, as.decompressorV0, m, *as.callInfo.maxReceiveMessageSize, nil, as.decompressorV1, false); err != nil { if err == io.EOF { - if statusErr := as.s.Status().Err(); statusErr != nil { + if statusErr := as.transportStream.Status().Err(); statusErr != nil { return statusErr } return io.EOF // indicates successful end of stream. @@ -1479,8 +1481,8 @@ func (as *addrConnStream) RecvMsg(m any) (err error) { // Special handling for non-server-stream rpcs. // This recv expects EOF or errors, so we don't collect inPayload. - if err := recv(as.p, as.codec, as.s, as.dc, m, *as.callInfo.maxReceiveMessageSize, nil, as.decomp, false); err == io.EOF { - return as.s.Status().Err() // non-server streaming Recv returns nil on success + if err := recv(as.parser, as.codec, as.transportStream, as.decompressorV0, m, *as.callInfo.maxReceiveMessageSize, nil, as.decompressorV1, false); err == io.EOF { + return as.transportStream.Status().Err() // non-server streaming Recv returns nil on success } else if err != nil { return toRPCErr(err) } @@ -1498,8 +1500,8 @@ func (as *addrConnStream) finish(err error) { // Ending a stream with EOF indicates a success. err = nil } - if as.s != nil { - as.s.Close(err) + if as.transportStream != nil { + as.transportStream.Close(err) } if err != nil { @@ -1570,10 +1572,10 @@ type serverStream struct { p *parser codec baseCodec - cp Compressor - dc Decompressor - comp encoding.Compressor - decomp encoding.Compressor + compressorV0 Compressor + compressorV1 encoding.Compressor + decompressorV0 Decompressor + decompressorV1 encoding.Compressor sendCompressorName string @@ -1669,12 +1671,12 @@ func (ss *serverStream) SendMsg(m any) (err error) { // Server handler could have set new compressor by calling SetSendCompressor. // In case it is set, we need to use it for compressing outbound message. if sendCompressorsName := ss.s.SendCompress(); sendCompressorsName != ss.sendCompressorName { - ss.comp = encoding.GetCompressor(sendCompressorsName) + ss.compressorV1 = encoding.GetCompressor(sendCompressorsName) ss.sendCompressorName = sendCompressorsName } // load hdr, payload, data - hdr, data, payload, pf, err := prepareMsg(m, ss.codec, ss.cp, ss.comp, ss.p.bufferPool) + hdr, data, payload, pf, err := prepareMsg(m, ss.codec, ss.compressorV0, ss.compressorV1, ss.p.bufferPool) if err != nil { return err } @@ -1755,7 +1757,7 @@ func (ss *serverStream) RecvMsg(m any) (err error) { payInfo = &payloadInfo{} defer payInfo.free() } - if err := recv(ss.p, ss.codec, ss.s, ss.dc, m, ss.maxReceiveMessageSize, payInfo, ss.decomp, true); err != nil { + if err := recv(ss.p, ss.codec, ss.s, ss.decompressorV0, m, ss.maxReceiveMessageSize, payInfo, ss.decompressorV1, true); err != nil { if err == io.EOF { if len(ss.binlogs) != 0 { chc := &binarylog.ClientHalfClose{} diff --git a/vendor/google.golang.org/grpc/version.go b/vendor/google.golang.org/grpc/version.go index 0e03fa4d4f..783c41f78c 100644 --- a/vendor/google.golang.org/grpc/version.go +++ b/vendor/google.golang.org/grpc/version.go @@ -19,4 +19,4 @@ package grpc // Version is the current grpc version. -const Version = "1.70.0" +const Version = "1.71.0" diff --git a/vendor/gopkg.in/ini.v1/.editorconfig b/vendor/gopkg.in/ini.v1/.editorconfig deleted file mode 100644 index 4a2d9180f9..0000000000 --- a/vendor/gopkg.in/ini.v1/.editorconfig +++ /dev/null @@ -1,12 +0,0 @@ -# http://editorconfig.org - -root = true - -[*] -charset = utf-8 -end_of_line = lf -insert_final_newline = true -trim_trailing_whitespace = true - -[*_test.go] -trim_trailing_whitespace = false diff --git a/vendor/gopkg.in/ini.v1/.gitignore b/vendor/gopkg.in/ini.v1/.gitignore deleted file mode 100644 index 588388bda2..0000000000 --- a/vendor/gopkg.in/ini.v1/.gitignore +++ /dev/null @@ -1,7 +0,0 @@ -testdata/conf_out.ini -ini.sublime-project -ini.sublime-workspace -testdata/conf_reflect.ini -.idea -/.vscode -.DS_Store diff --git a/vendor/gopkg.in/ini.v1/.golangci.yml b/vendor/gopkg.in/ini.v1/.golangci.yml deleted file mode 100644 index 631e369254..0000000000 --- a/vendor/gopkg.in/ini.v1/.golangci.yml +++ /dev/null @@ -1,27 +0,0 @@ -linters-settings: - staticcheck: - checks: [ - "all", - "-SA1019" # There are valid use cases of strings.Title - ] - nakedret: - max-func-lines: 0 # Disallow any unnamed return statement - -linters: - enable: - - deadcode - - errcheck - - gosimple - - govet - - ineffassign - - staticcheck - - structcheck - - typecheck - - unused - - varcheck - - nakedret - - gofmt - - rowserrcheck - - unconvert - - goimports - - unparam diff --git a/vendor/gopkg.in/ini.v1/LICENSE b/vendor/gopkg.in/ini.v1/LICENSE deleted file mode 100644 index d361bbcdf5..0000000000 --- a/vendor/gopkg.in/ini.v1/LICENSE +++ /dev/null @@ -1,191 +0,0 @@ -Apache License -Version 2.0, January 2004 -http://www.apache.org/licenses/ - -TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - -1. Definitions. - -"License" shall mean the terms and conditions for use, reproduction, and -distribution as defined by Sections 1 through 9 of this document. - -"Licensor" shall mean the copyright owner or entity authorized by the copyright -owner that is granting the License. - -"Legal Entity" shall mean the union of the acting entity and all other entities -that control, are controlled by, or are under common control with that entity. -For the purposes of this definition, "control" means (i) the power, direct or -indirect, to cause the direction or management of such entity, whether by -contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the -outstanding shares, or (iii) beneficial ownership of such entity. - -"You" (or "Your") shall mean an individual or Legal Entity exercising -permissions granted by this License. - -"Source" form shall mean the preferred form for making modifications, including -but not limited to software source code, documentation source, and configuration -files. - -"Object" form shall mean any form resulting from mechanical transformation or -translation of a Source form, including but not limited to compiled object code, -generated documentation, and conversions to other media types. - -"Work" shall mean the work of authorship, whether in Source or Object form, made -available under the License, as indicated by a copyright notice that is included -in or attached to the work (an example is provided in the Appendix below). - -"Derivative Works" shall mean any work, whether in Source or Object form, that -is based on (or derived from) the Work and for which the editorial revisions, -annotations, elaborations, or other modifications represent, as a whole, an -original work of authorship. For the purposes of this License, Derivative Works -shall not include works that remain separable from, or merely link (or bind by -name) to the interfaces of, the Work and Derivative Works thereof. - -"Contribution" shall mean any work of authorship, including the original version -of the Work and any modifications or additions to that Work or Derivative Works -thereof, that is intentionally submitted to Licensor for inclusion in the Work -by the copyright owner or by an individual or Legal Entity authorized to submit -on behalf of the copyright owner. For the purposes of this definition, -"submitted" means any form of electronic, verbal, or written communication sent -to the Licensor or its representatives, including but not limited to -communication on electronic mailing lists, source code control systems, and -issue tracking systems that are managed by, or on behalf of, the Licensor for -the purpose of discussing and improving the Work, but excluding communication -that is conspicuously marked or otherwise designated in writing by the copyright -owner as "Not a Contribution." - -"Contributor" shall mean Licensor and any individual or Legal Entity on behalf -of whom a Contribution has been received by Licensor and subsequently -incorporated within the Work. - -2. Grant of Copyright License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable copyright license to reproduce, prepare Derivative Works of, -publicly display, publicly perform, sublicense, and distribute the Work and such -Derivative Works in Source or Object form. - -3. Grant of Patent License. - -Subject to the terms and conditions of this License, each Contributor hereby -grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, -irrevocable (except as stated in this section) patent license to make, have -made, use, offer to sell, sell, import, and otherwise transfer the Work, where -such license applies only to those patent claims licensable by such Contributor -that are necessarily infringed by their Contribution(s) alone or by combination -of their Contribution(s) with the Work to which such Contribution(s) was -submitted. If You institute patent litigation against any entity (including a -cross-claim or counterclaim in a lawsuit) alleging that the Work or a -Contribution incorporated within the Work constitutes direct or contributory -patent infringement, then any patent licenses granted to You under this License -for that Work shall terminate as of the date such litigation is filed. - -4. Redistribution. - -You may reproduce and distribute copies of the Work or Derivative Works thereof -in any medium, with or without modifications, and in Source or Object form, -provided that You meet the following conditions: - -You must give any other recipients of the Work or Derivative Works a copy of -this License; and -You must cause any modified files to carry prominent notices stating that You -changed the files; and -You must retain, in the Source form of any Derivative Works that You distribute, -all copyright, patent, trademark, and attribution notices from the Source form -of the Work, excluding those notices that do not pertain to any part of the -Derivative Works; and -If the Work includes a "NOTICE" text file as part of its distribution, then any -Derivative Works that You distribute must include a readable copy of the -attribution notices contained within such NOTICE file, excluding those notices -that do not pertain to any part of the Derivative Works, in at least one of the -following places: within a NOTICE text file distributed as part of the -Derivative Works; within the Source form or documentation, if provided along -with the Derivative Works; or, within a display generated by the Derivative -Works, if and wherever such third-party notices normally appear. The contents of -the NOTICE file are for informational purposes only and do not modify the -License. You may add Your own attribution notices within Derivative Works that -You distribute, alongside or as an addendum to the NOTICE text from the Work, -provided that such additional attribution notices cannot be construed as -modifying the License. -You may add Your own copyright statement to Your modifications and may provide -additional or different license terms and conditions for use, reproduction, or -distribution of Your modifications, or for any such Derivative Works as a whole, -provided Your use, reproduction, and distribution of the Work otherwise complies -with the conditions stated in this License. - -5. Submission of Contributions. - -Unless You explicitly state otherwise, any Contribution intentionally submitted -for inclusion in the Work by You to the Licensor shall be under the terms and -conditions of this License, without any additional terms or conditions. -Notwithstanding the above, nothing herein shall supersede or modify the terms of -any separate license agreement you may have executed with Licensor regarding -such Contributions. - -6. Trademarks. - -This License does not grant permission to use the trade names, trademarks, -service marks, or product names of the Licensor, except as required for -reasonable and customary use in describing the origin of the Work and -reproducing the content of the NOTICE file. - -7. Disclaimer of Warranty. - -Unless required by applicable law or agreed to in writing, Licensor provides the -Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, -including, without limitation, any warranties or conditions of TITLE, -NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are -solely responsible for determining the appropriateness of using or -redistributing the Work and assume any risks associated with Your exercise of -permissions under this License. - -8. Limitation of Liability. - -In no event and under no legal theory, whether in tort (including negligence), -contract, or otherwise, unless required by applicable law (such as deliberate -and grossly negligent acts) or agreed to in writing, shall any Contributor be -liable to You for damages, including any direct, indirect, special, incidental, -or consequential damages of any character arising as a result of this License or -out of the use or inability to use the Work (including but not limited to -damages for loss of goodwill, work stoppage, computer failure or malfunction, or -any and all other commercial damages or losses), even if such Contributor has -been advised of the possibility of such damages. - -9. Accepting Warranty or Additional Liability. - -While redistributing the Work or Derivative Works thereof, You may choose to -offer, and charge a fee for, acceptance of support, warranty, indemnity, or -other liability obligations and/or rights consistent with this License. However, -in accepting such obligations, You may act only on Your own behalf and on Your -sole responsibility, not on behalf of any other Contributor, and only if You -agree to indemnify, defend, and hold each Contributor harmless for any liability -incurred by, or claims asserted against, such Contributor by reason of your -accepting any such warranty or additional liability. - -END OF TERMS AND CONDITIONS - -APPENDIX: How to apply the Apache License to your work - -To apply the Apache License to your work, attach the following boilerplate -notice, with the fields enclosed by brackets "[]" replaced with your own -identifying information. (Don't include the brackets!) The text should be -enclosed in the appropriate comment syntax for the file format. We also -recommend that a file or class name and description of purpose be included on -the same "printed page" as the copyright notice for easier identification within -third-party archives. - - Copyright 2014 Unknwon - - 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/gopkg.in/ini.v1/Makefile b/vendor/gopkg.in/ini.v1/Makefile deleted file mode 100644 index f3b0dae2d2..0000000000 --- a/vendor/gopkg.in/ini.v1/Makefile +++ /dev/null @@ -1,15 +0,0 @@ -.PHONY: build test bench vet coverage - -build: vet bench - -test: - go test -v -cover -race - -bench: - go test -v -cover -test.bench=. -test.benchmem - -vet: - go vet - -coverage: - go test -coverprofile=c.out && go tool cover -html=c.out && rm c.out diff --git a/vendor/gopkg.in/ini.v1/README.md b/vendor/gopkg.in/ini.v1/README.md deleted file mode 100644 index 30606d9700..0000000000 --- a/vendor/gopkg.in/ini.v1/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# INI - -[![GitHub Workflow Status](https://img.shields.io/github/checks-status/go-ini/ini/main?logo=github&style=for-the-badge)](https://github.com/go-ini/ini/actions?query=branch%3Amain) -[![codecov](https://img.shields.io/codecov/c/github/go-ini/ini/master?logo=codecov&style=for-the-badge)](https://codecov.io/gh/go-ini/ini) -[![GoDoc](https://img.shields.io/badge/GoDoc-Reference-blue?style=for-the-badge&logo=go)](https://pkg.go.dev/github.com/go-ini/ini?tab=doc) -[![Sourcegraph](https://img.shields.io/badge/view%20on-Sourcegraph-brightgreen.svg?style=for-the-badge&logo=sourcegraph)](https://sourcegraph.com/github.com/go-ini/ini) - -![](https://avatars0.githubusercontent.com/u/10216035?v=3&s=200) - -Package ini provides INI file read and write functionality in Go. - -## Features - -- Load from multiple data sources(file, `[]byte`, `io.Reader` and `io.ReadCloser`) with overwrites. -- Read with recursion values. -- Read with parent-child sections. -- Read with auto-increment key names. -- Read with multiple-line values. -- Read with tons of helper methods. -- Read and convert values to Go types. -- Read and **WRITE** comments of sections and keys. -- Manipulate sections, keys and comments with ease. -- Keep sections and keys in order as you parse and save. - -## Installation - -The minimum requirement of Go is **1.13**. - -```sh -$ go get gopkg.in/ini.v1 -``` - -Please add `-u` flag to update in the future. - -## Getting Help - -- [Getting Started](https://ini.unknwon.io/docs/intro/getting_started) -- [API Documentation](https://gowalker.org/gopkg.in/ini.v1) -- 中国大陆镜像:https://ini.unknwon.cn - -## License - -This project is under Apache v2 License. See the [LICENSE](LICENSE) file for the full license text. diff --git a/vendor/gopkg.in/ini.v1/codecov.yml b/vendor/gopkg.in/ini.v1/codecov.yml deleted file mode 100644 index e02ec84bc0..0000000000 --- a/vendor/gopkg.in/ini.v1/codecov.yml +++ /dev/null @@ -1,16 +0,0 @@ -coverage: - range: "60...95" - status: - project: - default: - threshold: 1% - informational: true - patch: - defualt: - only_pulls: true - informational: true - -comment: - layout: 'diff' - -github_checks: false diff --git a/vendor/gopkg.in/ini.v1/data_source.go b/vendor/gopkg.in/ini.v1/data_source.go deleted file mode 100644 index c3a541f1d1..0000000000 --- a/vendor/gopkg.in/ini.v1/data_source.go +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2019 Unknwon -// -// 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 ini - -import ( - "bytes" - "fmt" - "io" - "io/ioutil" - "os" -) - -var ( - _ dataSource = (*sourceFile)(nil) - _ dataSource = (*sourceData)(nil) - _ dataSource = (*sourceReadCloser)(nil) -) - -// dataSource is an interface that returns object which can be read and closed. -type dataSource interface { - ReadCloser() (io.ReadCloser, error) -} - -// sourceFile represents an object that contains content on the local file system. -type sourceFile struct { - name string -} - -func (s sourceFile) ReadCloser() (_ io.ReadCloser, err error) { - return os.Open(s.name) -} - -// sourceData represents an object that contains content in memory. -type sourceData struct { - data []byte -} - -func (s *sourceData) ReadCloser() (io.ReadCloser, error) { - return ioutil.NopCloser(bytes.NewReader(s.data)), nil -} - -// sourceReadCloser represents an input stream with Close method. -type sourceReadCloser struct { - reader io.ReadCloser -} - -func (s *sourceReadCloser) ReadCloser() (io.ReadCloser, error) { - return s.reader, nil -} - -func parseDataSource(source interface{}) (dataSource, error) { - switch s := source.(type) { - case string: - return sourceFile{s}, nil - case []byte: - return &sourceData{s}, nil - case io.ReadCloser: - return &sourceReadCloser{s}, nil - case io.Reader: - return &sourceReadCloser{ioutil.NopCloser(s)}, nil - default: - return nil, fmt.Errorf("error parsing data source: unknown type %q", s) - } -} diff --git a/vendor/gopkg.in/ini.v1/deprecated.go b/vendor/gopkg.in/ini.v1/deprecated.go deleted file mode 100644 index 48b8e66d6d..0000000000 --- a/vendor/gopkg.in/ini.v1/deprecated.go +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2019 Unknwon -// -// 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 ini - -var ( - // Deprecated: Use "DefaultSection" instead. - DEFAULT_SECTION = DefaultSection - // Deprecated: AllCapsUnderscore converts to format ALL_CAPS_UNDERSCORE. - AllCapsUnderscore = SnackCase -) diff --git a/vendor/gopkg.in/ini.v1/error.go b/vendor/gopkg.in/ini.v1/error.go deleted file mode 100644 index f66bc94b8b..0000000000 --- a/vendor/gopkg.in/ini.v1/error.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2016 Unknwon -// -// 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 ini - -import ( - "fmt" -) - -// ErrDelimiterNotFound indicates the error type of no delimiter is found which there should be one. -type ErrDelimiterNotFound struct { - Line string -} - -// IsErrDelimiterNotFound returns true if the given error is an instance of ErrDelimiterNotFound. -func IsErrDelimiterNotFound(err error) bool { - _, ok := err.(ErrDelimiterNotFound) - return ok -} - -func (err ErrDelimiterNotFound) Error() string { - return fmt.Sprintf("key-value delimiter not found: %s", err.Line) -} - -// ErrEmptyKeyName indicates the error type of no key name is found which there should be one. -type ErrEmptyKeyName struct { - Line string -} - -// IsErrEmptyKeyName returns true if the given error is an instance of ErrEmptyKeyName. -func IsErrEmptyKeyName(err error) bool { - _, ok := err.(ErrEmptyKeyName) - return ok -} - -func (err ErrEmptyKeyName) Error() string { - return fmt.Sprintf("empty key name: %s", err.Line) -} diff --git a/vendor/gopkg.in/ini.v1/file.go b/vendor/gopkg.in/ini.v1/file.go deleted file mode 100644 index f8b22408be..0000000000 --- a/vendor/gopkg.in/ini.v1/file.go +++ /dev/null @@ -1,541 +0,0 @@ -// Copyright 2017 Unknwon -// -// 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 ini - -import ( - "bytes" - "errors" - "fmt" - "io" - "io/ioutil" - "os" - "strings" - "sync" -) - -// File represents a combination of one or more INI files in memory. -type File struct { - options LoadOptions - dataSources []dataSource - - // Should make things safe, but sometimes doesn't matter. - BlockMode bool - lock sync.RWMutex - - // To keep data in order. - sectionList []string - // To keep track of the index of a section with same name. - // This meta list is only used with non-unique section names are allowed. - sectionIndexes []int - - // Actual data is stored here. - sections map[string][]*Section - - NameMapper - ValueMapper -} - -// newFile initializes File object with given data sources. -func newFile(dataSources []dataSource, opts LoadOptions) *File { - if len(opts.KeyValueDelimiters) == 0 { - opts.KeyValueDelimiters = "=:" - } - if len(opts.KeyValueDelimiterOnWrite) == 0 { - opts.KeyValueDelimiterOnWrite = "=" - } - if len(opts.ChildSectionDelimiter) == 0 { - opts.ChildSectionDelimiter = "." - } - - return &File{ - BlockMode: true, - dataSources: dataSources, - sections: make(map[string][]*Section), - options: opts, - } -} - -// Empty returns an empty file object. -func Empty(opts ...LoadOptions) *File { - var opt LoadOptions - if len(opts) > 0 { - opt = opts[0] - } - - // Ignore error here, we are sure our data is good. - f, _ := LoadSources(opt, []byte("")) - return f -} - -// NewSection creates a new section. -func (f *File) NewSection(name string) (*Section, error) { - if len(name) == 0 { - return nil, errors.New("empty section name") - } - - if (f.options.Insensitive || f.options.InsensitiveSections) && name != DefaultSection { - name = strings.ToLower(name) - } - - if f.BlockMode { - f.lock.Lock() - defer f.lock.Unlock() - } - - if !f.options.AllowNonUniqueSections && inSlice(name, f.sectionList) { - return f.sections[name][0], nil - } - - f.sectionList = append(f.sectionList, name) - - // NOTE: Append to indexes must happen before appending to sections, - // otherwise index will have off-by-one problem. - f.sectionIndexes = append(f.sectionIndexes, len(f.sections[name])) - - sec := newSection(f, name) - f.sections[name] = append(f.sections[name], sec) - - return sec, nil -} - -// NewRawSection creates a new section with an unparseable body. -func (f *File) NewRawSection(name, body string) (*Section, error) { - section, err := f.NewSection(name) - if err != nil { - return nil, err - } - - section.isRawSection = true - section.rawBody = body - return section, nil -} - -// NewSections creates a list of sections. -func (f *File) NewSections(names ...string) (err error) { - for _, name := range names { - if _, err = f.NewSection(name); err != nil { - return err - } - } - return nil -} - -// GetSection returns section by given name. -func (f *File) GetSection(name string) (*Section, error) { - secs, err := f.SectionsByName(name) - if err != nil { - return nil, err - } - - return secs[0], err -} - -// HasSection returns true if the file contains a section with given name. -func (f *File) HasSection(name string) bool { - section, _ := f.GetSection(name) - return section != nil -} - -// SectionsByName returns all sections with given name. -func (f *File) SectionsByName(name string) ([]*Section, error) { - if len(name) == 0 { - name = DefaultSection - } - if f.options.Insensitive || f.options.InsensitiveSections { - name = strings.ToLower(name) - } - - if f.BlockMode { - f.lock.RLock() - defer f.lock.RUnlock() - } - - secs := f.sections[name] - if len(secs) == 0 { - return nil, fmt.Errorf("section %q does not exist", name) - } - - return secs, nil -} - -// Section assumes named section exists and returns a zero-value when not. -func (f *File) Section(name string) *Section { - sec, err := f.GetSection(name) - if err != nil { - if name == "" { - name = DefaultSection - } - sec, _ = f.NewSection(name) - return sec - } - return sec -} - -// SectionWithIndex assumes named section exists and returns a new section when not. -func (f *File) SectionWithIndex(name string, index int) *Section { - secs, err := f.SectionsByName(name) - if err != nil || len(secs) <= index { - // NOTE: It's OK here because the only possible error is empty section name, - // but if it's empty, this piece of code won't be executed. - newSec, _ := f.NewSection(name) - return newSec - } - - return secs[index] -} - -// Sections returns a list of Section stored in the current instance. -func (f *File) Sections() []*Section { - if f.BlockMode { - f.lock.RLock() - defer f.lock.RUnlock() - } - - sections := make([]*Section, len(f.sectionList)) - for i, name := range f.sectionList { - sections[i] = f.sections[name][f.sectionIndexes[i]] - } - return sections -} - -// ChildSections returns a list of child sections of given section name. -func (f *File) ChildSections(name string) []*Section { - return f.Section(name).ChildSections() -} - -// SectionStrings returns list of section names. -func (f *File) SectionStrings() []string { - list := make([]string, len(f.sectionList)) - copy(list, f.sectionList) - return list -} - -// DeleteSection deletes a section or all sections with given name. -func (f *File) DeleteSection(name string) { - secs, err := f.SectionsByName(name) - if err != nil { - return - } - - for i := 0; i < len(secs); i++ { - // For non-unique sections, it is always needed to remove the first one so - // in the next iteration, the subsequent section continue having index 0. - // Ignoring the error as index 0 never returns an error. - _ = f.DeleteSectionWithIndex(name, 0) - } -} - -// DeleteSectionWithIndex deletes a section with given name and index. -func (f *File) DeleteSectionWithIndex(name string, index int) error { - if !f.options.AllowNonUniqueSections && index != 0 { - return fmt.Errorf("delete section with non-zero index is only allowed when non-unique sections is enabled") - } - - if len(name) == 0 { - name = DefaultSection - } - if f.options.Insensitive || f.options.InsensitiveSections { - name = strings.ToLower(name) - } - - if f.BlockMode { - f.lock.Lock() - defer f.lock.Unlock() - } - - // Count occurrences of the sections - occurrences := 0 - - sectionListCopy := make([]string, len(f.sectionList)) - copy(sectionListCopy, f.sectionList) - - for i, s := range sectionListCopy { - if s != name { - continue - } - - if occurrences == index { - if len(f.sections[name]) <= 1 { - delete(f.sections, name) // The last one in the map - } else { - f.sections[name] = append(f.sections[name][:index], f.sections[name][index+1:]...) - } - - // Fix section lists - f.sectionList = append(f.sectionList[:i], f.sectionList[i+1:]...) - f.sectionIndexes = append(f.sectionIndexes[:i], f.sectionIndexes[i+1:]...) - - } else if occurrences > index { - // Fix the indices of all following sections with this name. - f.sectionIndexes[i-1]-- - } - - occurrences++ - } - - return nil -} - -func (f *File) reload(s dataSource) error { - r, err := s.ReadCloser() - if err != nil { - return err - } - defer r.Close() - - return f.parse(r) -} - -// Reload reloads and parses all data sources. -func (f *File) Reload() (err error) { - for _, s := range f.dataSources { - if err = f.reload(s); err != nil { - // In loose mode, we create an empty default section for nonexistent files. - if os.IsNotExist(err) && f.options.Loose { - _ = f.parse(bytes.NewBuffer(nil)) - continue - } - return err - } - if f.options.ShortCircuit { - return nil - } - } - return nil -} - -// Append appends one or more data sources and reloads automatically. -func (f *File) Append(source interface{}, others ...interface{}) error { - ds, err := parseDataSource(source) - if err != nil { - return err - } - f.dataSources = append(f.dataSources, ds) - for _, s := range others { - ds, err = parseDataSource(s) - if err != nil { - return err - } - f.dataSources = append(f.dataSources, ds) - } - return f.Reload() -} - -func (f *File) writeToBuffer(indent string) (*bytes.Buffer, error) { - equalSign := DefaultFormatLeft + f.options.KeyValueDelimiterOnWrite + DefaultFormatRight - - if PrettyFormat || PrettyEqual { - equalSign = fmt.Sprintf(" %s ", f.options.KeyValueDelimiterOnWrite) - } - - // Use buffer to make sure target is safe until finish encoding. - buf := bytes.NewBuffer(nil) - lastSectionIdx := len(f.sectionList) - 1 - for i, sname := range f.sectionList { - sec := f.SectionWithIndex(sname, f.sectionIndexes[i]) - if len(sec.Comment) > 0 { - // Support multiline comments - lines := strings.Split(sec.Comment, LineBreak) - for i := range lines { - if lines[i][0] != '#' && lines[i][0] != ';' { - lines[i] = "; " + lines[i] - } else { - lines[i] = lines[i][:1] + " " + strings.TrimSpace(lines[i][1:]) - } - - if _, err := buf.WriteString(lines[i] + LineBreak); err != nil { - return nil, err - } - } - } - - if i > 0 || DefaultHeader || (i == 0 && strings.ToUpper(sec.name) != DefaultSection) { - if _, err := buf.WriteString("[" + sname + "]" + LineBreak); err != nil { - return nil, err - } - } else { - // Write nothing if default section is empty - if len(sec.keyList) == 0 { - continue - } - } - - isLastSection := i == lastSectionIdx - if sec.isRawSection { - if _, err := buf.WriteString(sec.rawBody); err != nil { - return nil, err - } - - if PrettySection && !isLastSection { - // Put a line between sections - if _, err := buf.WriteString(LineBreak); err != nil { - return nil, err - } - } - continue - } - - // Count and generate alignment length and buffer spaces using the - // longest key. Keys may be modified if they contain certain characters so - // we need to take that into account in our calculation. - alignLength := 0 - if PrettyFormat { - for _, kname := range sec.keyList { - keyLength := len(kname) - // First case will surround key by ` and second by """ - if strings.Contains(kname, "\"") || strings.ContainsAny(kname, f.options.KeyValueDelimiters) { - keyLength += 2 - } else if strings.Contains(kname, "`") { - keyLength += 6 - } - - if keyLength > alignLength { - alignLength = keyLength - } - } - } - alignSpaces := bytes.Repeat([]byte(" "), alignLength) - - KeyList: - for _, kname := range sec.keyList { - key := sec.Key(kname) - if len(key.Comment) > 0 { - if len(indent) > 0 && sname != DefaultSection { - buf.WriteString(indent) - } - - // Support multiline comments - lines := strings.Split(key.Comment, LineBreak) - for i := range lines { - if lines[i][0] != '#' && lines[i][0] != ';' { - lines[i] = "; " + strings.TrimSpace(lines[i]) - } else { - lines[i] = lines[i][:1] + " " + strings.TrimSpace(lines[i][1:]) - } - - if _, err := buf.WriteString(lines[i] + LineBreak); err != nil { - return nil, err - } - } - } - - if len(indent) > 0 && sname != DefaultSection { - buf.WriteString(indent) - } - - switch { - case key.isAutoIncrement: - kname = "-" - case strings.Contains(kname, "\"") || strings.ContainsAny(kname, f.options.KeyValueDelimiters): - kname = "`" + kname + "`" - case strings.Contains(kname, "`"): - kname = `"""` + kname + `"""` - } - - writeKeyValue := func(val string) (bool, error) { - if _, err := buf.WriteString(kname); err != nil { - return false, err - } - - if key.isBooleanType { - buf.WriteString(LineBreak) - return true, nil - } - - // Write out alignment spaces before "=" sign - if PrettyFormat { - buf.Write(alignSpaces[:alignLength-len(kname)]) - } - - // In case key value contains "\n", "`", "\"", "#" or ";" - if strings.ContainsAny(val, "\n`") { - val = `"""` + val + `"""` - } else if !f.options.IgnoreInlineComment && strings.ContainsAny(val, "#;") { - val = "`" + val + "`" - } else if len(strings.TrimSpace(val)) != len(val) { - val = `"` + val + `"` - } - if _, err := buf.WriteString(equalSign + val + LineBreak); err != nil { - return false, err - } - return false, nil - } - - shadows := key.ValueWithShadows() - if len(shadows) == 0 { - if _, err := writeKeyValue(""); err != nil { - return nil, err - } - } - - for _, val := range shadows { - exitLoop, err := writeKeyValue(val) - if err != nil { - return nil, err - } else if exitLoop { - continue KeyList - } - } - - for _, val := range key.nestedValues { - if _, err := buf.WriteString(indent + " " + val + LineBreak); err != nil { - return nil, err - } - } - } - - if PrettySection && !isLastSection { - // Put a line between sections - if _, err := buf.WriteString(LineBreak); err != nil { - return nil, err - } - } - } - - return buf, nil -} - -// WriteToIndent writes content into io.Writer with given indention. -// If PrettyFormat has been set to be true, -// it will align "=" sign with spaces under each section. -func (f *File) WriteToIndent(w io.Writer, indent string) (int64, error) { - buf, err := f.writeToBuffer(indent) - if err != nil { - return 0, err - } - return buf.WriteTo(w) -} - -// WriteTo writes file content into io.Writer. -func (f *File) WriteTo(w io.Writer) (int64, error) { - return f.WriteToIndent(w, "") -} - -// SaveToIndent writes content to file system with given value indention. -func (f *File) SaveToIndent(filename, indent string) error { - // Note: Because we are truncating with os.Create, - // so it's safer to save to a temporary file location and rename after done. - buf, err := f.writeToBuffer(indent) - if err != nil { - return err - } - - return ioutil.WriteFile(filename, buf.Bytes(), 0666) -} - -// SaveTo writes content to file system. -func (f *File) SaveTo(filename string) error { - return f.SaveToIndent(filename, "") -} diff --git a/vendor/gopkg.in/ini.v1/helper.go b/vendor/gopkg.in/ini.v1/helper.go deleted file mode 100644 index f9d80a682a..0000000000 --- a/vendor/gopkg.in/ini.v1/helper.go +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2019 Unknwon -// -// 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 ini - -func inSlice(str string, s []string) bool { - for _, v := range s { - if str == v { - return true - } - } - return false -} diff --git a/vendor/gopkg.in/ini.v1/ini.go b/vendor/gopkg.in/ini.v1/ini.go deleted file mode 100644 index 99e7f86511..0000000000 --- a/vendor/gopkg.in/ini.v1/ini.go +++ /dev/null @@ -1,176 +0,0 @@ -// Copyright 2014 Unknwon -// -// 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 ini provides INI file read and write functionality in Go. -package ini - -import ( - "os" - "regexp" - "runtime" - "strings" -) - -const ( - // Maximum allowed depth when recursively substituing variable names. - depthValues = 99 -) - -var ( - // DefaultSection is the name of default section. You can use this var or the string literal. - // In most of cases, an empty string is all you need to access the section. - DefaultSection = "DEFAULT" - - // LineBreak is the delimiter to determine or compose a new line. - // This variable will be changed to "\r\n" automatically on Windows at package init time. - LineBreak = "\n" - - // Variable regexp pattern: %(variable)s - varPattern = regexp.MustCompile(`%\(([^)]+)\)s`) - - // DefaultHeader explicitly writes default section header. - DefaultHeader = false - - // PrettySection indicates whether to put a line between sections. - PrettySection = true - // PrettyFormat indicates whether to align "=" sign with spaces to produce pretty output - // or reduce all possible spaces for compact format. - PrettyFormat = true - // PrettyEqual places spaces around "=" sign even when PrettyFormat is false. - PrettyEqual = false - // DefaultFormatLeft places custom spaces on the left when PrettyFormat and PrettyEqual are both disabled. - DefaultFormatLeft = "" - // DefaultFormatRight places custom spaces on the right when PrettyFormat and PrettyEqual are both disabled. - DefaultFormatRight = "" -) - -var inTest = len(os.Args) > 0 && strings.HasSuffix(strings.TrimSuffix(os.Args[0], ".exe"), ".test") - -func init() { - if runtime.GOOS == "windows" && !inTest { - LineBreak = "\r\n" - } -} - -// LoadOptions contains all customized options used for load data source(s). -type LoadOptions struct { - // Loose indicates whether the parser should ignore nonexistent files or return error. - Loose bool - // Insensitive indicates whether the parser forces all section and key names to lowercase. - Insensitive bool - // InsensitiveSections indicates whether the parser forces all section to lowercase. - InsensitiveSections bool - // InsensitiveKeys indicates whether the parser forces all key names to lowercase. - InsensitiveKeys bool - // IgnoreContinuation indicates whether to ignore continuation lines while parsing. - IgnoreContinuation bool - // IgnoreInlineComment indicates whether to ignore comments at the end of value and treat it as part of value. - IgnoreInlineComment bool - // SkipUnrecognizableLines indicates whether to skip unrecognizable lines that do not conform to key/value pairs. - SkipUnrecognizableLines bool - // ShortCircuit indicates whether to ignore other configuration sources after loaded the first available configuration source. - ShortCircuit bool - // AllowBooleanKeys indicates whether to allow boolean type keys or treat as value is missing. - // This type of keys are mostly used in my.cnf. - AllowBooleanKeys bool - // AllowShadows indicates whether to keep track of keys with same name under same section. - AllowShadows bool - // AllowNestedValues indicates whether to allow AWS-like nested values. - // Docs: http://docs.aws.amazon.com/cli/latest/topic/config-vars.html#nested-values - AllowNestedValues bool - // AllowPythonMultilineValues indicates whether to allow Python-like multi-line values. - // Docs: https://docs.python.org/3/library/configparser.html#supported-ini-file-structure - // Relevant quote: Values can also span multiple lines, as long as they are indented deeper - // than the first line of the value. - AllowPythonMultilineValues bool - // SpaceBeforeInlineComment indicates whether to allow comment symbols (\# and \;) inside value. - // Docs: https://docs.python.org/2/library/configparser.html - // Quote: Comments may appear on their own in an otherwise empty line, or may be entered in lines holding values or section names. - // In the latter case, they need to be preceded by a whitespace character to be recognized as a comment. - SpaceBeforeInlineComment bool - // UnescapeValueDoubleQuotes indicates whether to unescape double quotes inside value to regular format - // when value is surrounded by double quotes, e.g. key="a \"value\"" => key=a "value" - UnescapeValueDoubleQuotes bool - // UnescapeValueCommentSymbols indicates to unescape comment symbols (\# and \;) inside value to regular format - // when value is NOT surrounded by any quotes. - // Note: UNSTABLE, behavior might change to only unescape inside double quotes but may noy necessary at all. - UnescapeValueCommentSymbols bool - // UnparseableSections stores a list of blocks that are allowed with raw content which do not otherwise - // conform to key/value pairs. Specify the names of those blocks here. - UnparseableSections []string - // KeyValueDelimiters is the sequence of delimiters that are used to separate key and value. By default, it is "=:". - KeyValueDelimiters string - // KeyValueDelimiterOnWrite is the delimiter that are used to separate key and value output. By default, it is "=". - KeyValueDelimiterOnWrite string - // ChildSectionDelimiter is the delimiter that is used to separate child sections. By default, it is ".". - ChildSectionDelimiter string - // PreserveSurroundedQuote indicates whether to preserve surrounded quote (single and double quotes). - PreserveSurroundedQuote bool - // DebugFunc is called to collect debug information (currently only useful to debug parsing Python-style multiline values). - DebugFunc DebugFunc - // ReaderBufferSize is the buffer size of the reader in bytes. - ReaderBufferSize int - // AllowNonUniqueSections indicates whether to allow sections with the same name multiple times. - AllowNonUniqueSections bool - // AllowDuplicateShadowValues indicates whether values for shadowed keys should be deduplicated. - AllowDuplicateShadowValues bool -} - -// DebugFunc is the type of function called to log parse events. -type DebugFunc func(message string) - -// LoadSources allows caller to apply customized options for loading from data source(s). -func LoadSources(opts LoadOptions, source interface{}, others ...interface{}) (_ *File, err error) { - sources := make([]dataSource, len(others)+1) - sources[0], err = parseDataSource(source) - if err != nil { - return nil, err - } - for i := range others { - sources[i+1], err = parseDataSource(others[i]) - if err != nil { - return nil, err - } - } - f := newFile(sources, opts) - if err = f.Reload(); err != nil { - return nil, err - } - return f, nil -} - -// Load loads and parses from INI data sources. -// Arguments can be mixed of file name with string type, or raw data in []byte. -// It will return error if list contains nonexistent files. -func Load(source interface{}, others ...interface{}) (*File, error) { - return LoadSources(LoadOptions{}, source, others...) -} - -// LooseLoad has exactly same functionality as Load function -// except it ignores nonexistent files instead of returning error. -func LooseLoad(source interface{}, others ...interface{}) (*File, error) { - return LoadSources(LoadOptions{Loose: true}, source, others...) -} - -// InsensitiveLoad has exactly same functionality as Load function -// except it forces all section and key names to be lowercased. -func InsensitiveLoad(source interface{}, others ...interface{}) (*File, error) { - return LoadSources(LoadOptions{Insensitive: true}, source, others...) -} - -// ShadowLoad has exactly same functionality as Load function -// except it allows have shadow keys. -func ShadowLoad(source interface{}, others ...interface{}) (*File, error) { - return LoadSources(LoadOptions{AllowShadows: true}, source, others...) -} diff --git a/vendor/gopkg.in/ini.v1/key.go b/vendor/gopkg.in/ini.v1/key.go deleted file mode 100644 index a19d9f38ef..0000000000 --- a/vendor/gopkg.in/ini.v1/key.go +++ /dev/null @@ -1,837 +0,0 @@ -// Copyright 2014 Unknwon -// -// 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 ini - -import ( - "bytes" - "errors" - "fmt" - "strconv" - "strings" - "time" -) - -// Key represents a key under a section. -type Key struct { - s *Section - Comment string - name string - value string - isAutoIncrement bool - isBooleanType bool - - isShadow bool - shadows []*Key - - nestedValues []string -} - -// newKey simply return a key object with given values. -func newKey(s *Section, name, val string) *Key { - return &Key{ - s: s, - name: name, - value: val, - } -} - -func (k *Key) addShadow(val string) error { - if k.isShadow { - return errors.New("cannot add shadow to another shadow key") - } else if k.isAutoIncrement || k.isBooleanType { - return errors.New("cannot add shadow to auto-increment or boolean key") - } - - if !k.s.f.options.AllowDuplicateShadowValues { - // Deduplicate shadows based on their values. - if k.value == val { - return nil - } - for i := range k.shadows { - if k.shadows[i].value == val { - return nil - } - } - } - - shadow := newKey(k.s, k.name, val) - shadow.isShadow = true - k.shadows = append(k.shadows, shadow) - return nil -} - -// AddShadow adds a new shadow key to itself. -func (k *Key) AddShadow(val string) error { - if !k.s.f.options.AllowShadows { - return errors.New("shadow key is not allowed") - } - return k.addShadow(val) -} - -func (k *Key) addNestedValue(val string) error { - if k.isAutoIncrement || k.isBooleanType { - return errors.New("cannot add nested value to auto-increment or boolean key") - } - - k.nestedValues = append(k.nestedValues, val) - return nil -} - -// AddNestedValue adds a nested value to the key. -func (k *Key) AddNestedValue(val string) error { - if !k.s.f.options.AllowNestedValues { - return errors.New("nested value is not allowed") - } - return k.addNestedValue(val) -} - -// ValueMapper represents a mapping function for values, e.g. os.ExpandEnv -type ValueMapper func(string) string - -// Name returns name of key. -func (k *Key) Name() string { - return k.name -} - -// Value returns raw value of key for performance purpose. -func (k *Key) Value() string { - return k.value -} - -// ValueWithShadows returns raw values of key and its shadows if any. Shadow -// keys with empty values are ignored from the returned list. -func (k *Key) ValueWithShadows() []string { - if len(k.shadows) == 0 { - if k.value == "" { - return []string{} - } - return []string{k.value} - } - - vals := make([]string, 0, len(k.shadows)+1) - if k.value != "" { - vals = append(vals, k.value) - } - for _, s := range k.shadows { - if s.value != "" { - vals = append(vals, s.value) - } - } - return vals -} - -// NestedValues returns nested values stored in the key. -// It is possible returned value is nil if no nested values stored in the key. -func (k *Key) NestedValues() []string { - return k.nestedValues -} - -// transformValue takes a raw value and transforms to its final string. -func (k *Key) transformValue(val string) string { - if k.s.f.ValueMapper != nil { - val = k.s.f.ValueMapper(val) - } - - // Fail-fast if no indicate char found for recursive value - if !strings.Contains(val, "%") { - return val - } - for i := 0; i < depthValues; i++ { - vr := varPattern.FindString(val) - if len(vr) == 0 { - break - } - - // Take off leading '%(' and trailing ')s'. - noption := vr[2 : len(vr)-2] - - // Search in the same section. - // If not found or found the key itself, then search again in default section. - nk, err := k.s.GetKey(noption) - if err != nil || k == nk { - nk, _ = k.s.f.Section("").GetKey(noption) - if nk == nil { - // Stop when no results found in the default section, - // and returns the value as-is. - break - } - } - - // Substitute by new value and take off leading '%(' and trailing ')s'. - val = strings.Replace(val, vr, nk.value, -1) - } - return val -} - -// String returns string representation of value. -func (k *Key) String() string { - return k.transformValue(k.value) -} - -// Validate accepts a validate function which can -// return modifed result as key value. -func (k *Key) Validate(fn func(string) string) string { - return fn(k.String()) -} - -// parseBool returns the boolean value represented by the string. -// -// It accepts 1, t, T, TRUE, true, True, YES, yes, Yes, y, ON, on, On, -// 0, f, F, FALSE, false, False, NO, no, No, n, OFF, off, Off. -// Any other value returns an error. -func parseBool(str string) (value bool, err error) { - switch str { - case "1", "t", "T", "true", "TRUE", "True", "YES", "yes", "Yes", "y", "ON", "on", "On": - return true, nil - case "0", "f", "F", "false", "FALSE", "False", "NO", "no", "No", "n", "OFF", "off", "Off": - return false, nil - } - return false, fmt.Errorf("parsing \"%s\": invalid syntax", str) -} - -// Bool returns bool type value. -func (k *Key) Bool() (bool, error) { - return parseBool(k.String()) -} - -// Float64 returns float64 type value. -func (k *Key) Float64() (float64, error) { - return strconv.ParseFloat(k.String(), 64) -} - -// Int returns int type value. -func (k *Key) Int() (int, error) { - v, err := strconv.ParseInt(k.String(), 0, 64) - return int(v), err -} - -// Int64 returns int64 type value. -func (k *Key) Int64() (int64, error) { - return strconv.ParseInt(k.String(), 0, 64) -} - -// Uint returns uint type valued. -func (k *Key) Uint() (uint, error) { - u, e := strconv.ParseUint(k.String(), 0, 64) - return uint(u), e -} - -// Uint64 returns uint64 type value. -func (k *Key) Uint64() (uint64, error) { - return strconv.ParseUint(k.String(), 0, 64) -} - -// Duration returns time.Duration type value. -func (k *Key) Duration() (time.Duration, error) { - return time.ParseDuration(k.String()) -} - -// TimeFormat parses with given format and returns time.Time type value. -func (k *Key) TimeFormat(format string) (time.Time, error) { - return time.Parse(format, k.String()) -} - -// Time parses with RFC3339 format and returns time.Time type value. -func (k *Key) Time() (time.Time, error) { - return k.TimeFormat(time.RFC3339) -} - -// MustString returns default value if key value is empty. -func (k *Key) MustString(defaultVal string) string { - val := k.String() - if len(val) == 0 { - k.value = defaultVal - return defaultVal - } - return val -} - -// MustBool always returns value without error, -// it returns false if error occurs. -func (k *Key) MustBool(defaultVal ...bool) bool { - val, err := k.Bool() - if len(defaultVal) > 0 && err != nil { - k.value = strconv.FormatBool(defaultVal[0]) - return defaultVal[0] - } - return val -} - -// MustFloat64 always returns value without error, -// it returns 0.0 if error occurs. -func (k *Key) MustFloat64(defaultVal ...float64) float64 { - val, err := k.Float64() - if len(defaultVal) > 0 && err != nil { - k.value = strconv.FormatFloat(defaultVal[0], 'f', -1, 64) - return defaultVal[0] - } - return val -} - -// MustInt always returns value without error, -// it returns 0 if error occurs. -func (k *Key) MustInt(defaultVal ...int) int { - val, err := k.Int() - if len(defaultVal) > 0 && err != nil { - k.value = strconv.FormatInt(int64(defaultVal[0]), 10) - return defaultVal[0] - } - return val -} - -// MustInt64 always returns value without error, -// it returns 0 if error occurs. -func (k *Key) MustInt64(defaultVal ...int64) int64 { - val, err := k.Int64() - if len(defaultVal) > 0 && err != nil { - k.value = strconv.FormatInt(defaultVal[0], 10) - return defaultVal[0] - } - return val -} - -// MustUint always returns value without error, -// it returns 0 if error occurs. -func (k *Key) MustUint(defaultVal ...uint) uint { - val, err := k.Uint() - if len(defaultVal) > 0 && err != nil { - k.value = strconv.FormatUint(uint64(defaultVal[0]), 10) - return defaultVal[0] - } - return val -} - -// MustUint64 always returns value without error, -// it returns 0 if error occurs. -func (k *Key) MustUint64(defaultVal ...uint64) uint64 { - val, err := k.Uint64() - if len(defaultVal) > 0 && err != nil { - k.value = strconv.FormatUint(defaultVal[0], 10) - return defaultVal[0] - } - return val -} - -// MustDuration always returns value without error, -// it returns zero value if error occurs. -func (k *Key) MustDuration(defaultVal ...time.Duration) time.Duration { - val, err := k.Duration() - if len(defaultVal) > 0 && err != nil { - k.value = defaultVal[0].String() - return defaultVal[0] - } - return val -} - -// MustTimeFormat always parses with given format and returns value without error, -// it returns zero value if error occurs. -func (k *Key) MustTimeFormat(format string, defaultVal ...time.Time) time.Time { - val, err := k.TimeFormat(format) - if len(defaultVal) > 0 && err != nil { - k.value = defaultVal[0].Format(format) - return defaultVal[0] - } - return val -} - -// MustTime always parses with RFC3339 format and returns value without error, -// it returns zero value if error occurs. -func (k *Key) MustTime(defaultVal ...time.Time) time.Time { - return k.MustTimeFormat(time.RFC3339, defaultVal...) -} - -// In always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) In(defaultVal string, candidates []string) string { - val := k.String() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InFloat64 always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InFloat64(defaultVal float64, candidates []float64) float64 { - val := k.MustFloat64() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InInt always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InInt(defaultVal int, candidates []int) int { - val := k.MustInt() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InInt64 always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InInt64(defaultVal int64, candidates []int64) int64 { - val := k.MustInt64() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InUint always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InUint(defaultVal uint, candidates []uint) uint { - val := k.MustUint() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InUint64 always returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InUint64(defaultVal uint64, candidates []uint64) uint64 { - val := k.MustUint64() - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InTimeFormat always parses with given format and returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InTimeFormat(format string, defaultVal time.Time, candidates []time.Time) time.Time { - val := k.MustTimeFormat(format) - for _, cand := range candidates { - if val == cand { - return val - } - } - return defaultVal -} - -// InTime always parses with RFC3339 format and returns value without error, -// it returns default value if error occurs or doesn't fit into candidates. -func (k *Key) InTime(defaultVal time.Time, candidates []time.Time) time.Time { - return k.InTimeFormat(time.RFC3339, defaultVal, candidates) -} - -// RangeFloat64 checks if value is in given range inclusively, -// and returns default value if it's not. -func (k *Key) RangeFloat64(defaultVal, min, max float64) float64 { - val := k.MustFloat64() - if val < min || val > max { - return defaultVal - } - return val -} - -// RangeInt checks if value is in given range inclusively, -// and returns default value if it's not. -func (k *Key) RangeInt(defaultVal, min, max int) int { - val := k.MustInt() - if val < min || val > max { - return defaultVal - } - return val -} - -// RangeInt64 checks if value is in given range inclusively, -// and returns default value if it's not. -func (k *Key) RangeInt64(defaultVal, min, max int64) int64 { - val := k.MustInt64() - if val < min || val > max { - return defaultVal - } - return val -} - -// RangeTimeFormat checks if value with given format is in given range inclusively, -// and returns default value if it's not. -func (k *Key) RangeTimeFormat(format string, defaultVal, min, max time.Time) time.Time { - val := k.MustTimeFormat(format) - if val.Unix() < min.Unix() || val.Unix() > max.Unix() { - return defaultVal - } - return val -} - -// RangeTime checks if value with RFC3339 format is in given range inclusively, -// and returns default value if it's not. -func (k *Key) RangeTime(defaultVal, min, max time.Time) time.Time { - return k.RangeTimeFormat(time.RFC3339, defaultVal, min, max) -} - -// Strings returns list of string divided by given delimiter. -func (k *Key) Strings(delim string) []string { - str := k.String() - if len(str) == 0 { - return []string{} - } - - runes := []rune(str) - vals := make([]string, 0, 2) - var buf bytes.Buffer - escape := false - idx := 0 - for { - if escape { - escape = false - if runes[idx] != '\\' && !strings.HasPrefix(string(runes[idx:]), delim) { - buf.WriteRune('\\') - } - buf.WriteRune(runes[idx]) - } else { - if runes[idx] == '\\' { - escape = true - } else if strings.HasPrefix(string(runes[idx:]), delim) { - idx += len(delim) - 1 - vals = append(vals, strings.TrimSpace(buf.String())) - buf.Reset() - } else { - buf.WriteRune(runes[idx]) - } - } - idx++ - if idx == len(runes) { - break - } - } - - if buf.Len() > 0 { - vals = append(vals, strings.TrimSpace(buf.String())) - } - - return vals -} - -// StringsWithShadows returns list of string divided by given delimiter. -// Shadows will also be appended if any. -func (k *Key) StringsWithShadows(delim string) []string { - vals := k.ValueWithShadows() - results := make([]string, 0, len(vals)*2) - for i := range vals { - if len(vals) == 0 { - continue - } - - results = append(results, strings.Split(vals[i], delim)...) - } - - for i := range results { - results[i] = k.transformValue(strings.TrimSpace(results[i])) - } - return results -} - -// Float64s returns list of float64 divided by given delimiter. Any invalid input will be treated as zero value. -func (k *Key) Float64s(delim string) []float64 { - vals, _ := k.parseFloat64s(k.Strings(delim), true, false) - return vals -} - -// Ints returns list of int divided by given delimiter. Any invalid input will be treated as zero value. -func (k *Key) Ints(delim string) []int { - vals, _ := k.parseInts(k.Strings(delim), true, false) - return vals -} - -// Int64s returns list of int64 divided by given delimiter. Any invalid input will be treated as zero value. -func (k *Key) Int64s(delim string) []int64 { - vals, _ := k.parseInt64s(k.Strings(delim), true, false) - return vals -} - -// Uints returns list of uint divided by given delimiter. Any invalid input will be treated as zero value. -func (k *Key) Uints(delim string) []uint { - vals, _ := k.parseUints(k.Strings(delim), true, false) - return vals -} - -// Uint64s returns list of uint64 divided by given delimiter. Any invalid input will be treated as zero value. -func (k *Key) Uint64s(delim string) []uint64 { - vals, _ := k.parseUint64s(k.Strings(delim), true, false) - return vals -} - -// Bools returns list of bool divided by given delimiter. Any invalid input will be treated as zero value. -func (k *Key) Bools(delim string) []bool { - vals, _ := k.parseBools(k.Strings(delim), true, false) - return vals -} - -// TimesFormat parses with given format and returns list of time.Time divided by given delimiter. -// Any invalid input will be treated as zero value (0001-01-01 00:00:00 +0000 UTC). -func (k *Key) TimesFormat(format, delim string) []time.Time { - vals, _ := k.parseTimesFormat(format, k.Strings(delim), true, false) - return vals -} - -// Times parses with RFC3339 format and returns list of time.Time divided by given delimiter. -// Any invalid input will be treated as zero value (0001-01-01 00:00:00 +0000 UTC). -func (k *Key) Times(delim string) []time.Time { - return k.TimesFormat(time.RFC3339, delim) -} - -// ValidFloat64s returns list of float64 divided by given delimiter. If some value is not float, then -// it will not be included to result list. -func (k *Key) ValidFloat64s(delim string) []float64 { - vals, _ := k.parseFloat64s(k.Strings(delim), false, false) - return vals -} - -// ValidInts returns list of int divided by given delimiter. If some value is not integer, then it will -// not be included to result list. -func (k *Key) ValidInts(delim string) []int { - vals, _ := k.parseInts(k.Strings(delim), false, false) - return vals -} - -// ValidInt64s returns list of int64 divided by given delimiter. If some value is not 64-bit integer, -// then it will not be included to result list. -func (k *Key) ValidInt64s(delim string) []int64 { - vals, _ := k.parseInt64s(k.Strings(delim), false, false) - return vals -} - -// ValidUints returns list of uint divided by given delimiter. If some value is not unsigned integer, -// then it will not be included to result list. -func (k *Key) ValidUints(delim string) []uint { - vals, _ := k.parseUints(k.Strings(delim), false, false) - return vals -} - -// ValidUint64s returns list of uint64 divided by given delimiter. If some value is not 64-bit unsigned -// integer, then it will not be included to result list. -func (k *Key) ValidUint64s(delim string) []uint64 { - vals, _ := k.parseUint64s(k.Strings(delim), false, false) - return vals -} - -// ValidBools returns list of bool divided by given delimiter. If some value is not 64-bit unsigned -// integer, then it will not be included to result list. -func (k *Key) ValidBools(delim string) []bool { - vals, _ := k.parseBools(k.Strings(delim), false, false) - return vals -} - -// ValidTimesFormat parses with given format and returns list of time.Time divided by given delimiter. -func (k *Key) ValidTimesFormat(format, delim string) []time.Time { - vals, _ := k.parseTimesFormat(format, k.Strings(delim), false, false) - return vals -} - -// ValidTimes parses with RFC3339 format and returns list of time.Time divided by given delimiter. -func (k *Key) ValidTimes(delim string) []time.Time { - return k.ValidTimesFormat(time.RFC3339, delim) -} - -// StrictFloat64s returns list of float64 divided by given delimiter or error on first invalid input. -func (k *Key) StrictFloat64s(delim string) ([]float64, error) { - return k.parseFloat64s(k.Strings(delim), false, true) -} - -// StrictInts returns list of int divided by given delimiter or error on first invalid input. -func (k *Key) StrictInts(delim string) ([]int, error) { - return k.parseInts(k.Strings(delim), false, true) -} - -// StrictInt64s returns list of int64 divided by given delimiter or error on first invalid input. -func (k *Key) StrictInt64s(delim string) ([]int64, error) { - return k.parseInt64s(k.Strings(delim), false, true) -} - -// StrictUints returns list of uint divided by given delimiter or error on first invalid input. -func (k *Key) StrictUints(delim string) ([]uint, error) { - return k.parseUints(k.Strings(delim), false, true) -} - -// StrictUint64s returns list of uint64 divided by given delimiter or error on first invalid input. -func (k *Key) StrictUint64s(delim string) ([]uint64, error) { - return k.parseUint64s(k.Strings(delim), false, true) -} - -// StrictBools returns list of bool divided by given delimiter or error on first invalid input. -func (k *Key) StrictBools(delim string) ([]bool, error) { - return k.parseBools(k.Strings(delim), false, true) -} - -// StrictTimesFormat parses with given format and returns list of time.Time divided by given delimiter -// or error on first invalid input. -func (k *Key) StrictTimesFormat(format, delim string) ([]time.Time, error) { - return k.parseTimesFormat(format, k.Strings(delim), false, true) -} - -// StrictTimes parses with RFC3339 format and returns list of time.Time divided by given delimiter -// or error on first invalid input. -func (k *Key) StrictTimes(delim string) ([]time.Time, error) { - return k.StrictTimesFormat(time.RFC3339, delim) -} - -// parseBools transforms strings to bools. -func (k *Key) parseBools(strs []string, addInvalid, returnOnInvalid bool) ([]bool, error) { - vals := make([]bool, 0, len(strs)) - parser := func(str string) (interface{}, error) { - val, err := parseBool(str) - return val, err - } - rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser) - if err == nil { - for _, val := range rawVals { - vals = append(vals, val.(bool)) - } - } - return vals, err -} - -// parseFloat64s transforms strings to float64s. -func (k *Key) parseFloat64s(strs []string, addInvalid, returnOnInvalid bool) ([]float64, error) { - vals := make([]float64, 0, len(strs)) - parser := func(str string) (interface{}, error) { - val, err := strconv.ParseFloat(str, 64) - return val, err - } - rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser) - if err == nil { - for _, val := range rawVals { - vals = append(vals, val.(float64)) - } - } - return vals, err -} - -// parseInts transforms strings to ints. -func (k *Key) parseInts(strs []string, addInvalid, returnOnInvalid bool) ([]int, error) { - vals := make([]int, 0, len(strs)) - parser := func(str string) (interface{}, error) { - val, err := strconv.ParseInt(str, 0, 64) - return val, err - } - rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser) - if err == nil { - for _, val := range rawVals { - vals = append(vals, int(val.(int64))) - } - } - return vals, err -} - -// parseInt64s transforms strings to int64s. -func (k *Key) parseInt64s(strs []string, addInvalid, returnOnInvalid bool) ([]int64, error) { - vals := make([]int64, 0, len(strs)) - parser := func(str string) (interface{}, error) { - val, err := strconv.ParseInt(str, 0, 64) - return val, err - } - - rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser) - if err == nil { - for _, val := range rawVals { - vals = append(vals, val.(int64)) - } - } - return vals, err -} - -// parseUints transforms strings to uints. -func (k *Key) parseUints(strs []string, addInvalid, returnOnInvalid bool) ([]uint, error) { - vals := make([]uint, 0, len(strs)) - parser := func(str string) (interface{}, error) { - val, err := strconv.ParseUint(str, 0, 64) - return val, err - } - - rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser) - if err == nil { - for _, val := range rawVals { - vals = append(vals, uint(val.(uint64))) - } - } - return vals, err -} - -// parseUint64s transforms strings to uint64s. -func (k *Key) parseUint64s(strs []string, addInvalid, returnOnInvalid bool) ([]uint64, error) { - vals := make([]uint64, 0, len(strs)) - parser := func(str string) (interface{}, error) { - val, err := strconv.ParseUint(str, 0, 64) - return val, err - } - rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser) - if err == nil { - for _, val := range rawVals { - vals = append(vals, val.(uint64)) - } - } - return vals, err -} - -type Parser func(str string) (interface{}, error) - -// parseTimesFormat transforms strings to times in given format. -func (k *Key) parseTimesFormat(format string, strs []string, addInvalid, returnOnInvalid bool) ([]time.Time, error) { - vals := make([]time.Time, 0, len(strs)) - parser := func(str string) (interface{}, error) { - val, err := time.Parse(format, str) - return val, err - } - rawVals, err := k.doParse(strs, addInvalid, returnOnInvalid, parser) - if err == nil { - for _, val := range rawVals { - vals = append(vals, val.(time.Time)) - } - } - return vals, err -} - -// doParse transforms strings to different types -func (k *Key) doParse(strs []string, addInvalid, returnOnInvalid bool, parser Parser) ([]interface{}, error) { - vals := make([]interface{}, 0, len(strs)) - for _, str := range strs { - val, err := parser(str) - if err != nil && returnOnInvalid { - return nil, err - } - if err == nil || addInvalid { - vals = append(vals, val) - } - } - return vals, nil -} - -// SetValue changes key value. -func (k *Key) SetValue(v string) { - if k.s.f.BlockMode { - k.s.f.lock.Lock() - defer k.s.f.lock.Unlock() - } - - k.value = v - k.s.keysHash[k.name] = v -} diff --git a/vendor/gopkg.in/ini.v1/parser.go b/vendor/gopkg.in/ini.v1/parser.go deleted file mode 100644 index 44fc526c2c..0000000000 --- a/vendor/gopkg.in/ini.v1/parser.go +++ /dev/null @@ -1,520 +0,0 @@ -// Copyright 2015 Unknwon -// -// 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 ini - -import ( - "bufio" - "bytes" - "fmt" - "io" - "regexp" - "strconv" - "strings" - "unicode" -) - -const minReaderBufferSize = 4096 - -var pythonMultiline = regexp.MustCompile(`^([\t\f ]+)(.*)`) - -type parserOptions struct { - IgnoreContinuation bool - IgnoreInlineComment bool - AllowPythonMultilineValues bool - SpaceBeforeInlineComment bool - UnescapeValueDoubleQuotes bool - UnescapeValueCommentSymbols bool - PreserveSurroundedQuote bool - DebugFunc DebugFunc - ReaderBufferSize int -} - -type parser struct { - buf *bufio.Reader - options parserOptions - - isEOF bool - count int - comment *bytes.Buffer -} - -func (p *parser) debug(format string, args ...interface{}) { - if p.options.DebugFunc != nil { - p.options.DebugFunc(fmt.Sprintf(format, args...)) - } -} - -func newParser(r io.Reader, opts parserOptions) *parser { - size := opts.ReaderBufferSize - if size < minReaderBufferSize { - size = minReaderBufferSize - } - - return &parser{ - buf: bufio.NewReaderSize(r, size), - options: opts, - count: 1, - comment: &bytes.Buffer{}, - } -} - -// BOM handles header of UTF-8, UTF-16 LE and UTF-16 BE's BOM format. -// http://en.wikipedia.org/wiki/Byte_order_mark#Representations_of_byte_order_marks_by_encoding -func (p *parser) BOM() error { - mask, err := p.buf.Peek(2) - if err != nil && err != io.EOF { - return err - } else if len(mask) < 2 { - return nil - } - - switch { - case mask[0] == 254 && mask[1] == 255: - fallthrough - case mask[0] == 255 && mask[1] == 254: - _, err = p.buf.Read(mask) - if err != nil { - return err - } - case mask[0] == 239 && mask[1] == 187: - mask, err := p.buf.Peek(3) - if err != nil && err != io.EOF { - return err - } else if len(mask) < 3 { - return nil - } - if mask[2] == 191 { - _, err = p.buf.Read(mask) - if err != nil { - return err - } - } - } - return nil -} - -func (p *parser) readUntil(delim byte) ([]byte, error) { - data, err := p.buf.ReadBytes(delim) - if err != nil { - if err == io.EOF { - p.isEOF = true - } else { - return nil, err - } - } - return data, nil -} - -func cleanComment(in []byte) ([]byte, bool) { - i := bytes.IndexAny(in, "#;") - if i == -1 { - return nil, false - } - return in[i:], true -} - -func readKeyName(delimiters string, in []byte) (string, int, error) { - line := string(in) - - // Check if key name surrounded by quotes. - var keyQuote string - if line[0] == '"' { - if len(line) > 6 && line[0:3] == `"""` { - keyQuote = `"""` - } else { - keyQuote = `"` - } - } else if line[0] == '`' { - keyQuote = "`" - } - - // Get out key name - var endIdx int - if len(keyQuote) > 0 { - startIdx := len(keyQuote) - // FIXME: fail case -> """"""name"""=value - pos := strings.Index(line[startIdx:], keyQuote) - if pos == -1 { - return "", -1, fmt.Errorf("missing closing key quote: %s", line) - } - pos += startIdx - - // Find key-value delimiter - i := strings.IndexAny(line[pos+startIdx:], delimiters) - if i < 0 { - return "", -1, ErrDelimiterNotFound{line} - } - endIdx = pos + i - return strings.TrimSpace(line[startIdx:pos]), endIdx + startIdx + 1, nil - } - - endIdx = strings.IndexAny(line, delimiters) - if endIdx < 0 { - return "", -1, ErrDelimiterNotFound{line} - } - if endIdx == 0 { - return "", -1, ErrEmptyKeyName{line} - } - - return strings.TrimSpace(line[0:endIdx]), endIdx + 1, nil -} - -func (p *parser) readMultilines(line, val, valQuote string) (string, error) { - for { - data, err := p.readUntil('\n') - if err != nil { - return "", err - } - next := string(data) - - pos := strings.LastIndex(next, valQuote) - if pos > -1 { - val += next[:pos] - - comment, has := cleanComment([]byte(next[pos:])) - if has { - p.comment.Write(bytes.TrimSpace(comment)) - } - break - } - val += next - if p.isEOF { - return "", fmt.Errorf("missing closing key quote from %q to %q", line, next) - } - } - return val, nil -} - -func (p *parser) readContinuationLines(val string) (string, error) { - for { - data, err := p.readUntil('\n') - if err != nil { - return "", err - } - next := strings.TrimSpace(string(data)) - - if len(next) == 0 { - break - } - val += next - if val[len(val)-1] != '\\' { - break - } - val = val[:len(val)-1] - } - return val, nil -} - -// hasSurroundedQuote check if and only if the first and last characters -// are quotes \" or \'. -// It returns false if any other parts also contain same kind of quotes. -func hasSurroundedQuote(in string, quote byte) bool { - return len(in) >= 2 && in[0] == quote && in[len(in)-1] == quote && - strings.IndexByte(in[1:], quote) == len(in)-2 -} - -func (p *parser) readValue(in []byte, bufferSize int) (string, error) { - - line := strings.TrimLeftFunc(string(in), unicode.IsSpace) - if len(line) == 0 { - if p.options.AllowPythonMultilineValues && len(in) > 0 && in[len(in)-1] == '\n' { - return p.readPythonMultilines(line, bufferSize) - } - return "", nil - } - - var valQuote string - if len(line) > 3 && line[0:3] == `"""` { - valQuote = `"""` - } else if line[0] == '`' { - valQuote = "`" - } else if p.options.UnescapeValueDoubleQuotes && line[0] == '"' { - valQuote = `"` - } - - if len(valQuote) > 0 { - startIdx := len(valQuote) - pos := strings.LastIndex(line[startIdx:], valQuote) - // Check for multi-line value - if pos == -1 { - return p.readMultilines(line, line[startIdx:], valQuote) - } - - if p.options.UnescapeValueDoubleQuotes && valQuote == `"` { - return strings.Replace(line[startIdx:pos+startIdx], `\"`, `"`, -1), nil - } - return line[startIdx : pos+startIdx], nil - } - - lastChar := line[len(line)-1] - // Won't be able to reach here if value only contains whitespace - line = strings.TrimSpace(line) - trimmedLastChar := line[len(line)-1] - - // Check continuation lines when desired - if !p.options.IgnoreContinuation && trimmedLastChar == '\\' { - return p.readContinuationLines(line[:len(line)-1]) - } - - // Check if ignore inline comment - if !p.options.IgnoreInlineComment { - var i int - if p.options.SpaceBeforeInlineComment { - i = strings.Index(line, " #") - if i == -1 { - i = strings.Index(line, " ;") - } - - } else { - i = strings.IndexAny(line, "#;") - } - - if i > -1 { - p.comment.WriteString(line[i:]) - line = strings.TrimSpace(line[:i]) - } - - } - - // Trim single and double quotes - if (hasSurroundedQuote(line, '\'') || - hasSurroundedQuote(line, '"')) && !p.options.PreserveSurroundedQuote { - line = line[1 : len(line)-1] - } else if len(valQuote) == 0 && p.options.UnescapeValueCommentSymbols { - line = strings.ReplaceAll(line, `\;`, ";") - line = strings.ReplaceAll(line, `\#`, "#") - } else if p.options.AllowPythonMultilineValues && lastChar == '\n' { - return p.readPythonMultilines(line, bufferSize) - } - - return line, nil -} - -func (p *parser) readPythonMultilines(line string, bufferSize int) (string, error) { - parserBufferPeekResult, _ := p.buf.Peek(bufferSize) - peekBuffer := bytes.NewBuffer(parserBufferPeekResult) - - for { - peekData, peekErr := peekBuffer.ReadBytes('\n') - if peekErr != nil && peekErr != io.EOF { - p.debug("readPythonMultilines: failed to peek with error: %v", peekErr) - return "", peekErr - } - - p.debug("readPythonMultilines: parsing %q", string(peekData)) - - peekMatches := pythonMultiline.FindStringSubmatch(string(peekData)) - p.debug("readPythonMultilines: matched %d parts", len(peekMatches)) - for n, v := range peekMatches { - p.debug(" %d: %q", n, v) - } - - // Return if not a Python multiline value. - if len(peekMatches) != 3 { - p.debug("readPythonMultilines: end of value, got: %q", line) - return line, nil - } - - // Advance the parser reader (buffer) in-sync with the peek buffer. - _, err := p.buf.Discard(len(peekData)) - if err != nil { - p.debug("readPythonMultilines: failed to skip to the end, returning error") - return "", err - } - - line += "\n" + peekMatches[0] - } -} - -// parse parses data through an io.Reader. -func (f *File) parse(reader io.Reader) (err error) { - p := newParser(reader, parserOptions{ - IgnoreContinuation: f.options.IgnoreContinuation, - IgnoreInlineComment: f.options.IgnoreInlineComment, - AllowPythonMultilineValues: f.options.AllowPythonMultilineValues, - SpaceBeforeInlineComment: f.options.SpaceBeforeInlineComment, - UnescapeValueDoubleQuotes: f.options.UnescapeValueDoubleQuotes, - UnescapeValueCommentSymbols: f.options.UnescapeValueCommentSymbols, - PreserveSurroundedQuote: f.options.PreserveSurroundedQuote, - DebugFunc: f.options.DebugFunc, - ReaderBufferSize: f.options.ReaderBufferSize, - }) - if err = p.BOM(); err != nil { - return fmt.Errorf("BOM: %v", err) - } - - // Ignore error because default section name is never empty string. - name := DefaultSection - if f.options.Insensitive || f.options.InsensitiveSections { - name = strings.ToLower(DefaultSection) - } - section, _ := f.NewSection(name) - - // This "last" is not strictly equivalent to "previous one" if current key is not the first nested key - var isLastValueEmpty bool - var lastRegularKey *Key - - var line []byte - var inUnparseableSection bool - - // NOTE: Iterate and increase `currentPeekSize` until - // the size of the parser buffer is found. - // TODO(unknwon): When Golang 1.10 is the lowest version supported, replace with `parserBufferSize := p.buf.Size()`. - parserBufferSize := 0 - // NOTE: Peek 4kb at a time. - currentPeekSize := minReaderBufferSize - - if f.options.AllowPythonMultilineValues { - for { - peekBytes, _ := p.buf.Peek(currentPeekSize) - peekBytesLength := len(peekBytes) - - if parserBufferSize >= peekBytesLength { - break - } - - currentPeekSize *= 2 - parserBufferSize = peekBytesLength - } - } - - for !p.isEOF { - line, err = p.readUntil('\n') - if err != nil { - return err - } - - if f.options.AllowNestedValues && - isLastValueEmpty && len(line) > 0 { - if line[0] == ' ' || line[0] == '\t' { - err = lastRegularKey.addNestedValue(string(bytes.TrimSpace(line))) - if err != nil { - return err - } - continue - } - } - - line = bytes.TrimLeftFunc(line, unicode.IsSpace) - if len(line) == 0 { - continue - } - - // Comments - if line[0] == '#' || line[0] == ';' { - // Note: we do not care ending line break, - // it is needed for adding second line, - // so just clean it once at the end when set to value. - p.comment.Write(line) - continue - } - - // Section - if line[0] == '[' { - // Read to the next ']' (TODO: support quoted strings) - closeIdx := bytes.LastIndexByte(line, ']') - if closeIdx == -1 { - return fmt.Errorf("unclosed section: %s", line) - } - - name := string(line[1:closeIdx]) - section, err = f.NewSection(name) - if err != nil { - return err - } - - comment, has := cleanComment(line[closeIdx+1:]) - if has { - p.comment.Write(comment) - } - - section.Comment = strings.TrimSpace(p.comment.String()) - - // Reset auto-counter and comments - p.comment.Reset() - p.count = 1 - // Nested values can't span sections - isLastValueEmpty = false - - inUnparseableSection = false - for i := range f.options.UnparseableSections { - if f.options.UnparseableSections[i] == name || - ((f.options.Insensitive || f.options.InsensitiveSections) && strings.EqualFold(f.options.UnparseableSections[i], name)) { - inUnparseableSection = true - continue - } - } - continue - } - - if inUnparseableSection { - section.isRawSection = true - section.rawBody += string(line) - continue - } - - kname, offset, err := readKeyName(f.options.KeyValueDelimiters, line) - if err != nil { - switch { - // Treat as boolean key when desired, and whole line is key name. - case IsErrDelimiterNotFound(err): - switch { - case f.options.AllowBooleanKeys: - kname, err := p.readValue(line, parserBufferSize) - if err != nil { - return err - } - key, err := section.NewBooleanKey(kname) - if err != nil { - return err - } - key.Comment = strings.TrimSpace(p.comment.String()) - p.comment.Reset() - continue - - case f.options.SkipUnrecognizableLines: - continue - } - case IsErrEmptyKeyName(err) && f.options.SkipUnrecognizableLines: - continue - } - return err - } - - // Auto increment. - isAutoIncr := false - if kname == "-" { - isAutoIncr = true - kname = "#" + strconv.Itoa(p.count) - p.count++ - } - - value, err := p.readValue(line[offset:], parserBufferSize) - if err != nil { - return err - } - isLastValueEmpty = len(value) == 0 - - key, err := section.NewKey(kname, value) - if err != nil { - return err - } - key.isAutoIncrement = isAutoIncr - key.Comment = strings.TrimSpace(p.comment.String()) - p.comment.Reset() - lastRegularKey = key - } - return nil -} diff --git a/vendor/gopkg.in/ini.v1/section.go b/vendor/gopkg.in/ini.v1/section.go deleted file mode 100644 index a3615d820b..0000000000 --- a/vendor/gopkg.in/ini.v1/section.go +++ /dev/null @@ -1,256 +0,0 @@ -// Copyright 2014 Unknwon -// -// 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 ini - -import ( - "errors" - "fmt" - "strings" -) - -// Section represents a config section. -type Section struct { - f *File - Comment string - name string - keys map[string]*Key - keyList []string - keysHash map[string]string - - isRawSection bool - rawBody string -} - -func newSection(f *File, name string) *Section { - return &Section{ - f: f, - name: name, - keys: make(map[string]*Key), - keyList: make([]string, 0, 10), - keysHash: make(map[string]string), - } -} - -// Name returns name of Section. -func (s *Section) Name() string { - return s.name -} - -// Body returns rawBody of Section if the section was marked as unparseable. -// It still follows the other rules of the INI format surrounding leading/trailing whitespace. -func (s *Section) Body() string { - return strings.TrimSpace(s.rawBody) -} - -// SetBody updates body content only if section is raw. -func (s *Section) SetBody(body string) { - if !s.isRawSection { - return - } - s.rawBody = body -} - -// NewKey creates a new key to given section. -func (s *Section) NewKey(name, val string) (*Key, error) { - if len(name) == 0 { - return nil, errors.New("error creating new key: empty key name") - } else if s.f.options.Insensitive || s.f.options.InsensitiveKeys { - name = strings.ToLower(name) - } - - if s.f.BlockMode { - s.f.lock.Lock() - defer s.f.lock.Unlock() - } - - if inSlice(name, s.keyList) { - if s.f.options.AllowShadows { - if err := s.keys[name].addShadow(val); err != nil { - return nil, err - } - } else { - s.keys[name].value = val - s.keysHash[name] = val - } - return s.keys[name], nil - } - - s.keyList = append(s.keyList, name) - s.keys[name] = newKey(s, name, val) - s.keysHash[name] = val - return s.keys[name], nil -} - -// NewBooleanKey creates a new boolean type key to given section. -func (s *Section) NewBooleanKey(name string) (*Key, error) { - key, err := s.NewKey(name, "true") - if err != nil { - return nil, err - } - - key.isBooleanType = true - return key, nil -} - -// GetKey returns key in section by given name. -func (s *Section) GetKey(name string) (*Key, error) { - if s.f.BlockMode { - s.f.lock.RLock() - } - if s.f.options.Insensitive || s.f.options.InsensitiveKeys { - name = strings.ToLower(name) - } - key := s.keys[name] - if s.f.BlockMode { - s.f.lock.RUnlock() - } - - if key == nil { - // Check if it is a child-section. - sname := s.name - for { - if i := strings.LastIndex(sname, s.f.options.ChildSectionDelimiter); i > -1 { - sname = sname[:i] - sec, err := s.f.GetSection(sname) - if err != nil { - continue - } - return sec.GetKey(name) - } - break - } - return nil, fmt.Errorf("error when getting key of section %q: key %q not exists", s.name, name) - } - return key, nil -} - -// HasKey returns true if section contains a key with given name. -func (s *Section) HasKey(name string) bool { - key, _ := s.GetKey(name) - return key != nil -} - -// Deprecated: Use "HasKey" instead. -func (s *Section) Haskey(name string) bool { - return s.HasKey(name) -} - -// HasValue returns true if section contains given raw value. -func (s *Section) HasValue(value string) bool { - if s.f.BlockMode { - s.f.lock.RLock() - defer s.f.lock.RUnlock() - } - - for _, k := range s.keys { - if value == k.value { - return true - } - } - return false -} - -// Key assumes named Key exists in section and returns a zero-value when not. -func (s *Section) Key(name string) *Key { - key, err := s.GetKey(name) - if err != nil { - // It's OK here because the only possible error is empty key name, - // but if it's empty, this piece of code won't be executed. - key, _ = s.NewKey(name, "") - return key - } - return key -} - -// Keys returns list of keys of section. -func (s *Section) Keys() []*Key { - keys := make([]*Key, len(s.keyList)) - for i := range s.keyList { - keys[i] = s.Key(s.keyList[i]) - } - return keys -} - -// ParentKeys returns list of keys of parent section. -func (s *Section) ParentKeys() []*Key { - var parentKeys []*Key - sname := s.name - for { - if i := strings.LastIndex(sname, s.f.options.ChildSectionDelimiter); i > -1 { - sname = sname[:i] - sec, err := s.f.GetSection(sname) - if err != nil { - continue - } - parentKeys = append(parentKeys, sec.Keys()...) - } else { - break - } - - } - return parentKeys -} - -// KeyStrings returns list of key names of section. -func (s *Section) KeyStrings() []string { - list := make([]string, len(s.keyList)) - copy(list, s.keyList) - return list -} - -// KeysHash returns keys hash consisting of names and values. -func (s *Section) KeysHash() map[string]string { - if s.f.BlockMode { - s.f.lock.RLock() - defer s.f.lock.RUnlock() - } - - hash := make(map[string]string, len(s.keysHash)) - for key, value := range s.keysHash { - hash[key] = value - } - return hash -} - -// DeleteKey deletes a key from section. -func (s *Section) DeleteKey(name string) { - if s.f.BlockMode { - s.f.lock.Lock() - defer s.f.lock.Unlock() - } - - for i, k := range s.keyList { - if k == name { - s.keyList = append(s.keyList[:i], s.keyList[i+1:]...) - delete(s.keys, name) - delete(s.keysHash, name) - return - } - } -} - -// ChildSections returns a list of child sections of current section. -// For example, "[parent.child1]" and "[parent.child12]" are child sections -// of section "[parent]". -func (s *Section) ChildSections() []*Section { - prefix := s.name + s.f.options.ChildSectionDelimiter - children := make([]*Section, 0, 3) - for _, name := range s.f.sectionList { - if strings.HasPrefix(name, prefix) { - children = append(children, s.f.sections[name]...) - } - } - return children -} diff --git a/vendor/gopkg.in/ini.v1/struct.go b/vendor/gopkg.in/ini.v1/struct.go deleted file mode 100644 index a486b2fe0f..0000000000 --- a/vendor/gopkg.in/ini.v1/struct.go +++ /dev/null @@ -1,747 +0,0 @@ -// Copyright 2014 Unknwon -// -// 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 ini - -import ( - "bytes" - "errors" - "fmt" - "reflect" - "strings" - "time" - "unicode" -) - -// NameMapper represents a ini tag name mapper. -type NameMapper func(string) string - -// Built-in name getters. -var ( - // SnackCase converts to format SNACK_CASE. - SnackCase NameMapper = func(raw string) string { - newstr := make([]rune, 0, len(raw)) - for i, chr := range raw { - if isUpper := 'A' <= chr && chr <= 'Z'; isUpper { - if i > 0 { - newstr = append(newstr, '_') - } - } - newstr = append(newstr, unicode.ToUpper(chr)) - } - return string(newstr) - } - // TitleUnderscore converts to format title_underscore. - TitleUnderscore NameMapper = func(raw string) string { - newstr := make([]rune, 0, len(raw)) - for i, chr := range raw { - if isUpper := 'A' <= chr && chr <= 'Z'; isUpper { - if i > 0 { - newstr = append(newstr, '_') - } - chr -= 'A' - 'a' - } - newstr = append(newstr, chr) - } - return string(newstr) - } -) - -func (s *Section) parseFieldName(raw, actual string) string { - if len(actual) > 0 { - return actual - } - if s.f.NameMapper != nil { - return s.f.NameMapper(raw) - } - return raw -} - -func parseDelim(actual string) string { - if len(actual) > 0 { - return actual - } - return "," -} - -var reflectTime = reflect.TypeOf(time.Now()).Kind() - -// setSliceWithProperType sets proper values to slice based on its type. -func setSliceWithProperType(key *Key, field reflect.Value, delim string, allowShadow, isStrict bool) error { - var strs []string - if allowShadow { - strs = key.StringsWithShadows(delim) - } else { - strs = key.Strings(delim) - } - - numVals := len(strs) - if numVals == 0 { - return nil - } - - var vals interface{} - var err error - - sliceOf := field.Type().Elem().Kind() - switch sliceOf { - case reflect.String: - vals = strs - case reflect.Int: - vals, err = key.parseInts(strs, true, false) - case reflect.Int64: - vals, err = key.parseInt64s(strs, true, false) - case reflect.Uint: - vals, err = key.parseUints(strs, true, false) - case reflect.Uint64: - vals, err = key.parseUint64s(strs, true, false) - case reflect.Float64: - vals, err = key.parseFloat64s(strs, true, false) - case reflect.Bool: - vals, err = key.parseBools(strs, true, false) - case reflectTime: - vals, err = key.parseTimesFormat(time.RFC3339, strs, true, false) - default: - return fmt.Errorf("unsupported type '[]%s'", sliceOf) - } - if err != nil && isStrict { - return err - } - - slice := reflect.MakeSlice(field.Type(), numVals, numVals) - for i := 0; i < numVals; i++ { - switch sliceOf { - case reflect.String: - slice.Index(i).Set(reflect.ValueOf(vals.([]string)[i])) - case reflect.Int: - slice.Index(i).Set(reflect.ValueOf(vals.([]int)[i])) - case reflect.Int64: - slice.Index(i).Set(reflect.ValueOf(vals.([]int64)[i])) - case reflect.Uint: - slice.Index(i).Set(reflect.ValueOf(vals.([]uint)[i])) - case reflect.Uint64: - slice.Index(i).Set(reflect.ValueOf(vals.([]uint64)[i])) - case reflect.Float64: - slice.Index(i).Set(reflect.ValueOf(vals.([]float64)[i])) - case reflect.Bool: - slice.Index(i).Set(reflect.ValueOf(vals.([]bool)[i])) - case reflectTime: - slice.Index(i).Set(reflect.ValueOf(vals.([]time.Time)[i])) - } - } - field.Set(slice) - return nil -} - -func wrapStrictError(err error, isStrict bool) error { - if isStrict { - return err - } - return nil -} - -// setWithProperType sets proper value to field based on its type, -// but it does not return error for failing parsing, -// because we want to use default value that is already assigned to struct. -func setWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string, allowShadow, isStrict bool) error { - vt := t - isPtr := t.Kind() == reflect.Ptr - if isPtr { - vt = t.Elem() - } - switch vt.Kind() { - case reflect.String: - stringVal := key.String() - if isPtr { - field.Set(reflect.ValueOf(&stringVal)) - } else if len(stringVal) > 0 { - field.SetString(key.String()) - } - case reflect.Bool: - boolVal, err := key.Bool() - if err != nil { - return wrapStrictError(err, isStrict) - } - if isPtr { - field.Set(reflect.ValueOf(&boolVal)) - } else { - field.SetBool(boolVal) - } - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - // ParseDuration will not return err for `0`, so check the type name - if vt.Name() == "Duration" { - durationVal, err := key.Duration() - if err != nil { - if intVal, err := key.Int64(); err == nil { - field.SetInt(intVal) - return nil - } - return wrapStrictError(err, isStrict) - } - if isPtr { - field.Set(reflect.ValueOf(&durationVal)) - } else if int64(durationVal) > 0 { - field.Set(reflect.ValueOf(durationVal)) - } - return nil - } - - intVal, err := key.Int64() - if err != nil { - return wrapStrictError(err, isStrict) - } - if isPtr { - pv := reflect.New(t.Elem()) - pv.Elem().SetInt(intVal) - field.Set(pv) - } else { - field.SetInt(intVal) - } - // byte is an alias for uint8, so supporting uint8 breaks support for byte - case reflect.Uint, reflect.Uint16, reflect.Uint32, reflect.Uint64: - durationVal, err := key.Duration() - // Skip zero value - if err == nil && uint64(durationVal) > 0 { - if isPtr { - field.Set(reflect.ValueOf(&durationVal)) - } else { - field.Set(reflect.ValueOf(durationVal)) - } - return nil - } - - uintVal, err := key.Uint64() - if err != nil { - return wrapStrictError(err, isStrict) - } - if isPtr { - pv := reflect.New(t.Elem()) - pv.Elem().SetUint(uintVal) - field.Set(pv) - } else { - field.SetUint(uintVal) - } - - case reflect.Float32, reflect.Float64: - floatVal, err := key.Float64() - if err != nil { - return wrapStrictError(err, isStrict) - } - if isPtr { - pv := reflect.New(t.Elem()) - pv.Elem().SetFloat(floatVal) - field.Set(pv) - } else { - field.SetFloat(floatVal) - } - case reflectTime: - timeVal, err := key.Time() - if err != nil { - return wrapStrictError(err, isStrict) - } - if isPtr { - field.Set(reflect.ValueOf(&timeVal)) - } else { - field.Set(reflect.ValueOf(timeVal)) - } - case reflect.Slice: - return setSliceWithProperType(key, field, delim, allowShadow, isStrict) - default: - return fmt.Errorf("unsupported type %q", t) - } - return nil -} - -func parseTagOptions(tag string) (rawName string, omitEmpty bool, allowShadow bool, allowNonUnique bool, extends bool) { - opts := strings.SplitN(tag, ",", 5) - rawName = opts[0] - for _, opt := range opts[1:] { - omitEmpty = omitEmpty || (opt == "omitempty") - allowShadow = allowShadow || (opt == "allowshadow") - allowNonUnique = allowNonUnique || (opt == "nonunique") - extends = extends || (opt == "extends") - } - return rawName, omitEmpty, allowShadow, allowNonUnique, extends -} - -// mapToField maps the given value to the matching field of the given section. -// The sectionIndex is the index (if non unique sections are enabled) to which the value should be added. -func (s *Section) mapToField(val reflect.Value, isStrict bool, sectionIndex int, sectionName string) error { - if val.Kind() == reflect.Ptr { - val = val.Elem() - } - typ := val.Type() - - for i := 0; i < typ.NumField(); i++ { - field := val.Field(i) - tpField := typ.Field(i) - - tag := tpField.Tag.Get("ini") - if tag == "-" { - continue - } - - rawName, _, allowShadow, allowNonUnique, extends := parseTagOptions(tag) - fieldName := s.parseFieldName(tpField.Name, rawName) - if len(fieldName) == 0 || !field.CanSet() { - continue - } - - isStruct := tpField.Type.Kind() == reflect.Struct - isStructPtr := tpField.Type.Kind() == reflect.Ptr && tpField.Type.Elem().Kind() == reflect.Struct - isAnonymousPtr := tpField.Type.Kind() == reflect.Ptr && tpField.Anonymous - if isAnonymousPtr { - field.Set(reflect.New(tpField.Type.Elem())) - } - - if extends && (isAnonymousPtr || (isStruct && tpField.Anonymous)) { - if isStructPtr && field.IsNil() { - field.Set(reflect.New(tpField.Type.Elem())) - } - fieldSection := s - if rawName != "" { - sectionName = s.name + s.f.options.ChildSectionDelimiter + rawName - if secs, err := s.f.SectionsByName(sectionName); err == nil && sectionIndex < len(secs) { - fieldSection = secs[sectionIndex] - } - } - if err := fieldSection.mapToField(field, isStrict, sectionIndex, sectionName); err != nil { - return fmt.Errorf("map to field %q: %v", fieldName, err) - } - } else if isAnonymousPtr || isStruct || isStructPtr { - if secs, err := s.f.SectionsByName(fieldName); err == nil { - if len(secs) <= sectionIndex { - return fmt.Errorf("there are not enough sections (%d <= %d) for the field %q", len(secs), sectionIndex, fieldName) - } - // Only set the field to non-nil struct value if we have a section for it. - // Otherwise, we end up with a non-nil struct ptr even though there is no data. - if isStructPtr && field.IsNil() { - field.Set(reflect.New(tpField.Type.Elem())) - } - if err = secs[sectionIndex].mapToField(field, isStrict, sectionIndex, fieldName); err != nil { - return fmt.Errorf("map to field %q: %v", fieldName, err) - } - continue - } - } - - // Map non-unique sections - if allowNonUnique && tpField.Type.Kind() == reflect.Slice { - newField, err := s.mapToSlice(fieldName, field, isStrict) - if err != nil { - return fmt.Errorf("map to slice %q: %v", fieldName, err) - } - - field.Set(newField) - continue - } - - if key, err := s.GetKey(fieldName); err == nil { - delim := parseDelim(tpField.Tag.Get("delim")) - if err = setWithProperType(tpField.Type, key, field, delim, allowShadow, isStrict); err != nil { - return fmt.Errorf("set field %q: %v", fieldName, err) - } - } - } - return nil -} - -// mapToSlice maps all sections with the same name and returns the new value. -// The type of the Value must be a slice. -func (s *Section) mapToSlice(secName string, val reflect.Value, isStrict bool) (reflect.Value, error) { - secs, err := s.f.SectionsByName(secName) - if err != nil { - return reflect.Value{}, err - } - - typ := val.Type().Elem() - for i, sec := range secs { - elem := reflect.New(typ) - if err = sec.mapToField(elem, isStrict, i, sec.name); err != nil { - return reflect.Value{}, fmt.Errorf("map to field from section %q: %v", secName, err) - } - - val = reflect.Append(val, elem.Elem()) - } - return val, nil -} - -// mapTo maps a section to object v. -func (s *Section) mapTo(v interface{}, isStrict bool) error { - typ := reflect.TypeOf(v) - val := reflect.ValueOf(v) - if typ.Kind() == reflect.Ptr { - typ = typ.Elem() - val = val.Elem() - } else { - return errors.New("not a pointer to a struct") - } - - if typ.Kind() == reflect.Slice { - newField, err := s.mapToSlice(s.name, val, isStrict) - if err != nil { - return err - } - - val.Set(newField) - return nil - } - - return s.mapToField(val, isStrict, 0, s.name) -} - -// MapTo maps section to given struct. -func (s *Section) MapTo(v interface{}) error { - return s.mapTo(v, false) -} - -// StrictMapTo maps section to given struct in strict mode, -// which returns all possible error including value parsing error. -func (s *Section) StrictMapTo(v interface{}) error { - return s.mapTo(v, true) -} - -// MapTo maps file to given struct. -func (f *File) MapTo(v interface{}) error { - return f.Section("").MapTo(v) -} - -// StrictMapTo maps file to given struct in strict mode, -// which returns all possible error including value parsing error. -func (f *File) StrictMapTo(v interface{}) error { - return f.Section("").StrictMapTo(v) -} - -// MapToWithMapper maps data sources to given struct with name mapper. -func MapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error { - cfg, err := Load(source, others...) - if err != nil { - return err - } - cfg.NameMapper = mapper - return cfg.MapTo(v) -} - -// StrictMapToWithMapper maps data sources to given struct with name mapper in strict mode, -// which returns all possible error including value parsing error. -func StrictMapToWithMapper(v interface{}, mapper NameMapper, source interface{}, others ...interface{}) error { - cfg, err := Load(source, others...) - if err != nil { - return err - } - cfg.NameMapper = mapper - return cfg.StrictMapTo(v) -} - -// MapTo maps data sources to given struct. -func MapTo(v, source interface{}, others ...interface{}) error { - return MapToWithMapper(v, nil, source, others...) -} - -// StrictMapTo maps data sources to given struct in strict mode, -// which returns all possible error including value parsing error. -func StrictMapTo(v, source interface{}, others ...interface{}) error { - return StrictMapToWithMapper(v, nil, source, others...) -} - -// reflectSliceWithProperType does the opposite thing as setSliceWithProperType. -func reflectSliceWithProperType(key *Key, field reflect.Value, delim string, allowShadow bool) error { - slice := field.Slice(0, field.Len()) - if field.Len() == 0 { - return nil - } - sliceOf := field.Type().Elem().Kind() - - if allowShadow { - var keyWithShadows *Key - for i := 0; i < field.Len(); i++ { - var val string - switch sliceOf { - case reflect.String: - val = slice.Index(i).String() - case reflect.Int, reflect.Int64: - val = fmt.Sprint(slice.Index(i).Int()) - case reflect.Uint, reflect.Uint64: - val = fmt.Sprint(slice.Index(i).Uint()) - case reflect.Float64: - val = fmt.Sprint(slice.Index(i).Float()) - case reflect.Bool: - val = fmt.Sprint(slice.Index(i).Bool()) - case reflectTime: - val = slice.Index(i).Interface().(time.Time).Format(time.RFC3339) - default: - return fmt.Errorf("unsupported type '[]%s'", sliceOf) - } - - if i == 0 { - keyWithShadows = newKey(key.s, key.name, val) - } else { - _ = keyWithShadows.AddShadow(val) - } - } - *key = *keyWithShadows - return nil - } - - var buf bytes.Buffer - for i := 0; i < field.Len(); i++ { - switch sliceOf { - case reflect.String: - buf.WriteString(slice.Index(i).String()) - case reflect.Int, reflect.Int64: - buf.WriteString(fmt.Sprint(slice.Index(i).Int())) - case reflect.Uint, reflect.Uint64: - buf.WriteString(fmt.Sprint(slice.Index(i).Uint())) - case reflect.Float64: - buf.WriteString(fmt.Sprint(slice.Index(i).Float())) - case reflect.Bool: - buf.WriteString(fmt.Sprint(slice.Index(i).Bool())) - case reflectTime: - buf.WriteString(slice.Index(i).Interface().(time.Time).Format(time.RFC3339)) - default: - return fmt.Errorf("unsupported type '[]%s'", sliceOf) - } - buf.WriteString(delim) - } - key.SetValue(buf.String()[:buf.Len()-len(delim)]) - return nil -} - -// reflectWithProperType does the opposite thing as setWithProperType. -func reflectWithProperType(t reflect.Type, key *Key, field reflect.Value, delim string, allowShadow bool) error { - switch t.Kind() { - case reflect.String: - key.SetValue(field.String()) - case reflect.Bool: - key.SetValue(fmt.Sprint(field.Bool())) - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - key.SetValue(fmt.Sprint(field.Int())) - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: - key.SetValue(fmt.Sprint(field.Uint())) - case reflect.Float32, reflect.Float64: - key.SetValue(fmt.Sprint(field.Float())) - case reflectTime: - key.SetValue(fmt.Sprint(field.Interface().(time.Time).Format(time.RFC3339))) - case reflect.Slice: - return reflectSliceWithProperType(key, field, delim, allowShadow) - case reflect.Ptr: - if !field.IsNil() { - return reflectWithProperType(t.Elem(), key, field.Elem(), delim, allowShadow) - } - default: - return fmt.Errorf("unsupported type %q", t) - } - return nil -} - -// CR: copied from encoding/json/encode.go with modifications of time.Time support. -// TODO: add more test coverage. -func isEmptyValue(v reflect.Value) bool { - switch v.Kind() { - case reflect.Array, reflect.Map, reflect.Slice, reflect.String: - return v.Len() == 0 - case reflect.Bool: - return !v.Bool() - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return v.Int() == 0 - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return v.Uint() == 0 - case reflect.Float32, reflect.Float64: - return v.Float() == 0 - case reflect.Interface, reflect.Ptr: - return v.IsNil() - case reflectTime: - t, ok := v.Interface().(time.Time) - return ok && t.IsZero() - } - return false -} - -// StructReflector is the interface implemented by struct types that can extract themselves into INI objects. -type StructReflector interface { - ReflectINIStruct(*File) error -} - -func (s *Section) reflectFrom(val reflect.Value) error { - if val.Kind() == reflect.Ptr { - val = val.Elem() - } - typ := val.Type() - - for i := 0; i < typ.NumField(); i++ { - if !val.Field(i).CanInterface() { - continue - } - - field := val.Field(i) - tpField := typ.Field(i) - - tag := tpField.Tag.Get("ini") - if tag == "-" { - continue - } - - rawName, omitEmpty, allowShadow, allowNonUnique, extends := parseTagOptions(tag) - if omitEmpty && isEmptyValue(field) { - continue - } - - if r, ok := field.Interface().(StructReflector); ok { - return r.ReflectINIStruct(s.f) - } - - fieldName := s.parseFieldName(tpField.Name, rawName) - if len(fieldName) == 0 || !field.CanSet() { - continue - } - - if extends && tpField.Anonymous && (tpField.Type.Kind() == reflect.Ptr || tpField.Type.Kind() == reflect.Struct) { - if err := s.reflectFrom(field); err != nil { - return fmt.Errorf("reflect from field %q: %v", fieldName, err) - } - continue - } - - if (tpField.Type.Kind() == reflect.Ptr && tpField.Type.Elem().Kind() == reflect.Struct) || - (tpField.Type.Kind() == reflect.Struct && tpField.Type.Name() != "Time") { - // Note: The only error here is section doesn't exist. - sec, err := s.f.GetSection(fieldName) - if err != nil { - // Note: fieldName can never be empty here, ignore error. - sec, _ = s.f.NewSection(fieldName) - } - - // Add comment from comment tag - if len(sec.Comment) == 0 { - sec.Comment = tpField.Tag.Get("comment") - } - - if err = sec.reflectFrom(field); err != nil { - return fmt.Errorf("reflect from field %q: %v", fieldName, err) - } - continue - } - - if allowNonUnique && tpField.Type.Kind() == reflect.Slice { - slice := field.Slice(0, field.Len()) - if field.Len() == 0 { - return nil - } - sliceOf := field.Type().Elem().Kind() - - for i := 0; i < field.Len(); i++ { - if sliceOf != reflect.Struct && sliceOf != reflect.Ptr { - return fmt.Errorf("field %q is not a slice of pointer or struct", fieldName) - } - - sec, err := s.f.NewSection(fieldName) - if err != nil { - return err - } - - // Add comment from comment tag - if len(sec.Comment) == 0 { - sec.Comment = tpField.Tag.Get("comment") - } - - if err := sec.reflectFrom(slice.Index(i)); err != nil { - return fmt.Errorf("reflect from field %q: %v", fieldName, err) - } - } - continue - } - - // Note: Same reason as section. - key, err := s.GetKey(fieldName) - if err != nil { - key, _ = s.NewKey(fieldName, "") - } - - // Add comment from comment tag - if len(key.Comment) == 0 { - key.Comment = tpField.Tag.Get("comment") - } - - delim := parseDelim(tpField.Tag.Get("delim")) - if err = reflectWithProperType(tpField.Type, key, field, delim, allowShadow); err != nil { - return fmt.Errorf("reflect field %q: %v", fieldName, err) - } - - } - return nil -} - -// ReflectFrom reflects section from given struct. It overwrites existing ones. -func (s *Section) ReflectFrom(v interface{}) error { - typ := reflect.TypeOf(v) - val := reflect.ValueOf(v) - - if s.name != DefaultSection && s.f.options.AllowNonUniqueSections && - (typ.Kind() == reflect.Slice || typ.Kind() == reflect.Ptr) { - // Clear sections to make sure none exists before adding the new ones - s.f.DeleteSection(s.name) - - if typ.Kind() == reflect.Ptr { - sec, err := s.f.NewSection(s.name) - if err != nil { - return err - } - return sec.reflectFrom(val.Elem()) - } - - slice := val.Slice(0, val.Len()) - sliceOf := val.Type().Elem().Kind() - if sliceOf != reflect.Ptr { - return fmt.Errorf("not a slice of pointers") - } - - for i := 0; i < slice.Len(); i++ { - sec, err := s.f.NewSection(s.name) - if err != nil { - return err - } - - err = sec.reflectFrom(slice.Index(i)) - if err != nil { - return fmt.Errorf("reflect from %dth field: %v", i, err) - } - } - - return nil - } - - if typ.Kind() == reflect.Ptr { - val = val.Elem() - } else { - return errors.New("not a pointer to a struct") - } - - return s.reflectFrom(val) -} - -// ReflectFrom reflects file from given struct. -func (f *File) ReflectFrom(v interface{}) error { - return f.Section("").ReflectFrom(v) -} - -// ReflectFromWithMapper reflects data sources from given struct with name mapper. -func ReflectFromWithMapper(cfg *File, v interface{}, mapper NameMapper) error { - cfg.NameMapper = mapper - return cfg.ReflectFrom(v) -} - -// ReflectFrom reflects data sources from given struct. -func ReflectFrom(cfg *File, v interface{}) error { - return ReflectFromWithMapper(cfg, v, nil) -} diff --git a/vendor/helm.sh/helm/v3/pkg/chartutil/coalesce.go b/vendor/helm.sh/helm/v3/pkg/chartutil/coalesce.go index f0272fd6ab..40bce2a68e 100644 --- a/vendor/helm.sh/helm/v3/pkg/chartutil/coalesce.go +++ b/vendor/helm.sh/helm/v3/pkg/chartutil/coalesce.go @@ -237,6 +237,9 @@ func coalesceValues(printf printFn, c *chart.Chart, v map[string]interface{}, pr printf("warning: skipped value for %s.%s: Not a table.", subPrefix, key) } } else { + // If the key is a child chart, coalesce tables with Merge set to true + merge := childChartMergeTrue(c, key, merge) + // Because v has higher precedence than nv, dest values override src // values. coalesceTablesFullKey(printf, dest, src, concatPrefix(subPrefix, key), merge) @@ -249,6 +252,15 @@ func coalesceValues(printf printFn, c *chart.Chart, v map[string]interface{}, pr } } +func childChartMergeTrue(chrt *chart.Chart, key string, merge bool) bool { + for _, subchart := range chrt.Dependencies() { + if subchart.Name() == key { + return true + } + } + return merge +} + // CoalesceTables merges a source map into a destination map. // // dest is considered authoritative. diff --git a/vendor/helm.sh/helm/v3/pkg/kube/resource.go b/vendor/helm.sh/helm/v3/pkg/kube/resource.go index d441db8a70..db8e9178e7 100644 --- a/vendor/helm.sh/helm/v3/pkg/kube/resource.go +++ b/vendor/helm.sh/helm/v3/pkg/kube/resource.go @@ -81,5 +81,5 @@ func (r ResourceList) Intersect(rs ResourceList) ResourceList { // isMatchingInfo returns true if infos match on Name and GroupVersionKind. func isMatchingInfo(a, b *resource.Info) bool { - return a.Name == b.Name && a.Namespace == b.Namespace && a.Mapping.GroupVersionKind.Kind == b.Mapping.GroupVersionKind.Kind + return a.Name == b.Name && a.Namespace == b.Namespace && a.Mapping.GroupVersionKind.Kind == b.Mapping.GroupVersionKind.Kind && a.Mapping.GroupVersionKind.Group == b.Mapping.GroupVersionKind.Group } diff --git a/vendor/k8s.io/api/resource/v1alpha3/types.go b/vendor/k8s.io/api/resource/v1alpha3/types.go index fb4d7041db..49d7c86de3 100644 --- a/vendor/k8s.io/api/resource/v1alpha3/types.go +++ b/vendor/k8s.io/api/resource/v1alpha3/types.go @@ -145,6 +145,10 @@ type ResourceSliceSpec struct { Devices []Device `json:"devices" protobuf:"bytes,6,name=devices"` } +// DriverNameMaxLength is the maximum valid length of a driver name in the +// ResourceSliceSpec and other places. It's the same as for CSI driver names. +const DriverNameMaxLength = 63 + // ResourcePool describes the pool that ResourceSlices belong to. type ResourcePool struct { // Name is used to identify the pool. For node-local devices, this diff --git a/vendor/k8s.io/api/resource/v1beta1/types.go b/vendor/k8s.io/api/resource/v1beta1/types.go index ca79c5a664..fbdc35ca86 100644 --- a/vendor/k8s.io/api/resource/v1beta1/types.go +++ b/vendor/k8s.io/api/resource/v1beta1/types.go @@ -144,6 +144,10 @@ type ResourceSliceSpec struct { Devices []Device `json:"devices" protobuf:"bytes,6,name=devices"` } +// DriverNameMaxLength is the maximum valid length of a driver name in the +// ResourceSliceSpec and other places. It's the same as for CSI driver names. +const DriverNameMaxLength = 63 + // ResourcePool describes the pool that ResourceSlices belong to. type ResourcePool struct { // Name is used to identify the pool. For node-local devices, this diff --git a/vendor/modules.txt b/vendor/modules.txt index b2db8f69d5..1d098bb5eb 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -52,12 +52,11 @@ github.com/chai2010/gettext-go github.com/chai2010/gettext-go/mo github.com/chai2010/gettext-go/plural github.com/chai2010/gettext-go/po -# github.com/cilium/charts v0.0.0-20250204154402-8a35f8210901 +# github.com/cilium/charts v0.0.0-20250315135936-15c20a953df6 ## explicit; go 1.17 github.com/cilium/charts -# github.com/cilium/cilium v1.18.0-pre.0.0.20250307211928-06901cc99eca +# github.com/cilium/cilium v1.18.0-pre.0.0.20250321220641-9d59c1208ae1 ## explicit; go 1.24.0 -github.com/cilium/cilium github.com/cilium/cilium/api/v1/client github.com/cilium/cilium/api/v1/client/bgp github.com/cilium/cilium/api/v1/client/daemon @@ -104,8 +103,10 @@ github.com/cilium/cilium/cilium-cli/logging github.com/cilium/cilium/cilium-cli/multicast github.com/cilium/cilium/cilium-cli/status github.com/cilium/cilium/cilium-cli/sysdump +github.com/cilium/cilium/cilium-cli/utils/codeowners github.com/cilium/cilium/cilium-cli/utils/features github.com/cilium/cilium/cilium-cli/utils/lock +github.com/cilium/cilium/cilium-cli/utils/log github.com/cilium/cilium/cilium-cli/utils/runner github.com/cilium/cilium/cilium-cli/utils/wait github.com/cilium/cilium/daemon/k8s @@ -151,6 +152,7 @@ github.com/cilium/cilium/pkg/debug github.com/cilium/cilium/pkg/defaults github.com/cilium/cilium/pkg/endpoint/id github.com/cilium/cilium/pkg/endpoint/regeneration +github.com/cilium/cilium/pkg/envoy/policy github.com/cilium/cilium/pkg/envoy/resource github.com/cilium/cilium/pkg/fqdn/dns github.com/cilium/cilium/pkg/fqdn/matchpattern @@ -259,7 +261,7 @@ github.com/cilium/cilium/pkg/u8proto github.com/cilium/cilium/pkg/version github.com/cilium/cilium/pkg/versioncheck github.com/cilium/cilium/pkg/wireguard/types -# github.com/cilium/ebpf v0.17.1 +# github.com/cilium/ebpf v0.17.3 ## explicit; go 1.22 github.com/cilium/ebpf github.com/cilium/ebpf/asm @@ -275,16 +277,17 @@ github.com/cilium/ebpf/internal/testutils/fdtrace github.com/cilium/ebpf/internal/tracefs github.com/cilium/ebpf/internal/unix github.com/cilium/ebpf/link -# github.com/cilium/hive v0.0.0-20250217113459-914947d44393 +# github.com/cilium/hive v0.0.0-20250311080423-b13b66490d76 ## explicit; go 1.21.3 github.com/cilium/hive github.com/cilium/hive/cell +github.com/cilium/hive/hivetest github.com/cilium/hive/internal github.com/cilium/hive/job github.com/cilium/hive/script github.com/cilium/hive/script/internal/diff -# github.com/cilium/proxy v0.0.0-20250214115704-3e4b99dc5d1f -## explicit; go 1.23 +# github.com/cilium/proxy v0.0.0-20250318065604-173988fc0adb +## explicit; go 1.23.0 github.com/cilium/proxy/go/cilium/api github.com/cilium/proxy/go/envoy/admin/v3 github.com/cilium/proxy/go/envoy/annotations @@ -562,7 +565,7 @@ github.com/cilium/proxy/go/envoy/type/tracing/v3 github.com/cilium/proxy/go/envoy/type/v3 github.com/cilium/proxy/go/envoy/watchdog/v3 github.com/cilium/proxy/pkg/policy/api/kafka -# github.com/cilium/statedb v0.3.6 +# github.com/cilium/statedb v0.3.7 ## explicit; go 1.23 github.com/cilium/statedb github.com/cilium/statedb/index @@ -572,8 +575,8 @@ github.com/cilium/statedb/reconciler # github.com/cilium/stream v0.0.0-20241203114243-53c3e5d79744 ## explicit; go 1.21.0 github.com/cilium/stream -# github.com/cilium/workerpool v1.2.0 -## explicit; go 1.15 +# github.com/cilium/workerpool v1.3.0 +## explicit; go 1.22 github.com/cilium/workerpool # github.com/cloudflare/cfssl v1.6.5 ## explicit; go 1.18 @@ -604,7 +607,7 @@ github.com/cncf/xds/go/xds/annotations/v3 github.com/cncf/xds/go/xds/core/v3 github.com/cncf/xds/go/xds/type/matcher/v3 github.com/cncf/xds/go/xds/type/v3 -# github.com/containerd/containerd v1.7.24 +# github.com/containerd/containerd v1.7.27 ## explicit; go 1.21 github.com/containerd/containerd/archive/compression github.com/containerd/containerd/content @@ -665,14 +668,14 @@ github.com/docker/distribution/registry/client/auth/challenge github.com/docker/distribution/registry/client/transport github.com/docker/distribution/registry/storage/cache github.com/docker/distribution/registry/storage/cache/memory -# github.com/docker/docker v27.5.1+incompatible +# github.com/docker/docker v28.0.1+incompatible ## explicit github.com/docker/docker/api/types/filters github.com/docker/docker/api/types/registry github.com/docker/docker/api/types/versions github.com/docker/docker/errdefs +github.com/docker/docker/internal/lazyregexp github.com/docker/docker/pkg/homedir -github.com/docker/docker/pkg/ioutils github.com/docker/docker/registry # github.com/docker/docker-credential-helpers v0.7.0 ## explicit; go 1.18 @@ -732,7 +735,7 @@ github.com/go-openapi/analysis/internal/flatten/operations github.com/go-openapi/analysis/internal/flatten/replace github.com/go-openapi/analysis/internal/flatten/schutils github.com/go-openapi/analysis/internal/flatten/sortref -# github.com/go-openapi/errors v0.22.0 +# github.com/go-openapi/errors v0.22.1 ## explicit; go 1.20 github.com/go-openapi/errors # github.com/go-openapi/jsonpointer v0.21.0 @@ -762,12 +765,16 @@ github.com/go-openapi/spec # github.com/go-openapi/strfmt v0.23.0 ## explicit; go 1.20 github.com/go-openapi/strfmt -# github.com/go-openapi/swag v0.23.0 +# github.com/go-openapi/swag v0.23.1 ## explicit; go 1.20 github.com/go-openapi/swag # github.com/go-openapi/validate v0.24.0 ## explicit; go 1.20 github.com/go-openapi/validate +# github.com/go-viper/mapstructure/v2 v2.2.1 +## explicit; go 1.18 +github.com/go-viper/mapstructure/v2 +github.com/go-viper/mapstructure/v2/internal/errors # github.com/gobwas/glob v0.2.3 ## explicit github.com/gobwas/glob @@ -814,17 +821,17 @@ github.com/google/gnostic-models/extensions github.com/google/gnostic-models/jsonschema github.com/google/gnostic-models/openapiv2 github.com/google/gnostic-models/openapiv3 -# github.com/google/go-cmp v0.6.0 -## explicit; go 1.13 +# github.com/google/go-cmp v0.7.0 +## explicit; go 1.21 github.com/google/go-cmp/cmp github.com/google/go-cmp/cmp/cmpopts github.com/google/go-cmp/cmp/internal/diff github.com/google/go-cmp/cmp/internal/flags github.com/google/go-cmp/cmp/internal/function github.com/google/go-cmp/cmp/internal/value -# github.com/google/go-github/v68 v68.0.0 -## explicit; go 1.21 -github.com/google/go-github/v68/github +# github.com/google/go-github/v70 v70.0.0 +## explicit; go 1.23.0 +github.com/google/go-github/v70/github # github.com/google/go-querystring v1.1.0 ## explicit; go 1.10 github.com/google/go-querystring/query @@ -868,18 +875,6 @@ github.com/hashicorp/go-hclog # github.com/hashicorp/go-multierror v1.1.1 ## explicit; go 1.13 github.com/hashicorp/go-multierror -# github.com/hashicorp/hcl v1.0.1-vault-5 -## explicit; go 1.15 -github.com/hashicorp/hcl -github.com/hashicorp/hcl/hcl/ast -github.com/hashicorp/hcl/hcl/parser -github.com/hashicorp/hcl/hcl/printer -github.com/hashicorp/hcl/hcl/scanner -github.com/hashicorp/hcl/hcl/strconv -github.com/hashicorp/hcl/hcl/token -github.com/hashicorp/hcl/json/parser -github.com/hashicorp/hcl/json/scanner -github.com/hashicorp/hcl/json/token # github.com/hmarr/codeowners v1.2.1 ## explicit; go 1.18 github.com/hmarr/codeowners @@ -900,8 +895,8 @@ github.com/josharian/intern # github.com/json-iterator/go v1.1.12 ## explicit; go 1.12 github.com/json-iterator/go -# github.com/klauspost/compress v1.17.9 -## explicit; go 1.20 +# github.com/klauspost/compress v1.17.11 +## explicit; go 1.21 github.com/klauspost/compress github.com/klauspost/compress/fse github.com/klauspost/compress/huff0 @@ -926,11 +921,8 @@ github.com/liggitt/tabwriter # github.com/mackerelio/go-osstat v0.2.5 ## explicit; go 1.18 github.com/mackerelio/go-osstat/memory -# github.com/magiconair/properties v1.8.7 -## explicit; go 1.19 -github.com/magiconair/properties -# github.com/mailru/easyjson v0.7.7 -## explicit; go 1.12 +# github.com/mailru/easyjson v0.9.0 +## explicit; go 1.20 github.com/mailru/easyjson/buffer github.com/mailru/easyjson/jlexer github.com/mailru/easyjson/jwriter @@ -996,14 +988,14 @@ github.com/opencontainers/image-spec/specs-go/v1 github.com/opentracing/opentracing-go github.com/opentracing/opentracing-go/ext github.com/opentracing/opentracing-go/log -# github.com/osrg/gobgp/v3 v3.34.0 +# github.com/osrg/gobgp/v3 v3.35.0 ## explicit; go 1.22.7 github.com/osrg/gobgp/v3/pkg/packet/bgp # github.com/pelletier/go-toml v1.9.5 ## explicit; go 1.12 github.com/pelletier/go-toml -# github.com/pelletier/go-toml/v2 v2.2.2 -## explicit; go 1.16 +# github.com/pelletier/go-toml/v2 v2.2.3 +## explicit; go 1.21.0 github.com/pelletier/go-toml/v2 github.com/pelletier/go-toml/v2/internal/characters github.com/pelletier/go-toml/v2/internal/danger @@ -1021,8 +1013,8 @@ github.com/pkg/browser # github.com/pkg/errors v0.9.1 ## explicit github.com/pkg/errors -# github.com/prometheus/client_golang v1.20.5 -## explicit; go 1.20 +# github.com/prometheus/client_golang v1.21.1 +## explicit; go 1.21 github.com/prometheus/client_golang/internal/github.com/golang/gddo/httputil github.com/prometheus/client_golang/internal/github.com/golang/gddo/httputil/header github.com/prometheus/client_golang/prometheus @@ -1032,12 +1024,12 @@ github.com/prometheus/client_golang/prometheus/promhttp # github.com/prometheus/client_model v0.6.1 ## explicit; go 1.19 github.com/prometheus/client_model/go -# github.com/prometheus/common v0.62.0 +# github.com/prometheus/common v0.63.0 ## explicit; go 1.21 github.com/prometheus/common/expfmt github.com/prometheus/common/model -# github.com/prometheus/procfs v0.15.1 -## explicit; go 1.20 +# github.com/prometheus/procfs v0.16.0 +## explicit; go 1.21 github.com/prometheus/procfs github.com/prometheus/procfs/internal/fs github.com/prometheus/procfs/internal/util @@ -1051,12 +1043,9 @@ github.com/rubenv/sql-migrate/sqlparse # github.com/russross/blackfriday/v2 v2.1.0 ## explicit github.com/russross/blackfriday/v2 -# github.com/sagikazarmark/locafero v0.4.0 -## explicit; go 1.20 +# github.com/sagikazarmark/locafero v0.7.0 +## explicit; go 1.21 github.com/sagikazarmark/locafero -# github.com/sagikazarmark/slog-shim v0.1.0 -## explicit; go 1.20 -github.com/sagikazarmark/slog-shim # github.com/sasha-s/go-deadlock v0.3.5 ## explicit github.com/sasha-s/go-deadlock @@ -1073,28 +1062,24 @@ github.com/sourcegraph/conc github.com/sourcegraph/conc/internal/multierror github.com/sourcegraph/conc/iter github.com/sourcegraph/conc/panics -# github.com/spf13/afero v1.12.0 -## explicit; go 1.21 +# github.com/spf13/afero v1.14.0 +## explicit; go 1.23.0 github.com/spf13/afero github.com/spf13/afero/internal/common github.com/spf13/afero/mem # github.com/spf13/cast v1.7.1 ## explicit; go 1.19 github.com/spf13/cast -# github.com/spf13/cobra v1.8.1 +# github.com/spf13/cobra v1.9.1 ## explicit; go 1.15 github.com/spf13/cobra # github.com/spf13/pflag v1.0.6 ## explicit; go 1.12 github.com/spf13/pflag -# github.com/spf13/viper v1.19.0 -## explicit; go 1.20 +# github.com/spf13/viper v1.20.0 +## explicit; go 1.21.0 github.com/spf13/viper -github.com/spf13/viper/internal/encoding github.com/spf13/viper/internal/encoding/dotenv -github.com/spf13/viper/internal/encoding/hcl -github.com/spf13/viper/internal/encoding/ini -github.com/spf13/viper/internal/encoding/javaproperties github.com/spf13/viper/internal/encoding/json github.com/spf13/viper/internal/encoding/toml github.com/spf13/viper/internal/encoding/yaml @@ -1151,22 +1136,22 @@ github.com/zmap/zlint/v3/lints/etsi github.com/zmap/zlint/v3/lints/mozilla github.com/zmap/zlint/v3/lints/rfc github.com/zmap/zlint/v3/util -# go.etcd.io/etcd/api/v3 v3.5.18 -## explicit; go 1.22 +# go.etcd.io/etcd/api/v3 v3.5.19 +## explicit; go 1.23.0 go.etcd.io/etcd/api/v3/authpb go.etcd.io/etcd/api/v3/etcdserverpb go.etcd.io/etcd/api/v3/membershippb go.etcd.io/etcd/api/v3/mvccpb go.etcd.io/etcd/api/v3/v3rpc/rpctypes go.etcd.io/etcd/api/v3/version -# go.etcd.io/etcd/client/pkg/v3 v3.5.18 -## explicit; go 1.22 +# go.etcd.io/etcd/client/pkg/v3 v3.5.19 +## explicit; go 1.23.0 go.etcd.io/etcd/client/pkg/v3/logutil go.etcd.io/etcd/client/pkg/v3/systemd go.etcd.io/etcd/client/pkg/v3/tlsutil go.etcd.io/etcd/client/pkg/v3/types -# go.etcd.io/etcd/client/v3 v3.5.18 -## explicit; go 1.22 +# go.etcd.io/etcd/client/v3 v3.5.19 +## explicit; go 1.23.0 go.etcd.io/etcd/client/v3 go.etcd.io/etcd/client/v3/concurrency go.etcd.io/etcd/client/v3/credentials @@ -1192,7 +1177,7 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/request go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconvutil -# go.opentelemetry.io/otel v1.34.0 +# go.opentelemetry.io/otel v1.35.0 ## explicit; go 1.22.0 go.opentelemetry.io/otel go.opentelemetry.io/otel/attribute @@ -1209,15 +1194,16 @@ go.opentelemetry.io/otel/semconv/v1.17.0/httpconv go.opentelemetry.io/otel/semconv/v1.20.0 go.opentelemetry.io/otel/semconv/v1.21.0 go.opentelemetry.io/otel/semconv/v1.26.0 -# go.opentelemetry.io/otel/metric v1.34.0 +# go.opentelemetry.io/otel/metric v1.35.0 ## explicit; go 1.22.0 go.opentelemetry.io/otel/metric go.opentelemetry.io/otel/metric/embedded go.opentelemetry.io/otel/metric/noop -# go.opentelemetry.io/otel/trace v1.34.0 +# go.opentelemetry.io/otel/trace v1.35.0 ## explicit; go 1.22.0 go.opentelemetry.io/otel/trace go.opentelemetry.io/otel/trace/embedded +go.opentelemetry.io/otel/trace/internal/telemetry go.opentelemetry.io/otel/trace/noop # go.opentelemetry.io/proto/otlp v1.5.0 ## explicit; go 1.22.0 @@ -1247,8 +1233,8 @@ go.uber.org/zap/zapgrpc # go4.org/netipx v0.0.0-20231129151722-fdeea329fbba ## explicit; go 1.18 go4.org/netipx -# golang.org/x/crypto v0.33.0 -## explicit; go 1.20 +# golang.org/x/crypto v0.36.0 +## explicit; go 1.23.0 golang.org/x/crypto/bcrypt golang.org/x/crypto/blowfish golang.org/x/crypto/cast5 @@ -1267,15 +1253,8 @@ golang.org/x/crypto/pbkdf2 golang.org/x/crypto/pkcs12 golang.org/x/crypto/pkcs12/internal/rc2 golang.org/x/crypto/scrypt -# golang.org/x/exp v0.0.0-20240808152545-0cdaa3abc0fa -## explicit; go 1.20 -golang.org/x/exp/constraints -golang.org/x/exp/slices -golang.org/x/exp/slog -golang.org/x/exp/slog/internal -golang.org/x/exp/slog/internal/buffer -# golang.org/x/net v0.35.0 -## explicit; go 1.18 +# golang.org/x/net v0.37.0 +## explicit; go 1.23.0 golang.org/x/net/context/ctxhttp golang.org/x/net/html golang.org/x/net/html/atom @@ -1289,26 +1268,26 @@ golang.org/x/net/internal/timeseries golang.org/x/net/proxy golang.org/x/net/trace golang.org/x/net/websocket -# golang.org/x/oauth2 v0.26.0 -## explicit; go 1.18 +# golang.org/x/oauth2 v0.28.0 +## explicit; go 1.23.0 golang.org/x/oauth2 golang.org/x/oauth2/internal -# golang.org/x/sync v0.11.0 -## explicit; go 1.18 +# golang.org/x/sync v0.12.0 +## explicit; go 1.23.0 golang.org/x/sync/errgroup golang.org/x/sync/semaphore golang.org/x/sync/singleflight -# golang.org/x/sys v0.30.0 -## explicit; go 1.18 +# golang.org/x/sys v0.31.0 +## explicit; go 1.23.0 golang.org/x/sys/execabs golang.org/x/sys/plan9 golang.org/x/sys/unix golang.org/x/sys/windows -# golang.org/x/term v0.29.0 -## explicit; go 1.18 +# golang.org/x/term v0.30.0 +## explicit; go 1.23.0 golang.org/x/term -# golang.org/x/text v0.22.0 -## explicit; go 1.18 +# golang.org/x/text v0.23.0 +## explicit; go 1.23.0 golang.org/x/text/encoding golang.org/x/text/encoding/internal golang.org/x/text/encoding/internal/identifier @@ -1319,27 +1298,28 @@ golang.org/x/text/secure/bidirule golang.org/x/text/transform golang.org/x/text/unicode/bidi golang.org/x/text/unicode/norm -# golang.org/x/time v0.10.0 -## explicit; go 1.18 +# golang.org/x/time v0.11.0 +## explicit; go 1.23.0 golang.org/x/time/rate -# golang.org/x/tools v0.30.0 -## explicit; go 1.22.0 +# golang.org/x/tools v0.31.0 +## explicit; go 1.23.0 golang.org/x/tools/txtar -# google.golang.org/genproto/googleapis/api v0.0.0-20250207221924-e9438ea467c6 -## explicit; go 1.22 +# google.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4 +## explicit; go 1.23.0 google.golang.org/genproto/googleapis/api google.golang.org/genproto/googleapis/api/annotations google.golang.org/genproto/googleapis/api/expr/v1alpha1 -# google.golang.org/genproto/googleapis/rpc v0.0.0-20250207221924-e9438ea467c6 -## explicit; go 1.22 +# google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 +## explicit; go 1.23.0 google.golang.org/genproto/googleapis/rpc/status -# google.golang.org/grpc v1.70.0 -## explicit; go 1.22 +# google.golang.org/grpc v1.71.0 +## explicit; go 1.22.0 google.golang.org/grpc google.golang.org/grpc/attributes google.golang.org/grpc/backoff google.golang.org/grpc/balancer google.golang.org/grpc/balancer/base +google.golang.org/grpc/balancer/endpointsharding google.golang.org/grpc/balancer/grpclb/state google.golang.org/grpc/balancer/pickfirst google.golang.org/grpc/balancer/pickfirst/internal @@ -1371,7 +1351,9 @@ google.golang.org/grpc/internal/grpcutil google.golang.org/grpc/internal/idle google.golang.org/grpc/internal/metadata google.golang.org/grpc/internal/pretty +google.golang.org/grpc/internal/proxyattributes google.golang.org/grpc/internal/resolver +google.golang.org/grpc/internal/resolver/delegatingresolver google.golang.org/grpc/internal/resolver/dns google.golang.org/grpc/internal/resolver/dns/internal google.golang.org/grpc/internal/resolver/passthrough @@ -1443,13 +1425,10 @@ gopkg.in/evanphx/json-patch.v4 # gopkg.in/inf.v0 v0.9.1 ## explicit gopkg.in/inf.v0 -# gopkg.in/ini.v1 v1.67.0 -## explicit -gopkg.in/ini.v1 # gopkg.in/yaml.v3 v3.0.1 ## explicit gopkg.in/yaml.v3 -# helm.sh/helm/v3 v3.17.0 +# helm.sh/helm/v3 v3.17.2 ## explicit; go 1.23.0 helm.sh/helm/v3/internal/fileutil helm.sh/helm/v3/internal/resolver @@ -1491,7 +1470,7 @@ helm.sh/helm/v3/pkg/strvals helm.sh/helm/v3/pkg/time helm.sh/helm/v3/pkg/time/ctime helm.sh/helm/v3/pkg/uploader -# k8s.io/api v0.32.2 +# k8s.io/api v0.32.3 ## explicit; go 1.23.0 k8s.io/api/admission/v1 k8s.io/api/admission/v1beta1 @@ -1552,7 +1531,7 @@ k8s.io/api/storage/v1 k8s.io/api/storage/v1alpha1 k8s.io/api/storage/v1beta1 k8s.io/api/storagemigration/v1alpha1 -# k8s.io/apiextensions-apiserver v0.32.2 +# k8s.io/apiextensions-apiserver v0.32.3 ## explicit; go 1.23.0 k8s.io/apiextensions-apiserver/pkg/apis/apiextensions k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1 @@ -1568,7 +1547,7 @@ k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextension k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1/fake k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1 k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset/typed/apiextensions/v1beta1/fake -# k8s.io/apimachinery v0.32.2 +# k8s.io/apimachinery v0.32.3 ## explicit; go 1.23.0 k8s.io/apimachinery/pkg/api/equality k8s.io/apimachinery/pkg/api/errors @@ -1633,16 +1612,16 @@ k8s.io/apimachinery/pkg/watch k8s.io/apimachinery/third_party/forked/golang/json k8s.io/apimachinery/third_party/forked/golang/netutil k8s.io/apimachinery/third_party/forked/golang/reflect -# k8s.io/apiserver v0.32.2 +# k8s.io/apiserver v0.32.3 ## explicit; go 1.23.0 k8s.io/apiserver/pkg/endpoints/deprecation -# k8s.io/cli-runtime v0.32.2 +# k8s.io/cli-runtime v0.32.3 ## explicit; go 1.23.0 k8s.io/cli-runtime/pkg/genericclioptions k8s.io/cli-runtime/pkg/genericiooptions k8s.io/cli-runtime/pkg/printers k8s.io/cli-runtime/pkg/resource -# k8s.io/client-go v0.32.2 +# k8s.io/client-go v0.32.3 ## explicit; go 1.23.0 k8s.io/client-go/applyconfigurations k8s.io/client-go/applyconfigurations/admissionregistration/v1 @@ -1870,7 +1849,7 @@ k8s.io/client-go/util/keyutil k8s.io/client-go/util/retry k8s.io/client-go/util/watchlist k8s.io/client-go/util/workqueue -# k8s.io/component-base v0.32.2 +# k8s.io/component-base v0.32.3 ## explicit; go 1.23.0 k8s.io/component-base/version # k8s.io/klog/v2 v2.130.1 @@ -1894,7 +1873,7 @@ k8s.io/kube-openapi/pkg/spec3 k8s.io/kube-openapi/pkg/util/proto k8s.io/kube-openapi/pkg/util/proto/validation k8s.io/kube-openapi/pkg/validation/spec -# k8s.io/kubectl v0.32.2 +# k8s.io/kubectl v0.32.3 ## explicit; go 1.23.0 k8s.io/kubectl/pkg/cmd/util k8s.io/kubectl/pkg/scheme @@ -1931,7 +1910,7 @@ oras.land/oras-go/pkg/registry/remote/auth oras.land/oras-go/pkg/registry/remote/internal/errutil oras.land/oras-go/pkg/registry/remote/internal/syncutil oras.land/oras-go/pkg/target -# sigs.k8s.io/controller-runtime v0.20.1 +# sigs.k8s.io/controller-runtime v0.20.3 ## explicit; go 1.23.0 sigs.k8s.io/controller-runtime/pkg/client/apiutil # sigs.k8s.io/gateway-api v1.2.1 @@ -2021,7 +2000,7 @@ sigs.k8s.io/kustomize/kyaml/yaml/internal/k8sgen/pkg/util/validation/field sigs.k8s.io/kustomize/kyaml/yaml/merge2 sigs.k8s.io/kustomize/kyaml/yaml/schema sigs.k8s.io/kustomize/kyaml/yaml/walk -# sigs.k8s.io/mcs-api v0.1.1-0.20250129110323-a7986579439f +# sigs.k8s.io/mcs-api v0.1.1-0.20250224121229-6c631f4730d0 ## explicit; go 1.23.0 sigs.k8s.io/mcs-api/pkg/apis/v1alpha1 sigs.k8s.io/mcs-api/pkg/client/clientset/versioned @@ -2029,7 +2008,7 @@ sigs.k8s.io/mcs-api/pkg/client/clientset/versioned/fake sigs.k8s.io/mcs-api/pkg/client/clientset/versioned/scheme sigs.k8s.io/mcs-api/pkg/client/clientset/versioned/typed/apis/v1alpha1 sigs.k8s.io/mcs-api/pkg/client/clientset/versioned/typed/apis/v1alpha1/fake -# sigs.k8s.io/structured-merge-diff/v4 v4.4.2 +# sigs.k8s.io/structured-merge-diff/v4 v4.5.0 ## explicit; go 1.13 sigs.k8s.io/structured-merge-diff/v4/fieldpath sigs.k8s.io/structured-merge-diff/v4/merge diff --git a/vendor/sigs.k8s.io/structured-merge-diff/v4/merge/update.go b/vendor/sigs.k8s.io/structured-merge-diff/v4/merge/update.go index 34ab2d6fb4..455818ff85 100644 --- a/vendor/sigs.k8s.io/structured-merge-diff/v4/merge/update.go +++ b/vendor/sigs.k8s.io/structured-merge-diff/v4/merge/update.go @@ -33,6 +33,9 @@ type UpdaterBuilder struct { Converter Converter IgnoreFilter map[fieldpath.APIVersion]fieldpath.Filter + // IgnoredFields provides a set of fields to ignore for each + IgnoredFields map[fieldpath.APIVersion]*fieldpath.Set + // Stop comparing the new object with old object after applying. // This was initially used to avoid spurious etcd update, but // since that's vastly inefficient, we've come-up with a better @@ -46,6 +49,7 @@ func (u *UpdaterBuilder) BuildUpdater() *Updater { return &Updater{ Converter: u.Converter, IgnoreFilter: u.IgnoreFilter, + IgnoredFields: u.IgnoredFields, returnInputOnNoop: u.ReturnInputOnNoop, } } @@ -56,6 +60,9 @@ type Updater struct { // Deprecated: This will eventually become private. Converter Converter + // Deprecated: This will eventually become private. + IgnoredFields map[fieldpath.APIVersion]*fieldpath.Set + // Deprecated: This will eventually become private. IgnoreFilter map[fieldpath.APIVersion]fieldpath.Filter @@ -70,8 +77,19 @@ func (s *Updater) update(oldObject, newObject *typed.TypedValue, version fieldpa return nil, nil, fmt.Errorf("failed to compare objects: %v", err) } - versions := map[fieldpath.APIVersion]*typed.Comparison{ - version: compare.FilterFields(s.IgnoreFilter[version]), + var versions map[fieldpath.APIVersion]*typed.Comparison + + if s.IgnoredFields != nil && s.IgnoreFilter != nil { + return nil, nil, fmt.Errorf("IgnoreFilter and IgnoreFilter may not both be set") + } + if s.IgnoredFields != nil { + versions = map[fieldpath.APIVersion]*typed.Comparison{ + version: compare.ExcludeFields(s.IgnoredFields[version]), + } + } else { + versions = map[fieldpath.APIVersion]*typed.Comparison{ + version: compare.FilterFields(s.IgnoreFilter[version]), + } } for manager, managerSet := range managers { @@ -101,7 +119,12 @@ func (s *Updater) update(oldObject, newObject *typed.TypedValue, version fieldpa if err != nil { return nil, nil, fmt.Errorf("failed to compare objects: %v", err) } - versions[managerSet.APIVersion()] = compare.FilterFields(s.IgnoreFilter[managerSet.APIVersion()]) + + if s.IgnoredFields != nil { + versions[managerSet.APIVersion()] = compare.ExcludeFields(s.IgnoredFields[managerSet.APIVersion()]) + } else { + versions[managerSet.APIVersion()] = compare.FilterFields(s.IgnoreFilter[managerSet.APIVersion()]) + } } conflictSet := managerSet.Set().Intersection(compare.Modified.Union(compare.Added)) @@ -154,7 +177,16 @@ func (s *Updater) Update(liveObject, newObject *typed.TypedValue, version fieldp managers[manager] = fieldpath.NewVersionedSet(fieldpath.NewSet(), version, false) } set := managers[manager].Set().Difference(compare.Removed).Union(compare.Modified).Union(compare.Added) - ignoreFilter := s.IgnoreFilter[version] + + if s.IgnoredFields != nil && s.IgnoreFilter != nil { + return nil, nil, fmt.Errorf("IgnoreFilter and IgnoreFilter may not both be set") + } + var ignoreFilter fieldpath.Filter + if s.IgnoredFields != nil { + ignoreFilter = fieldpath.NewExcludeSetFilter(s.IgnoredFields[version]) + } else { + ignoreFilter = s.IgnoreFilter[version] + } if ignoreFilter != nil { set = ignoreFilter.Filter(set) } @@ -189,7 +221,15 @@ func (s *Updater) Apply(liveObject, configObject *typed.TypedValue, version fiel return nil, fieldpath.ManagedFields{}, fmt.Errorf("failed to get field set: %v", err) } - ignoreFilter := s.IgnoreFilter[version] + if s.IgnoredFields != nil && s.IgnoreFilter != nil { + return nil, nil, fmt.Errorf("IgnoreFilter and IgnoreFilter may not both be set") + } + var ignoreFilter fieldpath.Filter + if s.IgnoredFields != nil { + ignoreFilter = fieldpath.NewExcludeSetFilter(s.IgnoredFields[version]) + } else { + ignoreFilter = s.IgnoreFilter[version] + } if ignoreFilter != nil { set = ignoreFilter.Filter(set) } diff --git a/vendor/sigs.k8s.io/structured-merge-diff/v4/typed/typed.go b/vendor/sigs.k8s.io/structured-merge-diff/v4/typed/typed.go index 9be9028280..7edaa6d489 100644 --- a/vendor/sigs.k8s.io/structured-merge-diff/v4/typed/typed.go +++ b/vendor/sigs.k8s.io/structured-merge-diff/v4/typed/typed.go @@ -32,6 +32,21 @@ const ( AllowDuplicates ValidationOptions = iota ) +// extractItemsOptions is the options available when extracting items. +type extractItemsOptions struct { + appendKeyFields bool +} + +type ExtractItemsOption func(*extractItemsOptions) + +// WithAppendKeyFields configures ExtractItems to include key fields. +// It is exported for use in configuring ExtractItems. +func WithAppendKeyFields() ExtractItemsOption { + return func(opts *extractItemsOptions) { + opts.appendKeyFields = true + } +} + // AsTyped accepts a value and a type and returns a TypedValue. 'v' must have // type 'typeName' in the schema. An error is returned if the v doesn't conform // to the schema. @@ -187,7 +202,37 @@ func (tv TypedValue) RemoveItems(items *fieldpath.Set) *TypedValue { } // ExtractItems returns a value with only the provided list or map items extracted from the value. -func (tv TypedValue) ExtractItems(items *fieldpath.Set) *TypedValue { +func (tv TypedValue) ExtractItems(items *fieldpath.Set, opts ...ExtractItemsOption) *TypedValue { + options := &extractItemsOptions{} + for _, opt := range opts { + opt(options) + } + if options.appendKeyFields { + tvPathSet, err := tv.ToFieldSet() + if err == nil { + keyFieldPathSet := fieldpath.NewSet() + items.Iterate(func(path fieldpath.Path) { + if !tvPathSet.Has(path) { + return + } + for i, pe := range path { + if pe.Key == nil { + continue + } + for _, keyField := range *pe.Key { + keyName := keyField.Name + // Create a new slice with the same elements as path[:i+1], but set its capacity to len(path[:i+1]). + // This ensures that appending to keyFieldPath creates a new underlying array, avoiding accidental + // modification of the original slice (path). + keyFieldPath := append(path[:i+1:i+1], fieldpath.PathElement{FieldName: &keyName}) + keyFieldPathSet.Insert(keyFieldPath) + } + } + }) + items = items.Union(keyFieldPathSet) + } + } + tv.value = removeItemsWithSchema(tv.value, items, tv.schema, tv.typeRef, true) return &tv } diff --git a/vendor/sigs.k8s.io/structured-merge-diff/v4/value/scalar.go b/vendor/sigs.k8s.io/structured-merge-diff/v4/value/scalar.go index c78a4c18d1..5824219e51 100644 --- a/vendor/sigs.k8s.io/structured-merge-diff/v4/value/scalar.go +++ b/vendor/sigs.k8s.io/structured-merge-diff/v4/value/scalar.go @@ -43,7 +43,7 @@ func IntCompare(lhs, rhs int64) int { func BoolCompare(lhs, rhs bool) int { if lhs == rhs { return 0 - } else if lhs == false { + } else if !lhs { return -1 } return 1

      l&%8sgwvb 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 W`}+Ixe*YH$0RR67=X5Fn^b!D-HiJz7 literal 0 HcmV?d00001 diff --git a/vendor/github.com/cilium/charts/cilium-1.16.7.tgz b/vendor/github.com/cilium/charts/cilium-1.16.7.tgz new file mode 100644 index 0000000000000000000000000000000000000000..ea7417ee24b3749ab94b93e228e9e6797a143c1f GIT binary patch literal 205285 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 z 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 literal 0 HcmV?d00001 diff --git a/vendor/github.com/cilium/charts/cilium-1.16.8.tgz b/vendor/github.com/cilium/charts/cilium-1.16.8.tgz new file mode 100644 index 0000000000000000000000000000000000000000..dff3ab3f9abb9150ba37a53df07ccea203957258 GIT binary patch literal 205318 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