Compare commits
No commits in common. "7ac76c2e466fed01d7cac703be62b9cb0e06a2ef" and "f8ababb7bf53e8364af6e329ce9eaf298f4a6845" have entirely different histories.
7ac76c2e46
...
f8ababb7bf
4
.github/workflows/cache.yml
vendored
4
.github/workflows/cache.yml
vendored
@ -11,7 +11,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out the codebase
|
- name: Check out the codebase
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2
|
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # 4.2.0
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Cache Vagrant boxes
|
- name: Cache Vagrant boxes
|
||||||
id: cache-vagrant
|
id: cache-vagrant
|
||||||
uses: actions/cache@6849a6489940f00c2f30c0fb92c6274307ccb58a # 4.1.2
|
uses: actions/cache@2cdf405574d6ef1f33a1d12acccd3ae82f47b3f2 # 4.1.0
|
||||||
with:
|
with:
|
||||||
lookup-only: true #if it exists, we don't need to restore and can skip the next step
|
lookup-only: true #if it exists, we don't need to restore and can skip the next step
|
||||||
path: |
|
path: |
|
||||||
|
|||||||
8
.github/workflows/lint.yml
vendored
8
.github/workflows/lint.yml
vendored
@ -11,7 +11,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out the codebase
|
- name: Check out the codebase
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2
|
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # 4.2.0
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ jobs:
|
|||||||
cache: 'pip' # caching pip dependencies
|
cache: 'pip' # caching pip dependencies
|
||||||
|
|
||||||
- name: Restore Ansible cache
|
- name: Restore Ansible cache
|
||||||
uses: actions/cache/restore@6849a6489940f00c2f30c0fb92c6274307ccb58a # 4.1.2
|
uses: actions/cache/restore@2cdf405574d6ef1f33a1d12acccd3ae82f47b3f2 # 4.1.0
|
||||||
with:
|
with:
|
||||||
path: ~/.ansible/collections
|
path: ~/.ansible/collections
|
||||||
key: ansible-${{ hashFiles('collections/requirements.yml') }}
|
key: ansible-${{ hashFiles('collections/requirements.yml') }}
|
||||||
@ -45,9 +45,9 @@ jobs:
|
|||||||
runs-on: self-hosted
|
runs-on: self-hosted
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2
|
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # 4.2.0
|
||||||
- name: Ensure SHA pinned actions
|
- name: Ensure SHA pinned actions
|
||||||
uses: zgosalvez/github-actions-ensure-sha-pinned-actions@38608ef4fb69adae7f1eac6eeb88e67b7d083bfd # 3.0.16
|
uses: zgosalvez/github-actions-ensure-sha-pinned-actions@40ba2d51b6b6d8695f2b6bd74e785172d4f8d00f # 3.0.14
|
||||||
with:
|
with:
|
||||||
allowlist: |
|
allowlist: |
|
||||||
aws-actions/
|
aws-actions/
|
||||||
|
|||||||
6
.github/workflows/test.yml
vendored
6
.github/workflows/test.yml
vendored
@ -21,7 +21,7 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Check out the codebase
|
- name: Check out the codebase
|
||||||
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # 4.2.2
|
uses: actions/checkout@d632683dd7b4114ad314bca15554477dd762a938 # 4.2.0
|
||||||
with:
|
with:
|
||||||
ref: ${{ github.event.pull_request.head.sha }}
|
ref: ${{ github.event.pull_request.head.sha }}
|
||||||
|
|
||||||
@ -65,7 +65,7 @@ jobs:
|
|||||||
cache: 'pip' # caching pip dependencies
|
cache: 'pip' # caching pip dependencies
|
||||||
|
|
||||||
- name: Restore vagrant Boxes cache
|
- name: Restore vagrant Boxes cache
|
||||||
uses: actions/cache/restore@6849a6489940f00c2f30c0fb92c6274307ccb58a # 4.1.2
|
uses: actions/cache/restore@2cdf405574d6ef1f33a1d12acccd3ae82f47b3f2 # 4.1.0
|
||||||
with:
|
with:
|
||||||
path: ~/.vagrant.d/boxes
|
path: ~/.vagrant.d/boxes
|
||||||
key: vagrant-boxes-${{ hashFiles('**/molecule.yml') }}
|
key: vagrant-boxes-${{ hashFiles('**/molecule.yml') }}
|
||||||
@ -118,7 +118,7 @@ jobs:
|
|||||||
|
|
||||||
- name: Upload log files
|
- name: Upload log files
|
||||||
if: always() # do this even if a step before has failed
|
if: always() # do this even if a step before has failed
|
||||||
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882 # 4.4.3
|
uses: actions/upload-artifact@604373da6381bf24206979c74d06a550515601b9 # 4.4.1
|
||||||
with:
|
with:
|
||||||
name: logs
|
name: logs
|
||||||
path: |
|
path: |
|
||||||
|
|||||||
72
README.md
72
README.md
@ -117,78 +117,6 @@ Then change `server: https://127.0.0.1:6443` to match your master IP: `server: h
|
|||||||
|
|
||||||
See the commands [here](https://technotim.live/posts/k3s-etcd-ansible/#testing-your-cluster).
|
See the commands [here](https://technotim.live/posts/k3s-etcd-ansible/#testing-your-cluster).
|
||||||
|
|
||||||
### Variables
|
|
||||||
|
|
||||||
| Role(s) | Variable | Type | Default | Required | Description |
|
|
||||||
|---|---|---|---|---|---|
|
|
||||||
| `download` | `k3s_version` | string | ❌ | Required | K3s binaries version |
|
|
||||||
| `k3s_agent`, `k3s_server`, `k3s_server_post` | `apiserver_endpoint` | string | ❌ | Required | Virtual ip-address configured on each master |
|
|
||||||
| `k3s_agent` | `extra_agent_args` | string | `null` | Not required | Extra arguments for agents nodes |
|
|
||||||
| `k3s_agent`, `k3s_server` | `group_name_master` | string | `null` | Not required | Name othe master group |
|
|
||||||
| `k3s_agent` | `k3s_token` | string | `null` | Not required | Token used to communicate between masters |
|
|
||||||
| `k3s_agent`, `k3s_server` | `proxy_env` | dict | `null` | Not required | Internet proxy configurations |
|
|
||||||
| `k3s_agent`, `k3s_server` | `proxy_env.HTTP_PROXY` | string | ❌ | Required | HTTP internet proxy |
|
|
||||||
| `k3s_agent`, `k3s_server` | `proxy_env.HTTPS_PROXY` | string | ❌ | Required | HTTP internet proxy |
|
|
||||||
| `k3s_agent`, `k3s_server` | `proxy_env.NO_PROXY` | string | ❌ | Required | Addresses that will not use the proxies |
|
|
||||||
| `k3s_agent`, `k3s_server`, `reset` | `systemd_dir` | string | `/etc/systemd/system` | Not required | Path to systemd services |
|
|
||||||
| `k3s_custom_registries` | `custom_registries_yaml` | string | ❌ | Required | YAML block defining custom registries. The following is an example that pulls all images used in this playbook through your private registries. It also allows you to pull your own images from your private registry, without having to use imagePullSecrets in your deployments. If all you need is your own images and you don't care about caching the docker/quay/ghcr.io images, you can just remove those from the mirrors: section. |
|
|
||||||
| `k3s_server`, `k3s_server_post` | `cilium_bgp` | bool | `~` | Not required | Enable cilium BGP control plane for LB services and pod cidrs. Disables the use of MetalLB. |
|
|
||||||
| `k3s_server`, `k3s_server_post` | `cilium_iface` | string | ❌ | Not required | The network interface used for when Cilium is enabled |
|
|
||||||
| `k3s_server` | `extra_server_args` | string | `""` | Not required | Extra arguments for server nodes |
|
|
||||||
| `k3s_server` | `k3s_create_kubectl_symlink` | bool | `false` | Not required | Create the kubectl -> k3s symlink |
|
|
||||||
| `k3s_server` | `k3s_create_crictl_symlink` | bool | `true` | Not required | Create the crictl -> k3s symlink |
|
|
||||||
| `k3s_server` | `kube_vip_arp` | bool | `true` | Not required | Enables kube-vip ARP broadcasts |
|
|
||||||
| `k3s_server` | `kube_vip_bgp` | bool | `false` | Not required | Enables kube-vip BGP peering |
|
|
||||||
| `k3s_server` | `kube_vip_bgp_routerid` | string | `"127.0.0.1"` | Not required | Defines the router ID for the kube-vip BGP server |
|
|
||||||
| `k3s_server` | `kube_vip_bgp_as` | string | `"64513"` | Not required | Defines the AS for the kube-vip BGP server |
|
|
||||||
| `k3s_server` | `kube_vip_bgp_peeraddress` | string | `"192.168.30.1"` | Not required | Defines the address for the kube-vip BGP peer |
|
|
||||||
| `k3s_server` | `kube_vip_bgp_peeras` | string | `"64512"` | Not required | Defines the AS for the kube-vip BGP peer |
|
|
||||||
| `k3s_server` | `kube_vip_bgp_peers` | list | `[]` | Not required | List of BGP peer ASN & address pairs |
|
|
||||||
| `k3s_server` | `kube_vip_bgp_peers_groups` | list | `['k3s_master']` | Not required | Inventory group in which to search for additional `kube_vip_bgp_peers` parameters to merge. |
|
|
||||||
| `k3s_server` | `kube_vip_iface` | string | `~` | Not required | Explicitly define an interface that ALL control nodes should use to propagate the VIP, define it here. Otherwise, kube-vip will determine the right interface automatically at runtime. |
|
|
||||||
| `k3s_server` | `kube_vip_tag_version` | string | `v0.7.2` | Not required | Image tag for kube-vip |
|
|
||||||
| `k3s_server` | `kube_vip_cloud_provider_tag_version` | string | `main` | Not required | Tag for kube-vip-cloud-provider manifest when enable |
|
|
||||||
| `k3s_server`, `k3_server_post` | `kube_vip_lb_ip_range` | string | `~` | Not required | IP range for kube-vip load balancer |
|
|
||||||
| `k3s_server`, `k3s_server_post` | `metal_lb_controller_tag_version` | string | `v0.14.3` | Not required | Image tag for MetalLB |
|
|
||||||
| `k3s_server` | `metal_lb_speaker_tag_version` | string | `v0.14.3` | Not required | Image tag for MetalLB |
|
|
||||||
| `k3s_server` | `metal_lb_type` | string | `native` | Not required | Use FRR mode or native. Valid values are `frr` and `native` |
|
|
||||||
| `k3s_server` | `retry_count` | int | `20` | Not required | Amount of retries when verifying that nodes joined |
|
|
||||||
| `k3s_server` | `server_init_args` | string | ❌ | Not required | Arguments for server nodes |
|
|
||||||
| `k3s_server_post` | `bpf_lb_algorithm` | string | `maglev` | Not required | BPF lb algorithm |
|
|
||||||
| `k3s_server_post` | `bpf_lb_mode` | string | `hybrid` | Not required | BPF lb mode |
|
|
||||||
| `k3s_server_post` | `calico_blocksize` | int | `26` | Not required | IP pool block size |
|
|
||||||
| `k3s_server_post` | `calico_ebpf` | bool | `false` | Not required | Use eBPF dataplane instead of iptables |
|
|
||||||
| `k3s_server_post` | `calico_encapsulation` | string | `VXLANCrossSubnet` | Not required | IP pool encapsulation |
|
|
||||||
| `k3s_server_post` | `calico_natOutgoing` | string | `Enabled` | Not required | IP pool NAT outgoing |
|
|
||||||
| `k3s_server_post` | `calico_nodeSelector` | string | `all()` | Not required | IP pool node selector |
|
|
||||||
| `k3s_server_post` | `calico_iface` | string | `~` | Not required | The network interface used for when Calico is enabled |
|
|
||||||
| `k3s_server_post` | `calico_tag` | string | `v3.27.2` | Not required | Calico version tag |
|
|
||||||
| `k3s_server_post` | `cilium_bgp_my_asn` | int | `64513` | Not required | Local ASN for BGP peer |
|
|
||||||
| `k3s_server_post` | `cilium_bgp_peer_asn` | int | `64512` | Not required | BGP peer ASN |
|
|
||||||
| `k3s_server_post` | `cilium_bgp_peer_address` | string | `~` | Not required | BGP peer address |
|
|
||||||
| `k3s_server_post` | `cilium_bgp_neighbors` | list | `[]` | Not required | List of BGP peer ASN & address pairs |
|
|
||||||
| `k3s_server_post` | `cilium_bgp_neighbors_groups` | list | `['k3s_all']` | Not required | Inventory group in which to search for additional `cilium_bgp_neighbors` parameters to merge. |
|
|
||||||
| `k3s_server_post` | `cilium_bgp_lb_cidr` | string | `192.168.31.0/24` | Not required | BGP load balancer IP range |
|
|
||||||
| `k3s_server_post` | `cilium_exportPodCIDR` | bool | `true` | Not required | Export pod CIDR |
|
|
||||||
| `k3s_server_post` | `cilium_hubble` | bool | `true` | Not required | Enable Cilium Hubble |
|
|
||||||
| `k3s_server_post` | `cilium_hubble` | bool | `true` | Not required | Enable Cilium Hubble |
|
|
||||||
| `k3s_server_post` | `cilium_mode` | string | `native` | Not required | Inner-node communication mode (choices are `native` and `routed`) |
|
|
||||||
| `k3s_server_post` | `cluster_cidr` | string | `10.52.0.0/16` | Not required | Inner-cluster IP range |
|
|
||||||
| `k3s_server_post` | `enable_bpf_masquerade` | bool | `true` | Not required | Use IP masquerading |
|
|
||||||
| `k3s_server_post` | `kube_proxy_replacement` | bool | `true` | Not required | Replace the native kube-proxy with Cilium |
|
|
||||||
| `k3s_server_post` | `metal_lb_available_timeout` | string | `240s` | Not required | Wait for MetalLB resources |
|
|
||||||
| `k3s_server_post` | `metal_lb_ip_range` | string | `192.168.30.80-192.168.30.90` | Not required | MetalLB ip range for load balancer |
|
|
||||||
| `k3s_server_post` | `metal_lb_controller_tag_version` | string | `v0.14.3` | Not required | Image tag for MetalLB |
|
|
||||||
| `k3s_server_post` | `metal_lb_mode` | string | `layer2` | Not required | Metallb mode (choices are `bgp` and `layer2`) |
|
|
||||||
| `k3s_server_post` | `metal_lb_bgp_my_asn` | string | `~` | Not required | BGP ASN configurations |
|
|
||||||
| `k3s_server_post` | `metal_lb_bgp_peer_asn` | string | `~` | Not required | BGP peer ASN configurations |
|
|
||||||
| `k3s_server_post` | `metal_lb_bgp_peer_address` | string | `~` | Not required | BGP peer address |
|
|
||||||
| `lxc` | `custom_reboot_command` | string | `~` | Not required | Command to run on reboot |
|
|
||||||
| `prereq` | `system_timezone` | string | `null` | Not required | Timezone to be set on all nodes |
|
|
||||||
| `proxmox_lxc`, `reset_proxmox_lxc` | `proxmox_lxc_ct_ids` | list | ❌ | Required | Proxmox container ID list |
|
|
||||||
| `raspberrypi` | `state` | string | `present` | Not required | Indicates whether the k3s prerequisites for Raspberry Pi should be set up (possible values are `present` and `absent`) |
|
|
||||||
|
|
||||||
|
|
||||||
### Troubleshooting
|
### Troubleshooting
|
||||||
|
|
||||||
Be sure to see [this post](https://github.com/techno-tim/k3s-ansible/discussions/20) on how to troubleshoot common problems
|
Be sure to see [this post](https://github.com/techno-tim/k3s-ansible/discussions/20) on how to troubleshoot common problems
|
||||||
|
|||||||
@ -8,6 +8,6 @@ spec:
|
|||||||
selector:
|
selector:
|
||||||
app: nginx
|
app: nginx
|
||||||
ports:
|
ports:
|
||||||
- port: 11111
|
- port: 80
|
||||||
targetPort: 80
|
targetPort: 80
|
||||||
type: LoadBalancer
|
type: LoadBalancer
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
#
|
#
|
||||||
ansible-compat==4.1.11
|
ansible-compat==4.1.11
|
||||||
# via molecule
|
# via molecule
|
||||||
ansible-core==2.18.0
|
ansible-core==2.17.5
|
||||||
# via
|
# via
|
||||||
# -r requirements.in
|
# -r requirements.in
|
||||||
# ansible-compat
|
# ansible-compat
|
||||||
|
|||||||
@ -19,19 +19,14 @@ argument_specs:
|
|||||||
|
|
||||||
proxy_env:
|
proxy_env:
|
||||||
type: dict
|
type: dict
|
||||||
description:
|
description: Internet proxy configurations
|
||||||
- Internet proxy configurations.
|
|
||||||
- See https://docs.k3s.io/advanced#configuring-an-http-proxy for details
|
|
||||||
default: ~
|
default: ~
|
||||||
options:
|
options:
|
||||||
HTTP_PROXY:
|
HTTP_PROXY:
|
||||||
description: HTTP internet proxy
|
|
||||||
required: true
|
required: true
|
||||||
HTTPS_PROXY:
|
HTTPS_PROXY:
|
||||||
description: HTTPS internet proxy
|
|
||||||
required: true
|
required: true
|
||||||
NO_PROXY:
|
NO_PROXY:
|
||||||
description: Addresses that will not use the proxies
|
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
systemd_dir:
|
systemd_dir:
|
||||||
|
|||||||
@ -16,9 +16,6 @@ kube_vip_bgp_as: "64513"
|
|||||||
kube_vip_bgp_peeraddress: 192.168.30.1
|
kube_vip_bgp_peeraddress: 192.168.30.1
|
||||||
kube_vip_bgp_peeras: "64512"
|
kube_vip_bgp_peeras: "64512"
|
||||||
|
|
||||||
kube_vip_bgp_peers: []
|
|
||||||
kube_vip_bgp_peers_groups: ['k3s_master']
|
|
||||||
|
|
||||||
metal_lb_controller_tag_version: v0.14.3
|
metal_lb_controller_tag_version: v0.14.3
|
||||||
metal_lb_speaker_tag_version: v0.14.3
|
metal_lb_speaker_tag_version: v0.14.3
|
||||||
metal_lb_type: native
|
metal_lb_type: native
|
||||||
|
|||||||
@ -62,14 +62,6 @@ argument_specs:
|
|||||||
description: Defines the AS for the kube-vip BGP peer
|
description: Defines the AS for the kube-vip BGP peer
|
||||||
default: "64512"
|
default: "64512"
|
||||||
|
|
||||||
kube_vip_bgp_peers:
|
|
||||||
description: List of BGP peer ASN & address pairs
|
|
||||||
default: []
|
|
||||||
|
|
||||||
kube_vip_bgp_peers_groups:
|
|
||||||
description: Inventory group in which to search for additional kube_vip_bgp_peers parameters to merge.
|
|
||||||
default: ['k3s_master']
|
|
||||||
|
|
||||||
kube_vip_iface:
|
kube_vip_iface:
|
||||||
description:
|
description:
|
||||||
- Explicitly define an interface that ALL control nodes
|
- Explicitly define an interface that ALL control nodes
|
||||||
@ -103,23 +95,17 @@ argument_specs:
|
|||||||
- frr
|
- frr
|
||||||
- native
|
- native
|
||||||
default: native
|
default: native
|
||||||
description: Use FRR mode or native. Valid values are `frr` and `native`
|
|
||||||
|
|
||||||
proxy_env:
|
proxy_env:
|
||||||
type: dict
|
type: dict
|
||||||
description:
|
description: Internet proxy configurations
|
||||||
- Internet proxy configurations.
|
|
||||||
- See https://docs.k3s.io/advanced#configuring-an-http-proxy for details
|
|
||||||
default: ~
|
default: ~
|
||||||
options:
|
options:
|
||||||
HTTP_PROXY:
|
HTTP_PROXY:
|
||||||
description: HTTP internet proxy
|
|
||||||
required: true
|
required: true
|
||||||
HTTPS_PROXY:
|
HTTPS_PROXY:
|
||||||
description: HTTPS internet proxy
|
|
||||||
required: true
|
required: true
|
||||||
NO_PROXY:
|
NO_PROXY:
|
||||||
description: Addresses that will not use the proxies
|
|
||||||
required: true
|
required: true
|
||||||
|
|
||||||
retry_count:
|
retry_count:
|
||||||
|
|||||||
@ -1,8 +1,4 @@
|
|||||||
---
|
---
|
||||||
- name: Set _kube_vip_bgp_peers fact
|
|
||||||
ansible.builtin.set_fact:
|
|
||||||
_kube_vip_bgp_peers: "{{ lookup('community.general.merge_variables', '^kube_vip_bgp_peers__.+$', initial_value=kube_vip_bgp_peers, groups=kube_vip_bgp_peers_groups) }}" # yamllint disable-line rule:line-length
|
|
||||||
|
|
||||||
- name: Create manifests directory on first master
|
- name: Create manifests directory on first master
|
||||||
ansible.builtin.file:
|
ansible.builtin.file:
|
||||||
path: /var/lib/rancher/k3s/server/manifests
|
path: /var/lib/rancher/k3s/server/manifests
|
||||||
|
|||||||
@ -61,10 +61,6 @@ spec:
|
|||||||
- name: bgp_routerid
|
- name: bgp_routerid
|
||||||
value: "{{ kube_vip_bgp_routerid }}"
|
value: "{{ kube_vip_bgp_routerid }}"
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if _kube_vip_bgp_peers | length > 0 %}
|
|
||||||
- name: bgppeers
|
|
||||||
value: "{{ _kube_vip_bgp_peers | map(attribute='peer_address') | zip(_kube_vip_bgp_peers| map(attribute='peer_asn')) | map('join', ',') | join(':') }}" # yamllint disable-line rule:line-length
|
|
||||||
{% else %}
|
|
||||||
{% if kube_vip_bgp_as is defined %}
|
{% if kube_vip_bgp_as is defined %}
|
||||||
- name: bgp_as
|
- name: bgp_as
|
||||||
value: "{{ kube_vip_bgp_as }}"
|
value: "{{ kube_vip_bgp_as }}"
|
||||||
@ -77,7 +73,6 @@ spec:
|
|||||||
- name: bgp_peeras
|
- name: bgp_peeras
|
||||||
value: "{{ kube_vip_bgp_peeras }}"
|
value: "{{ kube_vip_bgp_peeras }}"
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endif %}
|
|
||||||
{% endif %}
|
{% endif %}
|
||||||
image: ghcr.io/kube-vip/kube-vip:{{ kube_vip_tag_version }}
|
image: ghcr.io/kube-vip/kube-vip:{{ kube_vip_tag_version }}
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
|
|||||||
@ -224,6 +224,7 @@
|
|||||||
- name: Set _cilium_bgp_neighbors fact
|
- name: Set _cilium_bgp_neighbors fact
|
||||||
ansible.builtin.set_fact:
|
ansible.builtin.set_fact:
|
||||||
_cilium_bgp_neighbors: "{{ lookup('community.general.merge_variables', '^cilium_bgp_neighbors__.+$', initial_value=cilium_bgp_neighbors, groups=cilium_bgp_neighbors_groups) }}" # yamllint disable-line rule:line-length
|
_cilium_bgp_neighbors: "{{ lookup('community.general.merge_variables', '^cilium_bgp_neighbors__.+$', initial_value=cilium_bgp_neighbors, groups=cilium_bgp_neighbors_groups) }}" # yamllint disable-line rule:line-length
|
||||||
|
when: cilium_bgp_neighbors | length > 0
|
||||||
|
|
||||||
- name: Copy BGP manifests to first master
|
- name: Copy BGP manifests to first master
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
|
|||||||
@ -5,4 +5,3 @@ argument_specs:
|
|||||||
options:
|
options:
|
||||||
custom_reboot_command:
|
custom_reboot_command:
|
||||||
default: ~
|
default: ~
|
||||||
description: Command to run on reboot
|
|
||||||
|
|||||||
@ -1,9 +0,0 @@
|
|||||||
---
|
|
||||||
argument_specs:
|
|
||||||
main:
|
|
||||||
short_description: Proxmox LXC settings
|
|
||||||
options:
|
|
||||||
proxmox_lxc_ct_ids:
|
|
||||||
description: Proxmox container ID list
|
|
||||||
type: list
|
|
||||||
required: true
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
---
|
|
||||||
argument_specs:
|
|
||||||
main:
|
|
||||||
short_description: Adjust some Raspberry Pi specific requisites
|
|
||||||
options:
|
|
||||||
state:
|
|
||||||
default: present
|
|
||||||
description:
|
|
||||||
- Indicates whether the k3s prerequisites for Raspberry Pi should be
|
|
||||||
- set up (possible values are `present` and `absent`)
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
---
|
|
||||||
argument_specs:
|
|
||||||
main:
|
|
||||||
short_description: Proxmox LXC settings
|
|
||||||
options:
|
|
||||||
proxmox_lxc_ct_ids:
|
|
||||||
description: Proxmox container ID list
|
|
||||||
type: list
|
|
||||||
required: true
|
|
||||||
@ -1 +0,0 @@
|
|||||||
https://github.com/caddyserver/ingress
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
/.github
|
|
||||||
/bin
|
|
||||||
/charts
|
|
||||||
/kubernetes
|
|
||||||
|
|
||||||
/.gitignore
|
|
||||||
/.goreleaser.yaml
|
|
||||||
/CONTRIBUTING.md
|
|
||||||
/ct.yaml
|
|
||||||
/skaffold.yaml
|
|
||||||
/README.md
|
|
||||||
/Makefile
|
|
||||||
/LICENSE.txt
|
|
||||||
6
serveis/altres/caddy/ingress/.gitignore
vendored
6
serveis/altres/caddy/ingress/.gitignore
vendored
@ -1,6 +0,0 @@
|
|||||||
.DS_Store
|
|
||||||
bin
|
|
||||||
vendor
|
|
||||||
.idea/
|
|
||||||
dist/
|
|
||||||
ingress-controller
|
|
||||||
@ -1,112 +0,0 @@
|
|||||||
project_name: ingress
|
|
||||||
before:
|
|
||||||
hooks:
|
|
||||||
- go mod tidy
|
|
||||||
builds:
|
|
||||||
- main: ./cmd/caddy
|
|
||||||
binary: ingress-controller
|
|
||||||
env:
|
|
||||||
- CGO_ENABLED=0
|
|
||||||
goos:
|
|
||||||
- linux
|
|
||||||
goarch:
|
|
||||||
- amd64
|
|
||||||
- arm64
|
|
||||||
- s390x
|
|
||||||
mod_timestamp: "{{ .CommitTimestamp }}"
|
|
||||||
|
|
||||||
dockers:
|
|
||||||
- use: buildx
|
|
||||||
goos: linux
|
|
||||||
goarch: amd64
|
|
||||||
dockerfile: ./Dockerfile
|
|
||||||
skip_push: "true"
|
|
||||||
image_templates:
|
|
||||||
- "caddy/{{ .ProjectName }}:test-image"
|
|
||||||
build_flag_templates:
|
|
||||||
- "--platform=linux/amd64"
|
|
||||||
- "--pull"
|
|
||||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
|
||||||
- "--label=org.opencontainers.image.name={{.ProjectName}}"
|
|
||||||
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
|
||||||
- "--label=org.opencontainers.image.version={{.Version}}"
|
|
||||||
|
|
||||||
- use: buildx
|
|
||||||
goos: linux
|
|
||||||
goarch: amd64
|
|
||||||
dockerfile: ./Dockerfile
|
|
||||||
image_templates:
|
|
||||||
- "caddy/{{ .ProjectName }}:{{ .Tag }}-amd64"
|
|
||||||
build_flag_templates:
|
|
||||||
- "--platform=linux/amd64"
|
|
||||||
- "--pull"
|
|
||||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
|
||||||
- "--label=org.opencontainers.image.name={{.ProjectName}}"
|
|
||||||
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
|
||||||
- "--label=org.opencontainers.image.version={{.Version}}"
|
|
||||||
|
|
||||||
- use: buildx
|
|
||||||
goos: linux
|
|
||||||
goarch: arm64
|
|
||||||
dockerfile: ./Dockerfile
|
|
||||||
image_templates:
|
|
||||||
- "caddy/{{ .ProjectName }}:{{ .Tag }}-arm64v8"
|
|
||||||
build_flag_templates:
|
|
||||||
- "--platform=linux/arm64/v8"
|
|
||||||
- "--pull"
|
|
||||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
|
||||||
- "--label=org.opencontainers.image.name={{.ProjectName}}"
|
|
||||||
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
|
||||||
- "--label=org.opencontainers.image.version={{.Version}}"
|
|
||||||
|
|
||||||
- use: buildx
|
|
||||||
goos: linux
|
|
||||||
goarch: s390x
|
|
||||||
dockerfile: ./Dockerfile
|
|
||||||
image_templates:
|
|
||||||
- "caddy/{{ .ProjectName }}:{{ .Tag }}-s390x"
|
|
||||||
build_flag_templates:
|
|
||||||
- "--platform=linux/s390x"
|
|
||||||
- "--pull"
|
|
||||||
- "--label=org.opencontainers.image.created={{.Date}}"
|
|
||||||
- "--label=org.opencontainers.image.name={{.ProjectName}}"
|
|
||||||
- "--label=org.opencontainers.image.revision={{.FullCommit}}"
|
|
||||||
- "--label=org.opencontainers.image.version={{.Version}}"
|
|
||||||
|
|
||||||
docker_manifests:
|
|
||||||
# https://goreleaser.com/customization/docker_manifest/
|
|
||||||
- name_template: caddy/{{ .ProjectName }}:latest
|
|
||||||
image_templates:
|
|
||||||
- caddy/{{ .ProjectName }}:{{ .Tag }}-amd64
|
|
||||||
- caddy/{{ .ProjectName }}:{{ .Tag }}-arm64v8
|
|
||||||
- caddy/{{ .ProjectName }}:{{ .Tag }}-s390x
|
|
||||||
- name_template: caddy/{{ .ProjectName }}:v{{ .Major }}
|
|
||||||
image_templates:
|
|
||||||
- caddy/{{ .ProjectName }}:{{ .Tag }}-amd64
|
|
||||||
- caddy/{{ .ProjectName }}:{{ .Tag }}-arm64v8
|
|
||||||
- caddy/{{ .ProjectName }}:{{ .Tag }}-s390x
|
|
||||||
- name_template: caddy/{{ .ProjectName }}:v{{ .Major }}.{{ .Minor }}
|
|
||||||
image_templates:
|
|
||||||
- caddy/{{ .ProjectName }}:{{ .Tag }}-amd64
|
|
||||||
- caddy/{{ .ProjectName }}:{{ .Tag }}-arm64v8
|
|
||||||
- caddy/{{ .ProjectName }}:{{ .Tag }}-s390x
|
|
||||||
- name_template: caddy/{{ .ProjectName }}:{{ .Tag }}
|
|
||||||
image_templates:
|
|
||||||
- caddy/{{ .ProjectName }}:{{ .Tag }}-amd64
|
|
||||||
- caddy/{{ .ProjectName }}:{{ .Tag }}-arm64v8
|
|
||||||
- caddy/{{ .ProjectName }}:{{ .Tag }}-s390x
|
|
||||||
|
|
||||||
release:
|
|
||||||
disable: true
|
|
||||||
|
|
||||||
checksum:
|
|
||||||
name_template: "checksums.txt"
|
|
||||||
snapshot:
|
|
||||||
name_template: "{{ incpatch .Version }}-next"
|
|
||||||
changelog:
|
|
||||||
sort: asc
|
|
||||||
filters:
|
|
||||||
exclude:
|
|
||||||
- "^docs:"
|
|
||||||
- "^test:"
|
|
||||||
- "^ci:"
|
|
||||||
@ -1,54 +0,0 @@
|
|||||||
## Requirements
|
|
||||||
|
|
||||||
- A running kubernetes cluster (if you don't have one see *Setup a local cluster* section)
|
|
||||||
- [skaffold](https://skaffold.dev/) installed on your machine
|
|
||||||
- [helm 3](https://helm.sh/) installed on your machine
|
|
||||||
- [kubectl](https://kubernetes.io/docs/tasks/tools/#kubectl/) installed on your machine
|
|
||||||
|
|
||||||
### Setup a local cluster
|
|
||||||
|
|
||||||
- You need a machine with [docker](https://docker.io) up & running
|
|
||||||
- If you are using Docker Desktop enable the kubernetes cluster from the config (see [docs](https://docs.docker.com/desktop/kubernetes/) here)
|
|
||||||
|
|
||||||
Otherwise you can decide to use [kind]() or [minukube]()
|
|
||||||
|
|
||||||
## Setup development env
|
|
||||||
|
|
||||||
Start `skaffold` using the command:
|
|
||||||
```
|
|
||||||
make dev
|
|
||||||
```
|
|
||||||
|
|
||||||
this will automatically:
|
|
||||||
|
|
||||||
- build your docker image every time you change some code
|
|
||||||
- update the helm release every time you change the helm chart
|
|
||||||
- expose the caddy ingress controller (port 8080 and 8443)
|
|
||||||
|
|
||||||
You can test that all work as expected with:
|
|
||||||
```
|
|
||||||
curl -D - -s -k https://example1.kubernetes.localhost:8443/hello1 --resolve example1.kubernetes.localhost:8443:127.0.0.1
|
|
||||||
curl -D - -s -k https://example1.kubernetes.localhost:8443/hello2 --resolve example1.kubernetes.localhost:8443:127.0.0.1
|
|
||||||
curl -D - -s -k https://example2.kubernetes.localhost:8443/hello1 --resolve example2.kubernetes.localhost:8443:127.0.0.1
|
|
||||||
curl -D - -s -k https://example2.kubernetes.localhost:8443/hello2 --resolve example2.kubernetes.localhost:8443:127.0.0.1
|
|
||||||
```
|
|
||||||
|
|
||||||
You can change domains defined in `kuberentes/sample` folder with some domain that are risolved on your local machine or you can add them in the `/etc/host` file to be automatically resolved as localhost.
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
- You can change local port forwarded by skaffold by changing the port values in the `skaffold.yaml` file on section `portForward` `localPort`. Remind that you can forward only port greater than 1024 if you execute it as non-root user
|
|
||||||
- We use an internal CA for test to simplify the installation, but then you can decide to use external CA for tests, see the `values.yaml` in the helm chart for the info on how to configure it.
|
|
||||||
|
|
||||||
## Releasing new helm chart version
|
|
||||||
|
|
||||||
If you want to release a new version of the `caddy-ingress-controller` chart, you'll need
|
|
||||||
to create a new PR with:
|
|
||||||
- The new chart's `version` in `Chart.yaml`
|
|
||||||
- The new `image.tag` in `values.yaml` (if you want to update the default image used in the chart)
|
|
||||||
- The new `appVersion` in `Chart.yaml` (if you did the previous line)
|
|
||||||
|
|
||||||
## Releasing a new app version
|
|
||||||
|
|
||||||
To release a new caddy-ingress-controller image, you need to create a new semver tag.
|
|
||||||
It will build and push an image to https://hub.docker.com/r/caddy/ingress.
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
FROM alpine:latest AS certs
|
|
||||||
RUN apk --update add ca-certificates
|
|
||||||
|
|
||||||
FROM alpine:latest
|
|
||||||
|
|
||||||
EXPOSE 80 443
|
|
||||||
ENTRYPOINT ["/ingress-controller"]
|
|
||||||
|
|
||||||
COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt
|
|
||||||
COPY ingress-controller /
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
FROM golang:1.22 as build
|
|
||||||
|
|
||||||
ENV CGO_ENABLED=0
|
|
||||||
|
|
||||||
WORKDIR /go/ingress-controller
|
|
||||||
|
|
||||||
COPY go.mod go.sum /go/ingress-controller/
|
|
||||||
RUN go mod download
|
|
||||||
|
|
||||||
COPY . .
|
|
||||||
RUN go build -o ./bin/ingress-controller ./cmd/caddy
|
|
||||||
|
|
||||||
FROM alpine:3.18
|
|
||||||
|
|
||||||
EXPOSE 80 443
|
|
||||||
|
|
||||||
COPY --from=build /go/ingress-controller/bin/ingress-controller /ingress-controller
|
|
||||||
|
|
||||||
ENTRYPOINT ["/ingress-controller"]
|
|
||||||
@ -1,202 +0,0 @@
|
|||||||
|
|
||||||
Apache License
|
|
||||||
Version 2.0, January 2004
|
|
||||||
http://www.apache.org/licenses/
|
|
||||||
|
|
||||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
||||||
|
|
||||||
1. Definitions.
|
|
||||||
|
|
||||||
"License" shall mean the terms and conditions for use, reproduction,
|
|
||||||
and distribution as defined by Sections 1 through 9 of this document.
|
|
||||||
|
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by
|
|
||||||
the copyright owner that is granting the License.
|
|
||||||
|
|
||||||
"Legal Entity" shall mean the union of the acting entity and all
|
|
||||||
other entities that control, are controlled by, or are under common
|
|
||||||
control with that entity. For the purposes of this definition,
|
|
||||||
"control" means (i) the power, direct or indirect, to cause the
|
|
||||||
direction or management of such entity, whether by contract or
|
|
||||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
||||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
||||||
|
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity
|
|
||||||
exercising permissions granted by this License.
|
|
||||||
|
|
||||||
"Source" form shall mean the preferred form for making modifications,
|
|
||||||
including but not limited to software source code, documentation
|
|
||||||
source, and configuration files.
|
|
||||||
|
|
||||||
"Object" form shall mean any form resulting from mechanical
|
|
||||||
transformation or translation of a Source form, including but
|
|
||||||
not limited to compiled object code, generated documentation,
|
|
||||||
and conversions to other media types.
|
|
||||||
|
|
||||||
"Work" shall mean the work of authorship, whether in Source or
|
|
||||||
Object form, made available under the License, as indicated by a
|
|
||||||
copyright notice that is included in or attached to the work
|
|
||||||
(an example is provided in the Appendix below).
|
|
||||||
|
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object
|
|
||||||
form, that is based on (or derived from) the Work and for which the
|
|
||||||
editorial revisions, annotations, elaborations, or other modifications
|
|
||||||
represent, as a whole, an original work of authorship. For the purposes
|
|
||||||
of this License, Derivative Works shall not include works that remain
|
|
||||||
separable from, or merely link (or bind by name) to the interfaces of,
|
|
||||||
the Work and Derivative Works thereof.
|
|
||||||
|
|
||||||
"Contribution" shall mean any work of authorship, including
|
|
||||||
the original version of the Work and any modifications or additions
|
|
||||||
to that Work or Derivative Works thereof, that is intentionally
|
|
||||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
||||||
or by an individual or Legal Entity authorized to submit on behalf of
|
|
||||||
the copyright owner. For the purposes of this definition, "submitted"
|
|
||||||
means any form of electronic, verbal, or written communication sent
|
|
||||||
to the Licensor or its representatives, including but not limited to
|
|
||||||
communication on electronic mailing lists, source code control systems,
|
|
||||||
and issue tracking systems that are managed by, or on behalf of, the
|
|
||||||
Licensor for the purpose of discussing and improving the Work, but
|
|
||||||
excluding communication that is conspicuously marked or otherwise
|
|
||||||
designated in writing by the copyright owner as "Not a Contribution."
|
|
||||||
|
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
||||||
on behalf of whom a Contribution has been received by Licensor and
|
|
||||||
subsequently incorporated within the Work.
|
|
||||||
|
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
copyright license to reproduce, prepare Derivative Works of,
|
|
||||||
publicly display, publicly perform, sublicense, and distribute the
|
|
||||||
Work and such Derivative Works in Source or Object form.
|
|
||||||
|
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of
|
|
||||||
this License, each Contributor hereby grants to You a perpetual,
|
|
||||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
||||||
(except as stated in this section) patent license to make, have made,
|
|
||||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
||||||
where such license applies only to those patent claims licensable
|
|
||||||
by such Contributor that are necessarily infringed by their
|
|
||||||
Contribution(s) alone or by combination of their Contribution(s)
|
|
||||||
with the Work to which such Contribution(s) was submitted. If You
|
|
||||||
institute patent litigation against any entity (including a
|
|
||||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
||||||
or a Contribution incorporated within the Work constitutes direct
|
|
||||||
or contributory patent infringement, then any patent licenses
|
|
||||||
granted to You under this License for that Work shall terminate
|
|
||||||
as of the date such litigation is filed.
|
|
||||||
|
|
||||||
4. Redistribution. You may reproduce and distribute copies of the
|
|
||||||
Work or Derivative Works thereof in any medium, with or without
|
|
||||||
modifications, and in Source or Object form, provided that You
|
|
||||||
meet the following conditions:
|
|
||||||
|
|
||||||
(a) You must give any other recipients of the Work or
|
|
||||||
Derivative Works a copy of this License; and
|
|
||||||
|
|
||||||
(b) You must cause any modified files to carry prominent notices
|
|
||||||
stating that You changed the files; and
|
|
||||||
|
|
||||||
(c) You must retain, in the Source form of any Derivative Works
|
|
||||||
that You distribute, all copyright, patent, trademark, and
|
|
||||||
attribution notices from the Source form of the Work,
|
|
||||||
excluding those notices that do not pertain to any part of
|
|
||||||
the Derivative Works; and
|
|
||||||
|
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its
|
|
||||||
distribution, then any Derivative Works that You distribute must
|
|
||||||
include a readable copy of the attribution notices contained
|
|
||||||
within such NOTICE file, excluding those notices that do not
|
|
||||||
pertain to any part of the Derivative Works, in at least one
|
|
||||||
of the following places: within a NOTICE text file distributed
|
|
||||||
as part of the Derivative Works; within the Source form or
|
|
||||||
documentation, if provided along with the Derivative Works; or,
|
|
||||||
within a display generated by the Derivative Works, if and
|
|
||||||
wherever such third-party notices normally appear. The contents
|
|
||||||
of the NOTICE file are for informational purposes only and
|
|
||||||
do not modify the License. You may add Your own attribution
|
|
||||||
notices within Derivative Works that You distribute, alongside
|
|
||||||
or as an addendum to the NOTICE text from the Work, provided
|
|
||||||
that such additional attribution notices cannot be construed
|
|
||||||
as modifying the License.
|
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and
|
|
||||||
may provide additional or different license terms and conditions
|
|
||||||
for use, reproduction, or distribution of Your modifications, or
|
|
||||||
for any such Derivative Works as a whole, provided Your use,
|
|
||||||
reproduction, and distribution of the Work otherwise complies with
|
|
||||||
the conditions stated in this License.
|
|
||||||
|
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
||||||
any Contribution intentionally submitted for inclusion in the Work
|
|
||||||
by You to the Licensor shall be under the terms and conditions of
|
|
||||||
this License, without any additional terms or conditions.
|
|
||||||
Notwithstanding the above, nothing herein shall supersede or modify
|
|
||||||
the terms of any separate license agreement you may have executed
|
|
||||||
with Licensor regarding such Contributions.
|
|
||||||
|
|
||||||
6. Trademarks. This License does not grant permission to use the trade
|
|
||||||
names, trademarks, service marks, or product names of the Licensor,
|
|
||||||
except as required for reasonable and customary use in describing the
|
|
||||||
origin of the Work and reproducing the content of the NOTICE file.
|
|
||||||
|
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
||||||
agreed to in writing, Licensor provides the Work (and each
|
|
||||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
||||||
implied, including, without limitation, any warranties or conditions
|
|
||||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
||||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
||||||
appropriateness of using or redistributing the Work and assume any
|
|
||||||
risks associated with Your exercise of permissions under this License.
|
|
||||||
|
|
||||||
8. Limitation of Liability. In no event and under no legal theory,
|
|
||||||
whether in tort (including negligence), contract, or otherwise,
|
|
||||||
unless required by applicable law (such as deliberate and grossly
|
|
||||||
negligent acts) or agreed to in writing, shall any Contributor be
|
|
||||||
liable to You for damages, including any direct, indirect, special,
|
|
||||||
incidental, or consequential damages of any character arising as a
|
|
||||||
result of this License or out of the use or inability to use the
|
|
||||||
Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all
|
|
||||||
other commercial damages or losses), even if such Contributor
|
|
||||||
has been advised of the possibility of such damages.
|
|
||||||
|
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing
|
|
||||||
the Work or Derivative Works thereof, You may choose to offer,
|
|
||||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
||||||
or other liability obligations and/or rights consistent with this
|
|
||||||
License. However, in accepting such obligations, You may act only
|
|
||||||
on Your own behalf and on Your sole responsibility, not on behalf
|
|
||||||
of any other Contributor, and only if You agree to indemnify,
|
|
||||||
defend, and hold each Contributor harmless for any liability
|
|
||||||
incurred by, or claims asserted against, such Contributor by reason
|
|
||||||
of your accepting any such warranty or additional liability.
|
|
||||||
|
|
||||||
END OF TERMS AND CONDITIONS
|
|
||||||
|
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following
|
|
||||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
||||||
replaced with your own identifying information. (Don't include
|
|
||||||
the brackets!) The text should be enclosed in the appropriate
|
|
||||||
comment syntax for the file format. We also recommend that a
|
|
||||||
file or class name and description of purpose be included on the
|
|
||||||
same "printed page" as the copyright notice for easier
|
|
||||||
identification within third-party archives.
|
|
||||||
|
|
||||||
Copyright [yyyy] [name of copyright owner]
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
build:
|
|
||||||
@mkdir -p bin
|
|
||||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o ./bin/ingress-controller ./cmd/caddy
|
|
||||||
|
|
||||||
dev:
|
|
||||||
skaffold dev --port-forward
|
|
||||||
@ -1,153 +0,0 @@
|
|||||||
# Caddy Ingress Controller
|
|
||||||
|
|
||||||
This is the Kubernetes Ingress Controller for Caddy. It includes functionality
|
|
||||||
for monitoring `Ingress` resources on a Kubernetes cluster and includes support
|
|
||||||
for providing automatic HTTPS certificates for all hostnames defined in the
|
|
||||||
ingress resources that it is managing.
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
- Helm 3+
|
|
||||||
- Kubernetes 1.19+
|
|
||||||
|
|
||||||
## Setup
|
|
||||||
|
|
||||||
In the `charts` folder, a Helm Chart is provided to make installing the Caddy
|
|
||||||
Ingress Controller on a Kubernetes cluster straightforward. To install the
|
|
||||||
Caddy Ingress Controller adhere to the following steps:
|
|
||||||
|
|
||||||
1. Create a new namespace in your cluster to isolate all Caddy resources.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
kubectl create namespace caddy-system
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Install the Helm Chart.
|
|
||||||
|
|
||||||
```sh
|
|
||||||
helm install \
|
|
||||||
--namespace=caddy-system \
|
|
||||||
--repo https://caddyserver.github.io/ingress/ \
|
|
||||||
--atomic \
|
|
||||||
mycaddy \
|
|
||||||
caddy-ingress-controller
|
|
||||||
```
|
|
||||||
|
|
||||||
Or
|
|
||||||
|
|
||||||
2. Generate kubernetes yaml file.
|
|
||||||
```sh
|
|
||||||
git clone https://github.com/caddyserver/ingress.git
|
|
||||||
cd ingress
|
|
||||||
|
|
||||||
# generate the yaml file
|
|
||||||
helm template mycaddy ./charts/caddy-ingress-controller \
|
|
||||||
--namespace=caddy-system \
|
|
||||||
> mycaddy.yaml
|
|
||||||
|
|
||||||
# apply the file
|
|
||||||
kubectl apply -f mycaddy.yaml
|
|
||||||
```
|
|
||||||
|
|
||||||
This will create a service of type `LoadBalancer` in the `caddy-system`
|
|
||||||
namespace on your cluster. You'll want to set any DNS records for accessing this
|
|
||||||
cluster to the external IP address of this `LoadBalancer` when the external IP
|
|
||||||
is provisioned by your cloud provider.
|
|
||||||
|
|
||||||
You can get the external IP address with `kubectl get svc -n caddy-system`
|
|
||||||
|
|
||||||
3. Alternate installation method: Glasskube
|
|
||||||
|
|
||||||
To install the Caddy ingress controller using [Glasskube](https://glasskube.dev/), you can select "caddy-ingress-controller" from the "ClusterPackages" tab in the Glasskube GUI then click "install" or you can run the following command:
|
|
||||||
```console
|
|
||||||
glasskube install caddy-ingress-controller
|
|
||||||
```
|
|
||||||
Add an email address in the package configuration section in the UI to enable automatic HTTPS, or run:
|
|
||||||
```
|
|
||||||
glasskube install caddy-ingress-controller --value "automaticHTTPS=your@email.com"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Debugging
|
|
||||||
|
|
||||||
To view any logs generated by Caddy or the Ingress Controller you can view the
|
|
||||||
pod logs of the Caddy Ingress Controller.
|
|
||||||
|
|
||||||
Get the pod name with:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
kubectl get pods -n caddy-system
|
|
||||||
```
|
|
||||||
|
|
||||||
View the pod logs:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
kubectl logs <pod-name> -n caddy-system
|
|
||||||
```
|
|
||||||
|
|
||||||
## Automatic HTTPS
|
|
||||||
|
|
||||||
To have automatic HTTPS (not to be confused with `On-demand TLS`), you simply have
|
|
||||||
to specify your email in the config map. When using Helm chart, you can add
|
|
||||||
`--set ingressController.config.email=your@email.com` when installing.
|
|
||||||
|
|
||||||
## On-Demand TLS
|
|
||||||
|
|
||||||
[On-demand TLS](https://caddyserver.com/docs/automatic-https#on-demand-tls) can generate SSL certs on the fly
|
|
||||||
and can be enabled in this controller by setting the `onDemandTLS` config to `true`:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
helm install ...\
|
|
||||||
--set ingressController.config.onDemandTLS=true
|
|
||||||
```
|
|
||||||
|
|
||||||
> You can also specify options
|
|
||||||
> for the on-demand config: `onDemandAsk`
|
|
||||||
|
|
||||||
|
|
||||||
## Bringing Your Own Certificates
|
|
||||||
|
|
||||||
If you would like to disable automatic HTTPS for a specific host and use your
|
|
||||||
own certificates you can create a new TLS secret in Kubernetes and define what
|
|
||||||
certificates to use when serving your application on the ingress resource.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
|
|
||||||
Create TLS secret `mycerts`, where `./tls.key` and `./tls.crt` are valid
|
|
||||||
certificates for `test.com`.
|
|
||||||
|
|
||||||
```
|
|
||||||
kubectl create secret tls mycerts --key ./tls.key --cert ./tls.crt
|
|
||||||
```
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
kind: Ingress
|
|
||||||
metadata:
|
|
||||||
name: example
|
|
||||||
annotations:
|
|
||||||
kubernetes.io/ingress.class: caddy
|
|
||||||
spec:
|
|
||||||
rules:
|
|
||||||
- host: test.com
|
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
- path: /
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: test
|
|
||||||
port:
|
|
||||||
number: 8080
|
|
||||||
tls:
|
|
||||||
- secretName: mycerts # use mycerts for host test.com
|
|
||||||
hosts:
|
|
||||||
- test.com
|
|
||||||
```
|
|
||||||
|
|
||||||
### Contribution
|
|
||||||
|
|
||||||
Learn how to start contributing on the [Contributing Guidline](CONTRIBUTING.md).
|
|
||||||
|
|
||||||
## License
|
|
||||||
|
|
||||||
[Apache License 2.0](LICENSE.txt)
|
|
||||||
@ -1,23 +0,0 @@
|
|||||||
# Patterns to ignore when building packages.
|
|
||||||
# This supports shell glob matching, relative path matching, and
|
|
||||||
# negation (prefixed with !). Only one pattern per line.
|
|
||||||
.DS_Store
|
|
||||||
# Common VCS dirs
|
|
||||||
.git/
|
|
||||||
.gitignore
|
|
||||||
.bzr/
|
|
||||||
.bzrignore
|
|
||||||
.hg/
|
|
||||||
.hgignore
|
|
||||||
.svn/
|
|
||||||
# Common backup files
|
|
||||||
*.swp
|
|
||||||
*.bak
|
|
||||||
*.tmp
|
|
||||||
*.orig
|
|
||||||
*~
|
|
||||||
# Various IDEs
|
|
||||||
.project
|
|
||||||
.idea/
|
|
||||||
*.tmproj
|
|
||||||
.vscode/
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
apiVersion: v2
|
|
||||||
name: caddy-ingress-controller
|
|
||||||
home: https://github.com/caddyserver/ingress
|
|
||||||
description: A helm chart for the Caddy Kubernetes ingress controller
|
|
||||||
icon: https://caddyserver.com/resources/images/caddy-circle-lock.svg
|
|
||||||
type: application
|
|
||||||
version: 1.3.0
|
|
||||||
appVersion: "v0.2.1"
|
|
||||||
keywords:
|
|
||||||
- ingress-controller
|
|
||||||
- caddyserver
|
|
||||||
sources:
|
|
||||||
- https://github.com/caddyserver/ingress
|
|
||||||
maintainers:
|
|
||||||
- name: mavimo
|
|
||||||
url: https://github.com/mavimo
|
|
||||||
- name: embraser01
|
|
||||||
url: https://github.com/embraser01
|
|
||||||
@ -1,84 +0,0 @@
|
|||||||
# caddy-ingress-controller
|
|
||||||
|
|
||||||
A helm chart for the Caddy Kubernetes ingress controller
|
|
||||||
|
|
||||||
## TL;DR:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
helm install my-release caddy-ingress-controller\
|
|
||||||
--repo https://caddyserver.github.io/ingress/ \
|
|
||||||
--namespace=caddy-system
|
|
||||||
```
|
|
||||||
|
|
||||||
## Introduction
|
|
||||||
|
|
||||||
This chart bootstraps a caddy-ingress-deployment deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager.
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
- Helm 3+
|
|
||||||
- Kubernetes 1.19+
|
|
||||||
|
|
||||||
## Installing the Chart
|
|
||||||
|
|
||||||
```bash
|
|
||||||
helm repo add caddyserver https://caddyserver.github.io/ingress/
|
|
||||||
helm install my-release caddyserver/caddy-ingress-controller --namespace=caddy-system
|
|
||||||
```
|
|
||||||
|
|
||||||
## Uninstalling the Chart
|
|
||||||
|
|
||||||
To uninstall `my-release`:
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ helm uninstall my-release
|
|
||||||
```
|
|
||||||
|
|
||||||
The command removes all the Kubernetes components associated with the chart and deletes the release.
|
|
||||||
|
|
||||||
> **Tip**: List all releases using `helm list` or start clean with `helm uninstall my-release`
|
|
||||||
|
|
||||||
## Additional Configuration
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
## Values
|
|
||||||
|
|
||||||
| Key | Type | Default | Description |
|
|
||||||
|-----|------|---------|-------------|
|
|
||||||
| affinity | object | `{}` | |
|
|
||||||
| fullnameOverride | string | `""` | |
|
|
||||||
| image.pullPolicy | string | `"IfNotPresent"` | |
|
|
||||||
| image.repository | string | `"caddy/ingress"` | |
|
|
||||||
| image.tag | string | `"latest"` | |
|
|
||||||
| imagePullSecrets | list | `[]` | |
|
|
||||||
| ingressController.config.acmeCA | string | `""` | |
|
|
||||||
| ingressController.config.acmeEABKeyId | string | `""` | |
|
|
||||||
| ingressController.config.acmeEABMacKey | string | `""` | |
|
|
||||||
| ingressController.config.debug | bool | `false` | |
|
|
||||||
| ingressController.config.email | string | `""` | |
|
|
||||||
| ingressController.config.metrics | bool | `true` | |
|
|
||||||
| ingressController.config.onDemandTLS | bool | `false` | |
|
|
||||||
| ingressController.config.proxyProtocol | bool | `false` | |
|
|
||||||
| ingressController.rbac.create | bool | `true` | |
|
|
||||||
| ingressController.verbose | bool | `false` | |
|
|
||||||
| ingressController.leaseId | string | `""` | |
|
|
||||||
| ingressController.watchNamespace | string | `""` | |
|
|
||||||
| minikube | bool | `false` | |
|
|
||||||
| nameOverride | string | `""` | |
|
|
||||||
| nodeSelector | object | `{}` | |
|
|
||||||
| podAnnotations | object | `{}` | |
|
|
||||||
| podDisruptionBudget.maxUnavailable | string | `nil` | |
|
|
||||||
| podDisruptionBudget.minAvailable | int | `1` | |
|
|
||||||
| podSecurityContext | object | `{}` | |
|
|
||||||
| replicaCount | int | `2` | |
|
|
||||||
| resources | object | `{}` | |
|
|
||||||
| securityContext.allowPrivilegeEscalation | bool | `true` | |
|
|
||||||
| securityContext.capabilities.add[0] | string | `"NET_BIND_SERVICE"` | |
|
|
||||||
| securityContext.capabilities.drop[0] | string | `"ALL"` | |
|
|
||||||
| securityContext.runAsGroup | int | `0` | |
|
|
||||||
| securityContext.runAsUser | int | `0` | |
|
|
||||||
| serviceAccount.annotations | object | `{}` | |
|
|
||||||
| serviceAccount.create | bool | `true` | |
|
|
||||||
| serviceAccount.name | string | `"caddy-ingress-controller"` | |
|
|
||||||
| tolerations | list | `[]` | |
|
|
||||||
@ -1,48 +0,0 @@
|
|||||||
{{ template "chart.header" . }}
|
|
||||||
|
|
||||||
{{ template "chart.description" . }}
|
|
||||||
|
|
||||||
## TL;DR:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
helm install my-release caddy-ingress-controller\
|
|
||||||
--repo https://caddyserver.github.io/ingress/ \
|
|
||||||
--namespace=caddy-system
|
|
||||||
```
|
|
||||||
|
|
||||||
## Introduction
|
|
||||||
|
|
||||||
This chart bootstraps a caddy-ingress-deployment deployment on a [Kubernetes](http://kubernetes.io) cluster using the [Helm](https://helm.sh) package manager.
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
- Helm 3+
|
|
||||||
- Kubernetes 1.14+
|
|
||||||
|
|
||||||
## Installing the Chart
|
|
||||||
|
|
||||||
```bash
|
|
||||||
helm repo add caddyserver https://caddyserver.github.io/ingress/
|
|
||||||
helm install my-release caddyserver/caddy-ingress-controller --namespace=caddy-system
|
|
||||||
```
|
|
||||||
|
|
||||||
## Uninstalling the Chart
|
|
||||||
|
|
||||||
To uninstall `my-release`:
|
|
||||||
|
|
||||||
```console
|
|
||||||
$ helm uninstall my-release
|
|
||||||
```
|
|
||||||
|
|
||||||
The command removes all the Kubernetes components associated with the chart and deletes the release.
|
|
||||||
|
|
||||||
> **Tip**: List all releases using `helm list` or start clean with `helm uninstall my-release`
|
|
||||||
|
|
||||||
## Additional Configuration
|
|
||||||
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{{ template "chart.valuesSection" . }}
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
image:
|
|
||||||
tag: test-image
|
|
||||||
|
|
||||||
ingressController:
|
|
||||||
verbose: true
|
|
||||||
@ -1,63 +0,0 @@
|
|||||||
{{/* vim: set filetype=mustache: */}}
|
|
||||||
{{/*
|
|
||||||
Expand the name of the chart.
|
|
||||||
*/}}
|
|
||||||
{{- define "caddy-ingress-controller.name" -}}
|
|
||||||
{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }}
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
{{/*
|
|
||||||
Create a default fully qualified app name.
|
|
||||||
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
|
|
||||||
If release name contains chart name it will be used as a full name.
|
|
||||||
*/}}
|
|
||||||
{{- define "caddy-ingress-controller.fullname" -}}
|
|
||||||
{{- if .Values.fullnameOverride }}
|
|
||||||
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
|
|
||||||
{{- else }}
|
|
||||||
{{- $name := default .Chart.Name .Values.nameOverride }}
|
|
||||||
{{- if contains $name .Release.Name }}
|
|
||||||
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
|
|
||||||
{{- else }}
|
|
||||||
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
{{/*
|
|
||||||
Create chart name and version as used by the chart label.
|
|
||||||
*/}}
|
|
||||||
{{- define "caddy-ingress-controller.chart" -}}
|
|
||||||
{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }}
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
{{/*
|
|
||||||
Common labels
|
|
||||||
*/}}
|
|
||||||
{{- define "caddy-ingress-controller.labels" -}}
|
|
||||||
helm.sh/chart: {{ include "caddy-ingress-controller.chart" . }}
|
|
||||||
{{ include "caddy-ingress-controller.selectorLabels" . }}
|
|
||||||
{{- if .Chart.AppVersion }}
|
|
||||||
app.kubernetes.io/version: {{ .Chart.AppVersion | quote }}
|
|
||||||
{{- end }}
|
|
||||||
app.kubernetes.io/managed-by: {{ .Release.Service }}
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
{{/*
|
|
||||||
Selector labels
|
|
||||||
*/}}
|
|
||||||
{{- define "caddy-ingress-controller.selectorLabels" -}}
|
|
||||||
app.kubernetes.io/name: {{ include "caddy-ingress-controller.name" . }}
|
|
||||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
{{/*
|
|
||||||
Create the name of the service account to use
|
|
||||||
*/}}
|
|
||||||
{{- define "caddy-ingress-controller.serviceAccountName" -}}
|
|
||||||
{{- if .Values.serviceAccount.create }}
|
|
||||||
{{- default (include "caddy-ingress-controller.fullname" .) .Values.serviceAccount.name }}
|
|
||||||
{{- else }}
|
|
||||||
{{- default "default" .Values.serviceAccount.name }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
@ -1,31 +0,0 @@
|
|||||||
{{- if .Values.ingressController.rbac.create }}
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
kind: ClusterRole
|
|
||||||
metadata:
|
|
||||||
name: {{ include "caddy-ingress-controller.name" . }}-role
|
|
||||||
namespace: {{ .Release.Namespace }}
|
|
||||||
rules:
|
|
||||||
- apiGroups:
|
|
||||||
- ""
|
|
||||||
- "networking.k8s.io"
|
|
||||||
- "coordination.k8s.io"
|
|
||||||
resources:
|
|
||||||
- ingresses
|
|
||||||
- ingresses/status
|
|
||||||
- secrets
|
|
||||||
- leases
|
|
||||||
verbs: ["*"]
|
|
||||||
- apiGroups:
|
|
||||||
- ""
|
|
||||||
resources:
|
|
||||||
- services
|
|
||||||
- pods
|
|
||||||
- nodes
|
|
||||||
- routes
|
|
||||||
- extensions
|
|
||||||
- configmaps
|
|
||||||
verbs:
|
|
||||||
- list
|
|
||||||
- get
|
|
||||||
- watch
|
|
||||||
{{- end }}
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
{{- if .Values.ingressController.rbac.create }}
|
|
||||||
kind: ClusterRoleBinding
|
|
||||||
apiVersion: rbac.authorization.k8s.io/v1
|
|
||||||
metadata:
|
|
||||||
name: {{ include "caddy-ingress-controller.name" . }}-role-binding
|
|
||||||
namespace: {{ .Release.Namespace }}
|
|
||||||
roleRef:
|
|
||||||
kind: ClusterRole
|
|
||||||
name: {{ include "caddy-ingress-controller.name" . }}-role
|
|
||||||
apiGroup: rbac.authorization.k8s.io
|
|
||||||
subjects:
|
|
||||||
- kind: ServiceAccount
|
|
||||||
name: {{ include "caddy-ingress-controller.serviceAccountName" . }}
|
|
||||||
namespace: {{ .Release.Namespace }}
|
|
||||||
{{- end }}
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: {{ include "caddy-ingress-controller.name" . }}-configmap
|
|
||||||
namespace: {{ .Release.Namespace }}
|
|
||||||
data:
|
|
||||||
{{- range keys .Values.ingressController.config | sortAlpha }}
|
|
||||||
{{ . }}: {{ get $.Values.ingressController.config . | quote }}
|
|
||||||
{{- end }}
|
|
||||||
|
|
||||||
@ -1,105 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: {{ include "caddy-ingress-controller.fullname" . }}
|
|
||||||
namespace: {{ .Release.Namespace }}
|
|
||||||
labels:
|
|
||||||
{{- include "caddy-ingress-controller.labels" . | nindent 4 }}
|
|
||||||
spec:
|
|
||||||
replicas: {{ .Values.replicaCount }}
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
{{- include "caddy-ingress-controller.selectorLabels" . | nindent 6 }}
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
{{- with .Values.podAnnotations }}
|
|
||||||
annotations:
|
|
||||||
{{- toYaml . | nindent 8 }}
|
|
||||||
{{- end }}
|
|
||||||
labels:
|
|
||||||
{{- include "caddy-ingress-controller.labels" . | nindent 8 }}
|
|
||||||
spec:
|
|
||||||
{{- with .Values.imagePullSecrets }}
|
|
||||||
imagePullSecrets:
|
|
||||||
{{- toYaml . | nindent 8 }}
|
|
||||||
{{- end }}
|
|
||||||
serviceAccountName: {{ include "caddy-ingress-controller.serviceAccountName" . }}
|
|
||||||
securityContext:
|
|
||||||
{{- toYaml .Values.podSecurityContext | nindent 8 }}
|
|
||||||
containers:
|
|
||||||
- name: {{ .Chart.Name }}
|
|
||||||
securityContext:
|
|
||||||
{{- toYaml .Values.securityContext | nindent 12 }}
|
|
||||||
image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}"
|
|
||||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
|
||||||
ports:
|
|
||||||
- name: http
|
|
||||||
containerPort: 80
|
|
||||||
protocol: TCP
|
|
||||||
{{- if .Values.minikube }}
|
|
||||||
hostPort: 80 # optional, required if running in minikube
|
|
||||||
{{- end }}
|
|
||||||
- name: https
|
|
||||||
containerPort: 443
|
|
||||||
protocol: TCP
|
|
||||||
{{- if .Values.minikube }}
|
|
||||||
hostPort: 443 # optional, required if running in minikube
|
|
||||||
{{- end }}
|
|
||||||
- name: metrics
|
|
||||||
containerPort: 9765
|
|
||||||
protocol: TCP
|
|
||||||
{{- if .Values.minikube }}
|
|
||||||
hostPort: 9765 # optional, required if running in minikube
|
|
||||||
{{- end }}
|
|
||||||
resources:
|
|
||||||
{{- toYaml .Values.resources | nindent 12 }}
|
|
||||||
env:
|
|
||||||
- name: POD_NAME
|
|
||||||
valueFrom:
|
|
||||||
fieldRef:
|
|
||||||
fieldPath: metadata.name
|
|
||||||
- name: POD_NAMESPACE
|
|
||||||
valueFrom:
|
|
||||||
fieldRef:
|
|
||||||
fieldPath: metadata.namespace
|
|
||||||
volumeMounts:
|
|
||||||
- name: tmp
|
|
||||||
mountPath: /tmp
|
|
||||||
args:
|
|
||||||
- -config-map={{ include "caddy-ingress-controller.name" . }}-configmap
|
|
||||||
{{- if .Values.ingressController.watchNamespace }}
|
|
||||||
- -namespace={{ .Values.ingressController.watchNamespace }}
|
|
||||||
{{- end }}
|
|
||||||
{{- if .Values.ingressController.leaseId }}
|
|
||||||
- -lease-id={{ .Values.ingressController.leaseId }}
|
|
||||||
{{- end }}
|
|
||||||
{{- if .Values.ingressController.verbose }}
|
|
||||||
- -verbose
|
|
||||||
{{- end }}
|
|
||||||
{{- if .Values.ingressController.className }}
|
|
||||||
- -class-name={{ .Values.ingressController.className }}
|
|
||||||
{{- end }}
|
|
||||||
{{- if .Values.ingressController.classNameRequired }}
|
|
||||||
- -class-name-required={{ .Values.ingressController.classNameRequired }}
|
|
||||||
{{- end }}
|
|
||||||
readinessProbe:
|
|
||||||
initialDelaySeconds: 3
|
|
||||||
periodSeconds: 10
|
|
||||||
httpGet:
|
|
||||||
port: 9765
|
|
||||||
path: /healthz
|
|
||||||
{{- with .Values.nodeSelector }}
|
|
||||||
nodeSelector:
|
|
||||||
{{- toYaml . | nindent 8 }}
|
|
||||||
{{- end }}
|
|
||||||
{{- with .Values.affinity }}
|
|
||||||
affinity:
|
|
||||||
{{- toYaml . | nindent 8 }}
|
|
||||||
{{- end }}
|
|
||||||
{{- with .Values.tolerations }}
|
|
||||||
tolerations:
|
|
||||||
{{- toYaml . | nindent 8 }}
|
|
||||||
{{- end }}
|
|
||||||
volumes:
|
|
||||||
- name: tmp
|
|
||||||
emptyDir: {}
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
{{- if .Values.loadBalancer.enabled }}
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: {{ include "caddy-ingress-controller.fullname" . }}
|
|
||||||
namespace: {{ .Release.Namespace }}
|
|
||||||
{{- with .Values.loadBalancer.annotations }}
|
|
||||||
annotations:
|
|
||||||
{{- toYaml . | nindent 4 }}
|
|
||||||
{{- end }}
|
|
||||||
labels:
|
|
||||||
{{- include "caddy-ingress-controller.labels" . | nindent 4 }}
|
|
||||||
{{- with .Values.loadBalancer.labels }}
|
|
||||||
{{- toYaml . | nindent 4 }}
|
|
||||||
{{- end }}
|
|
||||||
spec:
|
|
||||||
type: "LoadBalancer"
|
|
||||||
{{- if (semverCompare "<= 1.24.0" .Capabilities.KubeVersion.Version) }}
|
|
||||||
loadBalancerIP: {{ .Values.loadBalancer.loadBalancerIP }} #Deprecated in Kubernetes v1.24
|
|
||||||
{{- end }}
|
|
||||||
externalTrafficPolicy: {{ .Values.loadBalancer.externalTrafficPolicy }}
|
|
||||||
ports:
|
|
||||||
- name: http
|
|
||||||
port: 80
|
|
||||||
protocol: TCP
|
|
||||||
targetPort: http
|
|
||||||
- name: https
|
|
||||||
port: 443
|
|
||||||
protocol: TCP
|
|
||||||
targetPort: https
|
|
||||||
selector:
|
|
||||||
{{- include "caddy-ingress-controller.selectorLabels" . | nindent 4 }}
|
|
||||||
{{- end }}
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
apiVersion: policy/v1
|
|
||||||
kind: PodDisruptionBudget
|
|
||||||
metadata:
|
|
||||||
name: {{ include "caddy-ingress-controller.fullname" . }}
|
|
||||||
namespace: {{ .Release.Namespace }}
|
|
||||||
labels:
|
|
||||||
{{- include "caddy-ingress-controller.labels" . | nindent 4 }}
|
|
||||||
spec:
|
|
||||||
{{- with .Values.podDisruptionBudget }}
|
|
||||||
{{- if .minAvailable }}
|
|
||||||
minAvailable: {{ .minAvailable }}
|
|
||||||
{{- end }}
|
|
||||||
{{- if .maxUnavailable }}
|
|
||||||
maxUnavailable: {{ .maxUnavailable }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
{{- include "caddy-ingress-controller.selectorLabels" . | nindent 6 }}
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
{{- if not .Values.loadBalancer.enabled }}
|
|
||||||
apiVersion: v1
|
|
||||||
kind: Service
|
|
||||||
metadata:
|
|
||||||
name: {{ include "caddy-ingress-controller.fullname" . }}
|
|
||||||
namespace: {{ .Release.Namespace }}
|
|
||||||
{{- with .Values.service.annotations }}
|
|
||||||
annotations:
|
|
||||||
{{- toYaml . | nindent 4 }}
|
|
||||||
{{- end }}
|
|
||||||
labels:
|
|
||||||
{{- include "caddy-ingress-controller.labels" . | nindent 4 }}
|
|
||||||
spec:
|
|
||||||
type: {{ .Values.service.type }}
|
|
||||||
{{- if .Values.service.ipDualStack.enabled }}
|
|
||||||
ipFamilies: {{ toYaml .Values.service.ipDualStack.ipFamilies | nindent 4 }}
|
|
||||||
ipFamilyPolicy: {{ .Values.service.ipDualStack.ipFamilyPolicy }}
|
|
||||||
{{- end }}
|
|
||||||
internalTrafficPolicy: {{ .Values.service.internalTrafficPolicy }}
|
|
||||||
externalTrafficPolicy: {{ .Values.service.externalTrafficPolicy }}
|
|
||||||
{{- if and (eq .Values.service.type "ClusterIP") .Values.service.clusterIP }}
|
|
||||||
clusterIP: "{{ .Values.service.clusterIP }}"
|
|
||||||
{{- end }}
|
|
||||||
ports:
|
|
||||||
- name: http
|
|
||||||
port: 80
|
|
||||||
protocol: TCP
|
|
||||||
targetPort: http
|
|
||||||
- name: https
|
|
||||||
port: 443
|
|
||||||
protocol: TCP
|
|
||||||
targetPort: https
|
|
||||||
selector:
|
|
||||||
{{- include "caddy-ingress-controller.selectorLabels" . | nindent 4 }}
|
|
||||||
{{- end }}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
{{- if .Values.serviceAccount.create -}}
|
|
||||||
apiVersion: v1
|
|
||||||
kind: ServiceAccount
|
|
||||||
metadata:
|
|
||||||
name: {{ include "caddy-ingress-controller.serviceAccountName" . }}
|
|
||||||
namespace: {{ .Release.Namespace }}
|
|
||||||
labels:
|
|
||||||
{{- include "caddy-ingress-controller.labels" . | nindent 4 }}
|
|
||||||
{{- with .Values.serviceAccount.annotations }}
|
|
||||||
annotations:
|
|
||||||
{{- toYaml . | nindent 4 }}
|
|
||||||
{{- end }}
|
|
||||||
{{- end }}
|
|
||||||
@ -1,324 +0,0 @@
|
|||||||
{
|
|
||||||
"definitions": {},
|
|
||||||
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"replicaCount",
|
|
||||||
"minikube",
|
|
||||||
"image",
|
|
||||||
"imagePullSecrets",
|
|
||||||
"nameOverride",
|
|
||||||
"fullnameOverride",
|
|
||||||
"ingressController",
|
|
||||||
"serviceAccount",
|
|
||||||
"podAnnotations",
|
|
||||||
"podSecurityContext",
|
|
||||||
"securityContext",
|
|
||||||
"resources",
|
|
||||||
"nodeSelector",
|
|
||||||
"tolerations",
|
|
||||||
"affinity"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"replicaCount": {
|
|
||||||
"$id": "#/properties/replicaCount",
|
|
||||||
"type": "number"
|
|
||||||
},
|
|
||||||
"minikube": {
|
|
||||||
"$id": "#/properties/minikube",
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"image": {
|
|
||||||
"$id": "#/properties/image",
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"repository",
|
|
||||||
"tag",
|
|
||||||
"pullPolicy"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"repository": {
|
|
||||||
"$id": "#/properties/image/properties/repository",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"tag": {
|
|
||||||
"$id": "#/properties/image/properties/tag",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"pullPolicy": {
|
|
||||||
"$id": "#/properties/image/properties/pullPolicy",
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"Always",
|
|
||||||
"IfNotPresent",
|
|
||||||
"Never"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"imagePullSecrets": {
|
|
||||||
"$id": "#/properties/imagePullSecrets",
|
|
||||||
"type": "array"
|
|
||||||
},
|
|
||||||
"nameOverride": {
|
|
||||||
"$id": "#/properties/nameOverride",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"fullnameOverride": {
|
|
||||||
"$id": "#/properties/fullnameOverride",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"loadBalancer": {
|
|
||||||
"$id": "#/properties/loadBalancer",
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"enabled"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"enabled": {
|
|
||||||
"$id": "#/properties/loadBalancer/properties/enabled",
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"annotations": {
|
|
||||||
"$id": "#/properties/loadBalancer/properties/annotations",
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"labels": {
|
|
||||||
"$id": "#/properties/loadBalancer/properties/labels",
|
|
||||||
"type": "object",
|
|
||||||
"additionalProperties": {
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ingressController": {
|
|
||||||
"$id": "#/properties/ingressController",
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"rbac",
|
|
||||||
"config",
|
|
||||||
"watchNamespace"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"rbac": {
|
|
||||||
"$id": "#/properties/ingressController/properties/rbac",
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"create"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"create": {
|
|
||||||
"$id": "#/properties/ingressController/properties/rbac/properties/create",
|
|
||||||
"type": "boolean"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"leaseId": {
|
|
||||||
"$id": "#/properties/ingressController/properties/leaseId",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"config": {
|
|
||||||
"$id": "#/properties/ingressController/properties/config",
|
|
||||||
"type": "object",
|
|
||||||
"properties": {
|
|
||||||
"acmeCA": {
|
|
||||||
"$id": "#/properties/ingressController/properties/config/properties/acmeCA",
|
|
||||||
"type": "string",
|
|
||||||
"oneOf": [
|
|
||||||
{
|
|
||||||
"format": "uri"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"maxLength": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"acmeEABKeyId": {
|
|
||||||
"$id": "#/properties/ingressController/properties/config/properties/acmeEABKeyId",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"acmeEABMacKey": {
|
|
||||||
"$id": "#/properties/ingressController/properties/config/properties/acmeEABMacKey",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"debug": {
|
|
||||||
"$id": "#/properties/ingressController/properties/config/properties/debug",
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"email": {
|
|
||||||
"$id": "#/properties/ingressController/properties/config/properties/email",
|
|
||||||
"type": "string",
|
|
||||||
"oneOf": [
|
|
||||||
{
|
|
||||||
"format": "email"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"maxLength": 0
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"experimentalSmartSort": {
|
|
||||||
"$id": "#/properties/ingressController/properties/config/properties/experimentalSmartSort",
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"metrics": {
|
|
||||||
"$id": "#/properties/ingressController/properties/config/properties/metrics",
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"proxyProtocol": {
|
|
||||||
"$id": "#/properties/ingressController/properties/config/properties/proxyProtocol",
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"onDemandTLS": {
|
|
||||||
"$id": "#/properties/ingressController/properties/config/properties/onDemandTLS",
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"onDemandAsk": {
|
|
||||||
"$id": "#/properties/ingressController/properties/config/properties/onDemandAsk",
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"verbose": {
|
|
||||||
"$id": "#/properties/ingressController/properties/verbose",
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"watchNamespace": {
|
|
||||||
"$id": "#/properties/ingressController/properties/watchNamespace",
|
|
||||||
"type": "string"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"service": {
|
|
||||||
"$id": "#/properties/service",
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"type",
|
|
||||||
"ipDualStack"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"internalTrafficPolicy": {
|
|
||||||
"$id": "#/properties/service/properties/internalTrafficPolicy",
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"Cluster",
|
|
||||||
"Local"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"externalTrafficPolicy": {
|
|
||||||
"$id": "#/properties/service/properties/externalTrafficPolicy",
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"Cluster",
|
|
||||||
"Local"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"annotations": {
|
|
||||||
"$id": "#/properties/service/properties/annotations",
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"type": {
|
|
||||||
"$id": "#/properties/service/properties/type",
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"ClusterIP",
|
|
||||||
"NodePort",
|
|
||||||
"LoadBalancer",
|
|
||||||
"ExternalName"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"clusterIP": {
|
|
||||||
"$id": "#/properties/service/properties/clusterIP",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"ipDualStack": {
|
|
||||||
"$id": "#/properties/service/properties/name",
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"enabled"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"enabled": {
|
|
||||||
"$id": "#/properties/service/properties/ipDualStack/properties/enabled",
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"ipFamilies": {
|
|
||||||
"$id": "#/properties/service/properties/ipDualStack/properties/ipFamilies",
|
|
||||||
"type": "array",
|
|
||||||
"items": {
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"IPv4",
|
|
||||||
"IPv6"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ipFamilyPolicy": {
|
|
||||||
"$id": "#/properties/service/properties/ipDualStack/properties/ipFamilyPolicy",
|
|
||||||
"type": "string",
|
|
||||||
"enum": [
|
|
||||||
"SingleStack",
|
|
||||||
"PreferDualStack",
|
|
||||||
"RequireDualStack"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"serviceAccount": {
|
|
||||||
"$id": "#/properties/serviceAccount",
|
|
||||||
"type": "object",
|
|
||||||
"required": [
|
|
||||||
"create",
|
|
||||||
"name"
|
|
||||||
],
|
|
||||||
"properties": {
|
|
||||||
"create": {
|
|
||||||
"$id": "#/properties/serviceAccount/properties/create",
|
|
||||||
"type": "boolean"
|
|
||||||
},
|
|
||||||
"name": {
|
|
||||||
"$id": "#/properties/serviceAccount/properties/name",
|
|
||||||
"type": "string"
|
|
||||||
},
|
|
||||||
"annotations": {
|
|
||||||
"$id": "#/properties/serviceAccount/properties/annotations",
|
|
||||||
"type": "object"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"podAnnotations": {
|
|
||||||
"$id": "#/properties/podAnnotations",
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"podSecurityContext": {
|
|
||||||
"$id": "#/properties/podSecurityContext",
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"securityContext": {
|
|
||||||
"$id": "#/properties/securityContext",
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"resources": {
|
|
||||||
"$id": "#/properties/resources",
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"nodeSelector": {
|
|
||||||
"$id": "#/properties/nodeSelector",
|
|
||||||
"type": "object"
|
|
||||||
},
|
|
||||||
"tolerations": {
|
|
||||||
"$id": "#/properties/tolerations",
|
|
||||||
"type": "array"
|
|
||||||
},
|
|
||||||
"affinity": {
|
|
||||||
"$id": "#/properties/affinity",
|
|
||||||
"type": "object"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,120 +0,0 @@
|
|||||||
# Default values for caddy-ingress-controller.
|
|
||||||
# This is a YAML-formatted file.
|
|
||||||
# Declare variables to be passed into your templates.
|
|
||||||
replicaCount: 2
|
|
||||||
|
|
||||||
# Use to test in minikube context
|
|
||||||
minikube: false
|
|
||||||
|
|
||||||
image:
|
|
||||||
repository: caddy/ingress
|
|
||||||
pullPolicy: IfNotPresent
|
|
||||||
tag: "v0.2.1"
|
|
||||||
|
|
||||||
imagePullSecrets: []
|
|
||||||
nameOverride: ""
|
|
||||||
fullnameOverride: ""
|
|
||||||
|
|
||||||
# Default values for the caddy ingress controller.
|
|
||||||
ingressController:
|
|
||||||
watchNamespace: ""
|
|
||||||
verbose: false
|
|
||||||
rbac:
|
|
||||||
create: true
|
|
||||||
className: "caddy"
|
|
||||||
classNameRequired: false
|
|
||||||
leaseId: ""
|
|
||||||
config:
|
|
||||||
acmeEABKeyId: ""
|
|
||||||
acmeEABMacKey: ""
|
|
||||||
# -- Acme Server URL
|
|
||||||
acmeCA: ""
|
|
||||||
debug: false
|
|
||||||
email: ""
|
|
||||||
metrics: true
|
|
||||||
proxyProtocol: false
|
|
||||||
experimentalSmartSort: false
|
|
||||||
onDemandTLS: false
|
|
||||||
# onDemandAsk:
|
|
||||||
|
|
||||||
loadBalancer:
|
|
||||||
enabled: true
|
|
||||||
# Deprecated in Kubernetes v1.24
|
|
||||||
loadBalancerIP:
|
|
||||||
# Set to 'Local' to maintain the client's IP on inbound connections
|
|
||||||
externalTrafficPolicy:
|
|
||||||
annotations: {}
|
|
||||||
# service.beta.kubernetes.io/aws-load-balancer-type:
|
|
||||||
# service.beta.kubernetes.io/aws-load-balancer-nlb-target-type:
|
|
||||||
# service.beta.kubernetes.io/aws-load-balancer-scheme:
|
|
||||||
# service.beta.kubernetes.io/aws-load-balancer-eip-allocations:
|
|
||||||
# service.beta.kubernetes.io/aws-load-balancer-subnets:
|
|
||||||
labels: {}
|
|
||||||
|
|
||||||
service:
|
|
||||||
# Set to 'Local' to maintain the client's IP on inbound connections
|
|
||||||
externalTrafficPolicy: Cluster
|
|
||||||
# Enable internal traffic policy for the service
|
|
||||||
internalTrafficPolicy: Cluster
|
|
||||||
annotations: {}
|
|
||||||
# service.beta.kubernetes.io/aws-load-balancer-type:
|
|
||||||
# service.beta.kubernetes.io/aws-load-balancer-nlb-target-type:
|
|
||||||
# service.beta.kubernetes.io/aws-load-balancer-scheme:
|
|
||||||
# service.beta.kubernetes.io/aws-load-balancer-eip-allocations:
|
|
||||||
# service.beta.kubernetes.io/aws-load-balancer-subnets:
|
|
||||||
## Service type
|
|
||||||
type: ClusterIP
|
|
||||||
## IP address for type ClusterIP
|
|
||||||
clusterIP: ""
|
|
||||||
ipDualStack:
|
|
||||||
enabled: false
|
|
||||||
ipFamilies: ["IPv6", "IPv4"]
|
|
||||||
ipFamilyPolicy: "PreferDualStack"
|
|
||||||
|
|
||||||
serviceAccount:
|
|
||||||
# Specifies whether a service account should be created
|
|
||||||
create: true
|
|
||||||
# Annotations to add to the service account
|
|
||||||
annotations: {}
|
|
||||||
# The name of the service account to use.
|
|
||||||
# If not set and create is true, a name is generated using the fullname template
|
|
||||||
name: "caddy-ingress-controller"
|
|
||||||
|
|
||||||
podAnnotations: {}
|
|
||||||
|
|
||||||
podSecurityContext:
|
|
||||||
{}
|
|
||||||
# fsGroup: 2000
|
|
||||||
|
|
||||||
podDisruptionBudget:
|
|
||||||
minAvailable: 1
|
|
||||||
maxUnavailable:
|
|
||||||
|
|
||||||
securityContext:
|
|
||||||
allowPrivilegeEscalation: true
|
|
||||||
capabilities:
|
|
||||||
drop:
|
|
||||||
- ALL
|
|
||||||
add:
|
|
||||||
- NET_BIND_SERVICE
|
|
||||||
runAsUser: 0
|
|
||||||
runAsGroup: 0
|
|
||||||
|
|
||||||
resources:
|
|
||||||
{}
|
|
||||||
# We usually recommend not to specify default resources and to leave this as a conscious
|
|
||||||
# choice for the user. This also increases chances charts run on environments with little
|
|
||||||
# resources, such as Minikube. If you do want to specify resources, uncomment the following
|
|
||||||
# lines, adjust them as necessary, and remove the curly braces after 'resources:'.
|
|
||||||
# limits:
|
|
||||||
# cpu: 100m
|
|
||||||
# memory: 128Mi
|
|
||||||
# requests:
|
|
||||||
# cpu: 100m
|
|
||||||
# memory: 128Mi
|
|
||||||
|
|
||||||
nodeSelector: {}
|
|
||||||
|
|
||||||
tolerations: []
|
|
||||||
|
|
||||||
affinity: {}
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/caddyserver/ingress/pkg/store"
|
|
||||||
)
|
|
||||||
|
|
||||||
func parseFlags() store.Options {
|
|
||||||
var namespace string
|
|
||||||
flag.StringVar(&namespace, "namespace", "", "the namespace that you would like to observe kubernetes ingress resources in.")
|
|
||||||
|
|
||||||
var className string
|
|
||||||
flag.StringVar(&className, "class-name", "caddy", "class name of the ingress controller")
|
|
||||||
|
|
||||||
var classNameRequired bool
|
|
||||||
flag.BoolVar(&classNameRequired, "class-name-required", false, "only allow ingress resources with a matching ingress class name")
|
|
||||||
|
|
||||||
var configMapName string
|
|
||||||
flag.StringVar(&configMapName, "config-map", "", "defines the config map name from where to load global options")
|
|
||||||
|
|
||||||
var leaseId string
|
|
||||||
flag.StringVar(&leaseId, "lease-id", "", "defines the id of this instance for certmagic lock")
|
|
||||||
|
|
||||||
var verbose bool
|
|
||||||
flag.BoolVar(&verbose, "verbose", false, "set the log level to debug")
|
|
||||||
|
|
||||||
var pluginsOrder string
|
|
||||||
flag.StringVar(&pluginsOrder, "plugins-order", "", "defines the order plugins should be used")
|
|
||||||
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
return store.Options{
|
|
||||||
WatchNamespace: namespace,
|
|
||||||
ClassName: className,
|
|
||||||
ClassNameRequired: classNameRequired,
|
|
||||||
ConfigMapName: configMapName,
|
|
||||||
Verbose: verbose,
|
|
||||||
LeaseId: leaseId,
|
|
||||||
PluginsOrder: strings.Split(pluginsOrder, ","),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,126 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/caddyserver/ingress/internal/caddy"
|
|
||||||
"github.com/caddyserver/ingress/internal/controller"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
v1 "k8s.io/api/core/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
|
||||||
"k8s.io/apimachinery/pkg/version"
|
|
||||||
"k8s.io/client-go/kubernetes"
|
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// high enough QPS to fit all expected use cases.
|
|
||||||
highQPS = 1e6
|
|
||||||
|
|
||||||
// high enough Burst to fit all expected use cases.
|
|
||||||
highBurst = 1e6
|
|
||||||
)
|
|
||||||
|
|
||||||
func createLogger(verbose bool) *zap.SugaredLogger {
|
|
||||||
prodCfg := zap.NewProductionConfig()
|
|
||||||
|
|
||||||
if verbose {
|
|
||||||
prodCfg.Level = zap.NewAtomicLevelAt(zap.DebugLevel)
|
|
||||||
}
|
|
||||||
logger, _ := prodCfg.Build()
|
|
||||||
|
|
||||||
return logger.Sugar()
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// parse any flags required to configure the caddy ingress controller
|
|
||||||
cfg := parseFlags()
|
|
||||||
|
|
||||||
logger := createLogger(cfg.Verbose)
|
|
||||||
|
|
||||||
if cfg.WatchNamespace == "" {
|
|
||||||
cfg.WatchNamespace = v1.NamespaceAll
|
|
||||||
logger.Warn("-namespace flag is unset, caddy ingress controller will monitor ingress resources in all namespaces.")
|
|
||||||
}
|
|
||||||
|
|
||||||
// get client to access the kubernetes service api
|
|
||||||
kubeClient, _, err := createApiserverClient(logger)
|
|
||||||
if err != nil {
|
|
||||||
logger.Fatalf("Could not establish a connection to the Kubernetes API Server. %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
stopCh := make(chan struct{}, 1)
|
|
||||||
|
|
||||||
c := controller.NewCaddyController(logger, kubeClient, cfg, caddy.Converter{}, stopCh)
|
|
||||||
|
|
||||||
// start the ingress controller
|
|
||||||
logger.Info("Starting the caddy ingress controller")
|
|
||||||
go c.Run()
|
|
||||||
|
|
||||||
// Listen for SIGINT and SIGTERM signals
|
|
||||||
sigs := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(sigs, os.Interrupt, syscall.SIGTERM)
|
|
||||||
<-sigs
|
|
||||||
close(stopCh)
|
|
||||||
|
|
||||||
// Let controller exit the process
|
|
||||||
select {}
|
|
||||||
}
|
|
||||||
|
|
||||||
// createApiserverClient creates a new Kubernetes REST client. We assume the
|
|
||||||
// controller runs inside Kubernetes and use the in-cluster config.
|
|
||||||
func createApiserverClient(logger *zap.SugaredLogger) (*kubernetes.Clientset, *version.Info, error) {
|
|
||||||
cfg, err := clientcmd.BuildConfigFromFlags("", "")
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.Infof("Creating API client for %s", cfg.Host)
|
|
||||||
|
|
||||||
cfg.QPS = highQPS
|
|
||||||
cfg.Burst = highBurst
|
|
||||||
cfg.ContentType = "application/vnd.kubernetes.protobuf"
|
|
||||||
|
|
||||||
client, err := kubernetes.NewForConfig(cfg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// The client may fail to connect to the API server on the first request
|
|
||||||
defaultRetry := wait.Backoff{
|
|
||||||
Steps: 10,
|
|
||||||
Duration: 1 * time.Second,
|
|
||||||
Factor: 1.5,
|
|
||||||
Jitter: 0.1,
|
|
||||||
}
|
|
||||||
|
|
||||||
var v *version.Info
|
|
||||||
var retries int
|
|
||||||
var lastErr error
|
|
||||||
|
|
||||||
err = wait.ExponentialBackoff(defaultRetry, func() (bool, error) {
|
|
||||||
v, err = client.Discovery().ServerVersion()
|
|
||||||
if err == nil {
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
lastErr = err
|
|
||||||
logger.Infof("Unexpected error discovering Kubernetes version (attempt %v): %v", retries, err)
|
|
||||||
retries++
|
|
||||||
return false, nil
|
|
||||||
})
|
|
||||||
|
|
||||||
// err is returned in case of timeout in the exponential backoff (ErrWaitTimeout)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, lastErr
|
|
||||||
}
|
|
||||||
|
|
||||||
if retries > 0 {
|
|
||||||
logger.Warnf("Initial connection to the Kubernetes API server was retried %d times.", retries)
|
|
||||||
}
|
|
||||||
|
|
||||||
return client, v, nil
|
|
||||||
}
|
|
||||||
@ -1,3 +0,0 @@
|
|||||||
# See https://github.com/helm/chart-testing#configuration
|
|
||||||
all: true
|
|
||||||
helm-extra-args: --timeout 300s
|
|
||||||
@ -1,152 +0,0 @@
|
|||||||
module github.com/caddyserver/ingress
|
|
||||||
|
|
||||||
go 1.24.0
|
|
||||||
require (
|
|
||||||
github.com/caddyserver/caddy/v2 v2.9.1
|
|
||||||
github.com/caddyserver/certmagic v0.22.2
|
|
||||||
github.com/google/uuid v1.6.0
|
|
||||||
github.com/mholt/acmez/v3 v3.1.1
|
|
||||||
github.com/mitchellh/mapstructure v1.5.0
|
|
||||||
github.com/pires/go-proxyproto v0.8.0
|
|
||||||
github.com/stretchr/testify v1.10.0
|
|
||||||
go.uber.org/zap v1.27.0
|
|
||||||
gopkg.in/go-playground/pool.v3 v3.1.1
|
|
||||||
k8s.io/api v0.32.3
|
|
||||||
k8s.io/apimachinery v0.32.3
|
|
||||||
k8s.io/client-go v0.32.3
|
|
||||||
)
|
|
||||||
|
|
||||||
require (
|
|
||||||
dario.cat/mergo v1.0.1 // indirect
|
|
||||||
filippo.io/edwards25519 v1.1.0 // indirect
|
|
||||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 // indirect
|
|
||||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
|
||||||
github.com/Masterminds/semver/v3 v3.3.0 // indirect
|
|
||||||
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
|
|
||||||
github.com/Microsoft/go-winio v0.6.1 // indirect
|
|
||||||
github.com/antlr4-go/antlr/v4 v4.13.0 // indirect
|
|
||||||
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b // indirect
|
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
|
||||||
github.com/caddyserver/zerossl v0.1.3 // indirect
|
|
||||||
github.com/cespare/xxhash v1.1.0 // indirect
|
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
|
||||||
github.com/chzyer/readline v1.5.1 // indirect
|
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect
|
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
|
||||||
github.com/dgraph-io/badger v1.6.2 // indirect
|
|
||||||
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
|
|
||||||
github.com/dgraph-io/ristretto v0.1.1 // indirect
|
|
||||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
|
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
|
||||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
|
||||||
github.com/francoispqt/gojay v1.2.13 // indirect
|
|
||||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
|
||||||
github.com/go-jose/go-jose/v3 v3.0.4 // indirect
|
|
||||||
github.com/go-kit/kit v0.13.0 // indirect
|
|
||||||
github.com/go-kit/log v0.2.1 // indirect
|
|
||||||
github.com/go-logfmt/logfmt v0.6.0 // indirect
|
|
||||||
github.com/go-logr/logr v1.4.2 // indirect
|
|
||||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
|
||||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
|
||||||
github.com/go-openapi/swag v0.23.0 // indirect
|
|
||||||
github.com/go-sql-driver/mysql v1.7.1 // indirect
|
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
|
|
||||||
github.com/gogo/protobuf v1.3.2 // indirect
|
|
||||||
github.com/golang/glog v1.2.4 // indirect
|
|
||||||
github.com/golang/protobuf v1.5.4 // indirect
|
|
||||||
github.com/golang/snappy v0.0.4 // indirect
|
|
||||||
github.com/google/cel-go v0.21.0 // indirect
|
|
||||||
github.com/google/gnostic-models v0.6.8 // indirect
|
|
||||||
github.com/google/go-cmp v0.6.0 // indirect
|
|
||||||
github.com/google/gofuzz v1.2.0 // indirect
|
|
||||||
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db // indirect
|
|
||||||
github.com/huandu/xstrings v1.5.0 // indirect
|
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
|
||||||
github.com/jackc/chunkreader/v2 v2.0.1 // indirect
|
|
||||||
github.com/jackc/pgconn v1.14.3 // indirect
|
|
||||||
github.com/jackc/pgio v1.0.0 // indirect
|
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
|
||||||
github.com/jackc/pgproto3/v2 v2.3.3 // indirect
|
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
|
||||||
github.com/jackc/pgtype v1.14.0 // indirect
|
|
||||||
github.com/jackc/pgx/v4 v4.18.3 // indirect
|
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
|
||||||
github.com/klauspost/compress v1.17.11 // indirect
|
|
||||||
github.com/klauspost/cpuid/v2 v2.2.10 // indirect
|
|
||||||
github.com/libdns/libdns v0.2.3 // indirect
|
|
||||||
github.com/mailru/easyjson v0.7.7 // indirect
|
|
||||||
github.com/manifoldco/promptui v0.9.0 // indirect
|
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
|
|
||||||
github.com/miekg/dns v1.1.63 // indirect
|
|
||||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
|
||||||
github.com/mitchellh/go-ps v1.0.0 // indirect
|
|
||||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
|
||||||
github.com/onsi/ginkgo/v2 v2.21.0 // indirect
|
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
|
||||||
github.com/prometheus/client_golang v1.19.1 // indirect
|
|
||||||
github.com/prometheus/client_model v0.5.0 // indirect
|
|
||||||
github.com/prometheus/common v0.48.0 // indirect
|
|
||||||
github.com/prometheus/procfs v0.12.0 // indirect
|
|
||||||
github.com/quic-go/qpack v0.5.1 // indirect
|
|
||||||
github.com/quic-go/quic-go v0.48.2 // indirect
|
|
||||||
github.com/rs/xid v1.5.0 // indirect
|
|
||||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
|
||||||
github.com/shopspring/decimal v1.4.0 // indirect
|
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
|
|
||||||
github.com/slackhq/nebula v1.7.2 // indirect
|
|
||||||
github.com/smallstep/certificates v0.26.1 // indirect
|
|
||||||
github.com/smallstep/nosql v0.6.1 // indirect
|
|
||||||
github.com/smallstep/pkcs7 v0.0.0-20231024181729-3b98ecc1ca81 // indirect
|
|
||||||
github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d // indirect
|
|
||||||
github.com/smallstep/truststore v0.13.0 // indirect
|
|
||||||
github.com/spf13/cast v1.7.0 // indirect
|
|
||||||
github.com/spf13/cobra v1.8.1 // indirect
|
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
|
||||||
github.com/stoewer/go-strcase v1.3.0 // indirect
|
|
||||||
github.com/tailscale/tscert v0.0.0-20240608151842-d3f834017e53 // indirect
|
|
||||||
github.com/urfave/cli v1.22.14 // indirect
|
|
||||||
github.com/x448/float16 v0.8.4 // indirect
|
|
||||||
github.com/zeebo/blake3 v0.2.4 // indirect
|
|
||||||
go.etcd.io/bbolt v1.3.9 // indirect
|
|
||||||
go.step.sm/cli-utils v0.9.0 // indirect
|
|
||||||
go.step.sm/crypto v0.45.0 // indirect
|
|
||||||
go.step.sm/linkedca v0.20.1 // indirect
|
|
||||||
go.uber.org/automaxprocs v1.6.0 // indirect
|
|
||||||
go.uber.org/mock v0.4.0 // indirect
|
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
|
||||||
go.uber.org/zap/exp v0.3.0 // indirect
|
|
||||||
golang.org/x/crypto v0.36.0 // indirect
|
|
||||||
golang.org/x/crypto/x509roots/fallback v0.0.0-20241104001025-71ed71b4faf9 // indirect
|
|
||||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
|
|
||||||
golang.org/x/mod v0.24.0 // indirect
|
|
||||||
golang.org/x/net v0.37.0 // indirect
|
|
||||||
golang.org/x/oauth2 v0.23.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.7.0 // indirect
|
|
||||||
golang.org/x/tools v0.31.0 // indirect
|
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 // indirect
|
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect
|
|
||||||
google.golang.org/grpc v1.67.1 // indirect
|
|
||||||
google.golang.org/protobuf v1.35.1 // indirect
|
|
||||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
|
||||||
gopkg.in/go-playground/assert.v1 v1.2.1 // indirect
|
|
||||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
|
||||||
howett.net/plist v1.0.0 // indirect
|
|
||||||
k8s.io/klog/v2 v2.130.1 // indirect
|
|
||||||
k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect
|
|
||||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
|
|
||||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
|
||||||
sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect
|
|
||||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
|
||||||
)
|
|
||||||
@ -1,806 +0,0 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
|
||||||
cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
|
||||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
|
||||||
cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo=
|
|
||||||
cloud.google.com/go v0.112.1 h1:uJSeirPke5UNZHIb4SxfZklVSiWWVqW4oXlETwZziwM=
|
|
||||||
cloud.google.com/go/auth v0.4.1 h1:Z7YNIhlWRtrnKlZke7z3GMqzvuYzdc2z98F9D1NV5Hg=
|
|
||||||
cloud.google.com/go/auth v0.4.1/go.mod h1:QVBuVEKpCn4Zp58hzRGvL0tjRGU0YqdRTdCHM1IHnro=
|
|
||||||
cloud.google.com/go/auth/oauth2adapt v0.2.2 h1:+TTV8aXpjeChS9M+aTtN/TjdQnzJvmzKFt//oWu7HX4=
|
|
||||||
cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q=
|
|
||||||
cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
|
|
||||||
cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
|
|
||||||
cloud.google.com/go/iam v1.1.8 h1:r7umDwhj+BQyz0ScZMp4QrGXjSTI3ZINnpgU2nlB/K0=
|
|
||||||
cloud.google.com/go/iam v1.1.8/go.mod h1:GvE6lyMmfxXauzNq8NbgJbeVQNspG+tcdL/W8QO1+zE=
|
|
||||||
cloud.google.com/go/kms v1.16.0 h1:1yZsRPhmargZOmY+fVAh8IKiR9HzCb0U1zsxb5g2nRY=
|
|
||||||
cloud.google.com/go/kms v1.16.0/go.mod h1:olQUXy2Xud+1GzYfiBO9N0RhjsJk5IJLU6n/ethLXVc=
|
|
||||||
cloud.google.com/go/longrunning v0.5.7 h1:WLbHekDbjK1fVFD3ibpFFVoyizlLRl73I7YKuAKilhU=
|
|
||||||
cloud.google.com/go/longrunning v0.5.7/go.mod h1:8GClkudohy1Fxm3owmBGid8W0pSgodEMwEAztp38Xng=
|
|
||||||
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
|
|
||||||
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
|
|
||||||
dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU=
|
|
||||||
dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU=
|
|
||||||
dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4=
|
|
||||||
dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU=
|
|
||||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
|
||||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
|
||||||
git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg=
|
|
||||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96 h1:cTp8I5+VIoKjsnZuH8vjyaysT/ses3EvZeaV/1UkF2M=
|
|
||||||
github.com/AndreasBriese/bbloom v0.0.0-20190825152654-46b345b51c96/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
|
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
|
||||||
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
|
||||||
github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI=
|
|
||||||
github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=
|
|
||||||
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
|
|
||||||
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
|
|
||||||
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
|
||||||
github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe3tPhs=
|
|
||||||
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
|
|
||||||
github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=
|
|
||||||
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
|
|
||||||
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
|
|
||||||
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
|
|
||||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
|
||||||
github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c=
|
|
||||||
github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI=
|
|
||||||
github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g=
|
|
||||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
|
||||||
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b h1:uUXgbcPDK3KpW29o4iy7GtuappbWT0l5NaMo9H9pJDw=
|
|
||||||
github.com/aryann/difflib v0.0.0-20210328193216-ff5ff6dc229b/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
|
||||||
github.com/aws/aws-sdk-go-v2 v1.26.1 h1:5554eUqIYVWpU0YmeeYZ0wU64H2VLBs8TlhRB2L+EkA=
|
|
||||||
github.com/aws/aws-sdk-go-v2 v1.26.1/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM=
|
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.27.13 h1:WbKW8hOzrWoOA/+35S5okqO/2Ap8hkkFUzoW8Hzq24A=
|
|
||||||
github.com/aws/aws-sdk-go-v2/config v1.27.13/go.mod h1:XLiyiTMnguytjRER7u5RIkhIqS8Nyz41SwAWb4xEjxs=
|
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.13 h1:XDCJDzk/u5cN7Aple7D/MiAhx1Rjo/0nueJ0La8mRuE=
|
|
||||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.13/go.mod h1:FMNcjQrmuBYvOTZDtOLCIu0esmxjF7RuA/89iSXWzQI=
|
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1 h1:FVJ0r5XTHSmIHJV6KuDmdYhEpvlHpiSd38RQWhut5J4=
|
|
||||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.1/go.mod h1:zusuAeqezXzAB24LGuzuekqMAEgWkVYukBec3kr3jUg=
|
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5 h1:aw39xVGeRWlWx9EzGVnhOR4yOjQDHPQ6o6NmBlscyQg=
|
|
||||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.5/go.mod h1:FSaRudD0dXiMPK2UjknVwwTYyZMRsHv3TtkabsZih5I=
|
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5 h1:PG1F3OD1szkuQPzDw3CIQsRIrtTlUC3lP84taWzHlq0=
|
|
||||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.5/go.mod h1:jU1li6RFryMz+so64PpKtudI+QzbKoIEivqdf6LNpOc=
|
|
||||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
|
|
||||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
|
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 h1:Ji0DY1xUsUr3I8cHps0G+XM3WWU16lP6yG8qu1GAZAs=
|
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2/go.mod h1:5CsjAbs3NlGQyZNFACh+zztPDI7fU6eW9QsxjfnuBKg=
|
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7 h1:ogRAwT1/gxJBcSWDMZlgyFUM962F51A5CRhDLbxLdmo=
|
|
||||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.7/go.mod h1:YCsIZhXfRPLFFCl5xxY+1T9RKzOKjCut+28JSX2DnAk=
|
|
||||||
github.com/aws/aws-sdk-go-v2/service/kms v1.31.1 h1:5wtyAwuUiJiM3DHYeGZmP5iMonM7DFBWAEaaVPHYZA0=
|
|
||||||
github.com/aws/aws-sdk-go-v2/service/kms v1.31.1/go.mod h1:2snWQJQUKsbN66vAawJuOGX7dr37pfOq9hb0tZDGIqQ=
|
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.20.6 h1:o5cTaeunSpfXiLTIBx5xo2enQmiChtu1IBbzXnfU9Hs=
|
|
||||||
github.com/aws/aws-sdk-go-v2/service/sso v1.20.6/go.mod h1:qGzynb/msuZIE8I75DVRCUXw3o3ZyBmUvMwQ2t/BrGM=
|
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.0 h1:Qe0r0lVURDDeBQJ4yP+BOrJkvkiCo/3FH/t+wY11dmw=
|
|
||||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.0/go.mod h1:mUYPBhaF2lGiukDEjJX2BLRRKTmoUSitGDUgM4tRxak=
|
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.28.7 h1:et3Ta53gotFR4ERLXXHIHl/Uuk1qYpP5uU7cvNql8ns=
|
|
||||||
github.com/aws/aws-sdk-go-v2/service/sts v1.28.7/go.mod h1:FZf1/nKNEkHdGGJP/cI2MoIMquumuRK6ol3QQJNDxmw=
|
|
||||||
github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q=
|
|
||||||
github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
|
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
|
||||||
github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g=
|
|
||||||
github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
|
|
||||||
github.com/caddyserver/caddy/v2 v2.9.1 h1:OEYiZ7DbCzAWVb6TNEkjRcSCRGHVoZsJinoDR/n9oaY=
|
|
||||||
github.com/caddyserver/caddy/v2 v2.9.1/go.mod h1:ImUELya2el1FDVp3ahnSO2iH1or1aHxlQEQxd/spP68=
|
|
||||||
github.com/caddyserver/certmagic v0.22.2 h1:qzZURXlrxwR5m25/jpvVeEyJHeJJMvAwe5zlMufOTQk=
|
|
||||||
github.com/caddyserver/certmagic v0.22.2/go.mod h1:hbqE7BnkjhX5IJiFslPmrSeobSeZvI6ux8tyxhsd6qs=
|
|
||||||
github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA=
|
|
||||||
github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4=
|
|
||||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
|
||||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
|
||||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
|
||||||
github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
|
|
||||||
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
|
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
|
||||||
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
|
|
||||||
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
|
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
|
||||||
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
|
|
||||||
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
|
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
|
||||||
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
|
||||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
|
||||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
|
||||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
|
||||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
|
||||||
github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
|
||||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
|
||||||
github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
|
||||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4=
|
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
|
||||||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
|
||||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
|
||||||
github.com/dgraph-io/badger v1.6.2 h1:mNw0qs90GVgGGWylh0umH5iag1j6n/PeJtNvL6KY/x8=
|
|
||||||
github.com/dgraph-io/badger v1.6.2/go.mod h1:JW2yswe3V058sS0kZ2h/AXeDSqFjxnZcRrVH//y2UQE=
|
|
||||||
github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o=
|
|
||||||
github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk=
|
|
||||||
github.com/dgraph-io/ristretto v0.0.2/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
|
||||||
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
|
|
||||||
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
|
|
||||||
github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA=
|
|
||||||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
|
||||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
|
|
||||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
|
||||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
|
||||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
|
||||||
github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g=
|
|
||||||
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
|
||||||
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
|
|
||||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
|
||||||
github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc=
|
|
||||||
github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk=
|
|
||||||
github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY=
|
|
||||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
|
||||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
|
||||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
|
||||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
|
||||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
|
||||||
github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0=
|
|
||||||
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
|
|
||||||
github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY=
|
|
||||||
github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
|
|
||||||
github.com/go-kit/kit v0.4.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
|
||||||
github.com/go-kit/kit v0.13.0 h1:OoneCcHKHQ03LfBpoQCUfCluwd2Vt3ohz+kvbJneZAU=
|
|
||||||
github.com/go-kit/kit v0.13.0/go.mod h1:phqEHMMUbyrCFCTgH48JueqrM3md2HcAZ8N3XE4FKDg=
|
|
||||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
|
||||||
github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
|
|
||||||
github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
|
|
||||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
|
||||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
|
||||||
github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4=
|
|
||||||
github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
|
||||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
|
||||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
|
||||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
|
||||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
|
||||||
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
|
|
||||||
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.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
|
|
||||||
github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k=
|
|
||||||
github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14=
|
|
||||||
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-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
|
||||||
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
|
||||||
github.com/go-stack/stack v1.6.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
|
||||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
|
||||||
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
|
|
||||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
|
||||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
|
||||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
|
||||||
github.com/golang/glog v1.2.4 h1:CNNw5U8lSiiBk7druxtSHHTsRWcxKoac6kZKm2peBBc=
|
|
||||||
github.com/golang/glog v1.2.4/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
|
||||||
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
|
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
|
||||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
|
||||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
|
||||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
|
||||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
|
||||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
|
||||||
github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=
|
|
||||||
github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
|
|
||||||
github.com/google/cel-go v0.21.0 h1:cl6uW/gxN+Hy50tNYvI691+sXxioCnstFzLp2WO4GCI=
|
|
||||||
github.com/google/cel-go v0.21.0/go.mod h1:rHUlWCcBKgyEk+eV03RPdZUekPp6YcJwV0FxuUksYxc=
|
|
||||||
github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745 h1:heyoXNxkRT155x4jTAiSv5BVSVkueifPUm+Q8LUXMRo=
|
|
||||||
github.com/google/certificate-transparency-go v1.1.8-0.20240110162603-74a5dd331745/go.mod h1:zN0wUQgV9LjwLZeFHnrAbQi8hzMVvEWePyk+MhPOk7k=
|
|
||||||
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I=
|
|
||||||
github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
|
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
|
||||||
github.com/google/go-cmp v0.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-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ=
|
|
||||||
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
|
|
||||||
github.com/google/go-tpm v0.9.0 h1:sQF6YqWMi+SCXpsmS3fd21oPy/vSddwZry4JnmltHVk=
|
|
||||||
github.com/google/go-tpm v0.9.0/go.mod h1:FkNVkc6C+IsvDI9Jw1OveJmxGZUUaKxtrpOS47QWKfU=
|
|
||||||
github.com/google/go-tpm-tools v0.4.4 h1:oiQfAIkc6xTy9Fl5NKTeTJkBTlXdHsxAofmQyxBKY98=
|
|
||||||
github.com/google/go-tpm-tools v0.4.4/go.mod h1:T8jXkp2s+eltnCDIsXR84/MTcVU9Ja7bh3Mit0pa4AY=
|
|
||||||
github.com/google/go-tspi v0.3.0 h1:ADtq8RKfP+jrTyIWIZDIYcKOMecRqNJFOew2IT0Inus=
|
|
||||||
github.com/google/go-tspi v0.3.0/go.mod h1:xfMGI3G0PhxCdNVcYr1C4C+EizojDg/TXuX5by8CiHI=
|
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
|
||||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
|
||||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
|
||||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
|
||||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
|
||||||
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
|
|
||||||
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
|
||||||
github.com/google/s2a-go v0.1.7 h1:60BLSyTrOV4/haCDW4zb1guZItoSq8foHCXrAnjBo/o=
|
|
||||||
github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw=
|
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2 h1:Vie5ybvEvT75RniqhfFxPRy3Bf7vr3h0cechB90XaQs=
|
|
||||||
github.com/googleapis/enterprise-certificate-proxy v0.3.2/go.mod h1:VLSiSSBs/ksPL8kq3OBOQ6WRI2QnaFynd1DCjZ62+V0=
|
|
||||||
github.com/googleapis/gax-go v2.0.0+incompatible h1:j0GKcs05QVmm7yesiZq2+9cxHkNK9YM6zKx4D2qucQU=
|
|
||||||
github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY=
|
|
||||||
github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg=
|
|
||||||
github.com/googleapis/gax-go/v2 v2.12.4 h1:9gWcmF85Wvq4ryPFvGFaOgPIs1AQX0d0bcbGw4Z96qg=
|
|
||||||
github.com/googleapis/gax-go/v2 v2.12.4/go.mod h1:KYEYLorsnIGDi/rPC8b5TdlB9kbKoFubselGIoBMCwI=
|
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
|
||||||
github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
|
|
||||||
github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw=
|
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
|
||||||
github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI=
|
|
||||||
github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE=
|
|
||||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
|
||||||
github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
|
|
||||||
github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
|
||||||
github.com/jackc/chunkreader/v2 v2.0.1 h1:i+RDz65UE+mmpjTfyz0MoVTnzeYxroil2G82ki7MGG8=
|
|
||||||
github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
|
|
||||||
github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
|
|
||||||
github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
|
|
||||||
github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
|
|
||||||
github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
|
|
||||||
github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
|
|
||||||
github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
|
|
||||||
github.com/jackc/pgconn v1.14.3 h1:bVoTr12EGANZz66nZPkMInAV/KHD2TxH9npjXXgiB3w=
|
|
||||||
github.com/jackc/pgconn v1.14.3/go.mod h1:RZbme4uasqzybK2RK5c65VsHxoyaml09lx3tXOcO/VM=
|
|
||||||
github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE=
|
|
||||||
github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
|
|
||||||
github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
|
|
||||||
github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
|
|
||||||
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65 h1:DadwsjnMwFjfWc9y5Wi/+Zz7xoE5ALHsRQlOctkOiHc=
|
|
||||||
github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
|
|
||||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
|
||||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
|
||||||
github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
|
|
||||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
|
|
||||||
github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
|
|
||||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
|
||||||
github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
|
|
||||||
github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
|
||||||
github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
|
||||||
github.com/jackc/pgproto3/v2 v2.3.3 h1:1HLSx5H+tXR9pW3in3zaztoEwQYRC9SQaYUHjTSUOag=
|
|
||||||
github.com/jackc/pgproto3/v2 v2.3.3/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
|
|
||||||
github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
|
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
|
||||||
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
|
||||||
github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
|
|
||||||
github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
|
|
||||||
github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
|
|
||||||
github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
|
|
||||||
github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw=
|
|
||||||
github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
|
|
||||||
github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
|
|
||||||
github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
|
|
||||||
github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
|
|
||||||
github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
|
|
||||||
github.com/jackc/pgx/v4 v4.18.3 h1:dE2/TrEsGX3RBprb3qryqSV9Y60iZN1C6i8IrmW9/BA=
|
|
||||||
github.com/jackc/pgx/v4 v4.18.3/go.mod h1:Ey4Oru5tH5sB6tV7hDmfWFahwF15Eb7DNXlRKx2CkVw=
|
|
||||||
github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
|
||||||
github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
|
||||||
github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
|
|
||||||
github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU=
|
|
||||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
|
||||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
|
||||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
|
||||||
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.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
|
|
||||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
|
||||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
|
||||||
github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
|
|
||||||
github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
|
|
||||||
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.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
|
||||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
|
||||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|
||||||
github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|
||||||
github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
|
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
|
||||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
|
||||||
github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
|
||||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
|
||||||
github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
|
|
||||||
github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
|
||||||
github.com/libdns/libdns v0.2.3 h1:ba30K4ObwMGB/QTmqUxf3H4/GmUrCAIkMWejeGl12v8=
|
|
||||||
github.com/libdns/libdns v0.2.3/go.mod h1:4Bj9+5CQiNMVGf87wjX4CY3HQJypUHRuLvlsfsZqLWQ=
|
|
||||||
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
|
||||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
|
||||||
github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
|
||||||
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
|
|
||||||
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
|
|
||||||
github.com/manifoldco/promptui v0.9.0 h1:3V4HzJk1TtXW1MTZMP7mdlwbBpIinw3HztaIlYthEiA=
|
|
||||||
github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
|
|
||||||
github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
|
|
||||||
github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
|
||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
|
||||||
github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
|
||||||
github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
|
||||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
|
|
||||||
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
|
|
||||||
github.com/mholt/acmez/v3 v3.1.1 h1:Jh+9uKHkPxUJdxM16q5mOr+G2V0aqkuFtNA28ihCxhQ=
|
|
||||||
github.com/mholt/acmez/v3 v3.1.1/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ=
|
|
||||||
github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4=
|
|
||||||
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-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
|
||||||
github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
|
|
||||||
github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
|
|
||||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
|
||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
|
||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
|
||||||
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
|
|
||||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
|
||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
|
||||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
|
|
||||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
|
||||||
github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo=
|
|
||||||
github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM=
|
|
||||||
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/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8=
|
|
||||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
|
||||||
github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI=
|
|
||||||
github.com/peterbourgon/diskv/v3 v3.0.1 h1:x06SQA46+PKIUftmEujdwSEpIx8kR+M9eLYsUxeYveU=
|
|
||||||
github.com/peterbourgon/diskv/v3 v3.0.1/go.mod h1:kJ5Ny7vLdARGU3WUuy6uzO6T0nb/2gWcT1JiBvRmb5o=
|
|
||||||
github.com/pires/go-proxyproto v0.8.0 h1:5unRmEAPbHXHuLjDg01CxJWf91cw3lKHc/0xzKpXEe0=
|
|
||||||
github.com/pires/go-proxyproto v0.8.0/go.mod h1:iknsfgnH8EkjrMeMyvfKByp9TiBZCKZM0jx2xmKqnVY=
|
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
|
||||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
|
||||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
|
||||||
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
|
||||||
github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE=
|
|
||||||
github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho=
|
|
||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
|
||||||
github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw=
|
|
||||||
github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI=
|
|
||||||
github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
|
||||||
github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE=
|
|
||||||
github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc=
|
|
||||||
github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
|
||||||
github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo=
|
|
||||||
github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo=
|
|
||||||
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
|
|
||||||
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
|
|
||||||
github.com/quic-go/quic-go v0.48.2 h1:wsKXZPeGWpMpCGSWqOcqpW2wZYic/8T3aqiOID0/KWE=
|
|
||||||
github.com/quic-go/quic-go v0.48.2/go.mod h1:yBgs3rWBOADpga7F+jJsb6Ybg1LSYiQvwWlLX+/6HMs=
|
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
|
||||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
|
||||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
|
||||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
|
||||||
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
|
|
||||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
|
||||||
github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
|
|
||||||
github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
|
|
||||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
|
||||||
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/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
|
|
||||||
github.com/schollz/jsonstore v1.1.0 h1:WZBDjgezFS34CHI+myb4s8GGpir3UMpy7vWoCeO0n6E=
|
|
||||||
github.com/schollz/jsonstore v1.1.0/go.mod h1:15c6+9guw8vDRyozGjN3FoILt0wpruJk9Pi66vjaZfg=
|
|
||||||
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
|
||||||
github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
|
|
||||||
github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
|
||||||
github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k=
|
|
||||||
github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME=
|
|
||||||
github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY=
|
|
||||||
github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM=
|
|
||||||
github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0=
|
|
||||||
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
|
||||||
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
|
|
||||||
github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw=
|
|
||||||
github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI=
|
|
||||||
github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU=
|
|
||||||
github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag=
|
|
||||||
github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg=
|
|
||||||
github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw=
|
|
||||||
github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y=
|
|
||||||
github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg=
|
|
||||||
github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q=
|
|
||||||
github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ=
|
|
||||||
github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I=
|
|
||||||
github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0=
|
|
||||||
github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ=
|
|
||||||
github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk=
|
|
||||||
github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
|
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
|
||||||
github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4=
|
|
||||||
github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw=
|
|
||||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
|
||||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
|
||||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
|
||||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
|
||||||
github.com/slackhq/nebula v1.7.2 h1:Rko1Mlksz/nC0c919xjGpB8uOSrTJ5e6KPgZx+lVfYw=
|
|
||||||
github.com/slackhq/nebula v1.7.2/go.mod h1:cnaoahkUipDs1vrNoIszyp0QPRIQN9Pm68ppQEW1Fhg=
|
|
||||||
github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262 h1:unQFBIznI+VYD1/1fApl1A+9VcBk+9dcqGfnePY87LY=
|
|
||||||
github.com/smallstep/assert v0.0.0-20200723003110-82e2b9b3b262/go.mod h1:MyOHs9Po2fbM1LHej6sBUT8ozbxmMOFG+E+rx/GSGuc=
|
|
||||||
github.com/smallstep/certificates v0.26.1 h1:FIUliEBcExSfJJDhRFA/s8aZgMIFuorexnRSKQd884o=
|
|
||||||
github.com/smallstep/certificates v0.26.1/go.mod h1:OQMrW39IrGKDViKSHrKcgSQArMZ8c7EcjhYKK7mYqis=
|
|
||||||
github.com/smallstep/go-attestation v0.4.4-0.20240109183208-413678f90935 h1:kjYvkvS/Wdy0PVRDUAA0gGJIVSEZYhiAJtfwYgOYoGA=
|
|
||||||
github.com/smallstep/go-attestation v0.4.4-0.20240109183208-413678f90935/go.mod h1:vNAduivU014fubg6ewygkAvQC0IQVXqdc8vaGl/0er4=
|
|
||||||
github.com/smallstep/nosql v0.6.1 h1:X8IBZFTRIp1gmuf23ne/jlD/BWKJtDQbtatxEn7Et1Y=
|
|
||||||
github.com/smallstep/nosql v0.6.1/go.mod h1:vrN+CftYYNnDM+DQqd863ATynvYFm/6FuY9D4TeAm2Y=
|
|
||||||
github.com/smallstep/pkcs7 v0.0.0-20231024181729-3b98ecc1ca81 h1:B6cED3iLJTgxpdh4tuqByDjRRKan2EvtnOfHr2zHJVg=
|
|
||||||
github.com/smallstep/pkcs7 v0.0.0-20231024181729-3b98ecc1ca81/go.mod h1:SoUAr/4M46rZ3WaLstHxGhLEgoYIDRqxQEXLOmOEB0Y=
|
|
||||||
github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d h1:06LUHn4Ia2X6syjIaCMNaXXDNdU+1N/oOHynJbWgpXw=
|
|
||||||
github.com/smallstep/scep v0.0.0-20231024192529-aee96d7ad34d/go.mod h1:4d0ub42ut1mMtvGyMensjuHYEUpRrASvkzLEJvoRQcU=
|
|
||||||
github.com/smallstep/truststore v0.13.0 h1:90if9htAOblavbMeWlqNLnO9bsjjgVv2hQeQJCi/py4=
|
|
||||||
github.com/smallstep/truststore v0.13.0/go.mod h1:3tmMp2aLKZ/OA/jnFUB0cYPcho402UG2knuJoPh4j7A=
|
|
||||||
github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE=
|
|
||||||
github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA=
|
|
||||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
|
||||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
|
||||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
|
||||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
|
||||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
|
||||||
github.com/spf13/cast v1.7.0 h1:ntdiHjuueXFgm5nzDRdOS4yfT43P5Fnud6DH50rz/7w=
|
|
||||||
github.com/spf13/cast v1.7.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
|
||||||
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
|
||||||
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/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
|
||||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
|
||||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
|
||||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
|
||||||
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
|
||||||
github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs=
|
|
||||||
github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo=
|
|
||||||
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.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
|
||||||
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/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
|
||||||
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/tailscale/tscert v0.0.0-20240608151842-d3f834017e53 h1:uxMgm0C+EjytfAqyfBG55ZONKQ7mvd7x4YYCWsf8QHQ=
|
|
||||||
github.com/tailscale/tscert v0.0.0-20240608151842-d3f834017e53/go.mod h1:kNGUQ3VESx3VZwRwA9MSCUegIl6+saPL8Noq82ozCaU=
|
|
||||||
github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA=
|
|
||||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
|
||||||
github.com/urfave/cli v1.22.14 h1:ebbhrRiGK2i4naQJr+1Xj92HXZCrK7MsyTS/ob3HnAk=
|
|
||||||
github.com/urfave/cli v1.22.14/go.mod h1:X0eDS6pD6Exaclxm99NJ3FiCDRED7vIHpx2mDOHLvkA=
|
|
||||||
github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU=
|
|
||||||
github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM=
|
|
||||||
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
|
|
||||||
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
|
|
||||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
|
||||||
github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY=
|
|
||||||
github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
|
|
||||||
github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI=
|
|
||||||
github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE=
|
|
||||||
github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
|
||||||
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
|
||||||
github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
|
|
||||||
go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI=
|
|
||||||
go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE=
|
|
||||||
go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA=
|
|
||||||
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
|
|
||||||
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0 h1:4Pp6oUg3+e/6M4C0A/3kJ2VYa++dsWVTtGgLVj5xtHg=
|
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.49.0/go.mod h1:Mjt1i1INqiaoZOMGR1RIUJN+i3ChKoFRqzrRQhlkbs0=
|
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0 h1:UP6IpuHFkUgOQL9FFQFrZ+5LiwhhYRbi7VZSIx6Nj5s=
|
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.56.0/go.mod h1:qxuZLtbq5QDtdeSHsS7bcf6EH6uO6jUAgk764zd3rhM=
|
|
||||||
go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY=
|
|
||||||
go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE=
|
|
||||||
go.opentelemetry.io/otel/metric v1.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE=
|
|
||||||
go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY=
|
|
||||||
go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys=
|
|
||||||
go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A=
|
|
||||||
go.step.sm/cli-utils v0.9.0 h1:55jYcsQbnArNqepZyAwcato6Zy2MoZDRkWW+jF+aPfQ=
|
|
||||||
go.step.sm/cli-utils v0.9.0/go.mod h1:Y/CRoWl1FVR9j+7PnAewufAwKmBOTzR6l9+7EYGAnp8=
|
|
||||||
go.step.sm/crypto v0.45.0 h1:Z0WYAaaOYrJmKP9sJkPW+6wy3pgN3Ija8ek/D4serjc=
|
|
||||||
go.step.sm/crypto v0.45.0/go.mod h1:6IYlT0L2jfj81nVyCPpvA5cORy0EVHPhieSgQyuwHIY=
|
|
||||||
go.step.sm/linkedca v0.20.1 h1:bHDn1+UG1NgRrERkWbbCiAIvv4lD5NOFaswPDTyO5vU=
|
|
||||||
go.step.sm/linkedca v0.20.1/go.mod h1:Vaq4+Umtjh7DLFI1KuIxeo598vfBzgSYZUjgVJ7Syxw=
|
|
||||||
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
|
||||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
|
||||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
|
||||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
|
||||||
go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
|
|
||||||
go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
|
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
|
||||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
|
||||||
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
|
||||||
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
|
||||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
|
||||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
|
||||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
|
||||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
|
||||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
|
||||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
|
||||||
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
|
||||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
|
||||||
go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
|
||||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
|
||||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
|
||||||
go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U=
|
|
||||||
go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ=
|
|
||||||
go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE=
|
|
||||||
golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw=
|
|
||||||
golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
|
||||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
|
||||||
golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
|
||||||
golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
|
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
|
||||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
|
||||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
|
||||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
|
||||||
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/crypto/x509roots/fallback v0.0.0-20241104001025-71ed71b4faf9 h1:4cEcP5+OjGppY79LCQ5Go2B1Boix2x0v6pvA01P3FoA=
|
|
||||||
golang.org/x/crypto/x509roots/fallback v0.0.0-20241104001025-71ed71b4faf9/go.mod h1:kNa9WdvYnzFwC79zRpLRMJbdEFlhyM5RPFBBZp/wWH8=
|
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
|
||||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
|
|
||||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
|
||||||
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
|
||||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
|
||||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
|
||||||
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.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
|
||||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
|
||||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
|
||||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
|
||||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
|
||||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
|
||||||
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.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
|
||||||
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
|
|
||||||
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
|
||||||
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
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-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20221010170243-090e33056c14/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.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.17.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.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
|
||||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
|
||||||
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.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
|
||||||
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.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
|
||||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
|
||||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
|
||||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
|
||||||
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.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
|
||||||
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
|
|
||||||
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
|
||||||
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
|
||||||
golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
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.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-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
|
||||||
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/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
|
||||||
google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
|
|
||||||
google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y=
|
|
||||||
google.golang.org/api v0.180.0 h1:M2D87Yo0rGBPWpo1orwfCLehUUL6E7/TYe5gvMQWDh4=
|
|
||||||
google.golang.org/api v0.180.0/go.mod h1:51AiyoEg1MJPSZ9zvklA8VnRILPXxn1iVen9v25XHAE=
|
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
|
||||||
google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
|
||||||
google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
|
||||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
|
||||||
google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
|
||||||
google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg=
|
|
||||||
google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
|
||||||
google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda h1:wu/KJm9KJwpfHWhkkZGohVC6KRrc1oJNr4jwtQMOQXw=
|
|
||||||
google.golang.org/genproto v0.0.0-20240401170217-c3f982113cda/go.mod h1:g2LLCvCeCSir/JJSWosk19BR4NVxGqHUC6rxIRsd7Aw=
|
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9 h1:T6rh4haD3GVYsgEfWExoCZA2o2FmbNyKpTuAxbEFPTg=
|
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:wp2WsuBYj6j8wUdo3ToZsdxxixbvQNAHqVJrTgi5E5M=
|
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc=
|
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
|
||||||
google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
|
|
||||||
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
|
|
||||||
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
|
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
|
||||||
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
|
|
||||||
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
|
|
||||||
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
|
||||||
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
|
||||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4=
|
|
||||||
gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M=
|
|
||||||
gopkg.in/go-playground/assert.v1 v1.2.1 h1:xoYuJVE7KT85PYWrN730RguIQO0ePzVRfFMXadIrXTM=
|
|
||||||
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
|
|
||||||
gopkg.in/go-playground/pool.v3 v3.1.1 h1:4Qcj91IsYTpIeRhe/eo6Fz+w6uKWPEghx8vHFTYMfhw=
|
|
||||||
gopkg.in/go-playground/pool.v3 v3.1.1/go.mod h1:pUAGBximS/hccTTSzEop6wvvQhVa3QPDFFW+8REdutg=
|
|
||||||
gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
|
|
||||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
|
||||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
|
||||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
|
||||||
gopkg.in/yaml.v1 v1.0.0-20140924161607-9f9df34309c0/go.mod h1:WDnlLJ4WF5VGsH/HVa3CI79GS0ol3YnhVnKP89i0kNg=
|
|
||||||
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.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
|
||||||
grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o=
|
|
||||||
honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
|
||||||
howett.net/plist v1.0.0 h1:7CrbWYbPPO/PyNy38b2EB/+gYbjCe2DXBxgtOOZbSQM=
|
|
||||||
howett.net/plist v1.0.0/go.mod h1:lqaXoTrLY4hg8tnEzNru53gicrbv7rrk+2xJA/7hw9g=
|
|
||||||
k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls=
|
|
||||||
k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k=
|
|
||||||
k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U=
|
|
||||||
k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
|
|
||||||
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/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/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro=
|
|
||||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
|
||||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8=
|
|
||||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo=
|
|
||||||
sigs.k8s.io/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/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
|
||||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
|
||||||
sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck=
|
|
||||||
sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0=
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
package caddy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/caddyserver/ingress/pkg/converter"
|
|
||||||
"github.com/caddyserver/ingress/pkg/store"
|
|
||||||
|
|
||||||
// Load default plugins
|
|
||||||
_ "github.com/caddyserver/ingress/internal/caddy/global"
|
|
||||||
_ "github.com/caddyserver/ingress/internal/caddy/ingress"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Converter struct{}
|
|
||||||
|
|
||||||
func (c Converter) ConvertToCaddyConfig(store *store.Store) (interface{}, error) {
|
|
||||||
cfg := converter.NewConfig()
|
|
||||||
|
|
||||||
for _, p := range converter.Plugins(store.Options.PluginsOrder) {
|
|
||||||
if m, ok := p.(converter.GlobalMiddleware); ok {
|
|
||||||
err := m.GlobalHandler(cfg, store)
|
|
||||||
if err != nil {
|
|
||||||
return cfg, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return cfg, nil
|
|
||||||
}
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
package caddy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/caddyserver/ingress/pkg/store"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestConvertToCaddyConfig(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
expectedConfigPath string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "default",
|
|
||||||
expectedConfigPath: "./test_data/default.json",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
cfg, err := Converter{}.ConvertToCaddyConfig(store.NewStore(store.Options{}, &store.PodInfo{}))
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
cfgJson, err := json.Marshal(cfg)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
expectedCfg, err := os.ReadFile(test.expectedConfigPath)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
require.JSONEq(t, string(expectedCfg), string(cfgJson))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,88 +0,0 @@
|
|||||||
package global
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
caddy2 "github.com/caddyserver/caddy/v2"
|
|
||||||
"github.com/caddyserver/caddy/v2/caddyconfig"
|
|
||||||
"github.com/caddyserver/caddy/v2/modules/caddytls"
|
|
||||||
"github.com/caddyserver/ingress/pkg/converter"
|
|
||||||
"github.com/caddyserver/ingress/pkg/store"
|
|
||||||
"github.com/mholt/acmez/v3/acme"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ConfigMapPlugin struct{}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
converter.RegisterPlugin(ConfigMapPlugin{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p ConfigMapPlugin) IngressPlugin() converter.PluginInfo {
|
|
||||||
return converter.PluginInfo{
|
|
||||||
Name: "configmap",
|
|
||||||
New: func() converter.Plugin { return new(ConfigMapPlugin) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p ConfigMapPlugin) GlobalHandler(config *converter.Config, store *store.Store) error {
|
|
||||||
cfgMap := store.ConfigMap
|
|
||||||
|
|
||||||
tlsApp := config.GetTLSApp()
|
|
||||||
httpServer := config.GetHTTPServer()
|
|
||||||
|
|
||||||
if cfgMap.Debug {
|
|
||||||
config.Logging.Logs = map[string]*caddy2.CustomLog{"default": {BaseLog: caddy2.BaseLog{Level: "DEBUG"}}}
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfgMap.AcmeCA != "" || cfgMap.Email != "" {
|
|
||||||
acmeIssuer := caddytls.ACMEIssuer{}
|
|
||||||
|
|
||||||
if cfgMap.AcmeCA != "" {
|
|
||||||
acmeIssuer.CA = cfgMap.AcmeCA
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfgMap.AcmeEABKeyId != "" && cfgMap.AcmeEABMacKey != "" {
|
|
||||||
acmeIssuer.ExternalAccount = &acme.EAB{
|
|
||||||
KeyID: cfgMap.AcmeEABKeyId,
|
|
||||||
MACKey: cfgMap.AcmeEABMacKey,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfgMap.Email != "" {
|
|
||||||
acmeIssuer.Email = cfgMap.Email
|
|
||||||
}
|
|
||||||
|
|
||||||
var onDemandConfig *caddytls.OnDemandConfig
|
|
||||||
if cfgMap.OnDemandTLS {
|
|
||||||
onDemandConfig = &caddytls.OnDemandConfig{
|
|
||||||
Ask: cfgMap.OnDemandAsk,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tlsApp.Automation = &caddytls.AutomationConfig{
|
|
||||||
OnDemand: onDemandConfig,
|
|
||||||
OCSPCheckInterval: cfgMap.OCSPCheckInterval,
|
|
||||||
Policies: []*caddytls.AutomationPolicy{
|
|
||||||
{
|
|
||||||
IssuersRaw: []json.RawMessage{
|
|
||||||
caddyconfig.JSONModuleObject(acmeIssuer, "module", "acme", nil),
|
|
||||||
},
|
|
||||||
OnDemand: cfgMap.OnDemandTLS,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfgMap.ProxyProtocol {
|
|
||||||
httpServer.ListenerWrappersRaw = []json.RawMessage{
|
|
||||||
json.RawMessage(`{"wrapper":"proxy_protocol"}`),
|
|
||||||
json.RawMessage(`{"wrapper":"tls"}`),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interface guards
|
|
||||||
var (
|
|
||||||
_ = converter.GlobalMiddleware(ConfigMapPlugin{})
|
|
||||||
)
|
|
||||||
@ -1,48 +0,0 @@
|
|||||||
package global
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/caddyserver/caddy/v2"
|
|
||||||
"github.com/caddyserver/caddy/v2/caddyconfig"
|
|
||||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
|
||||||
"github.com/caddyserver/ingress/pkg/converter"
|
|
||||||
"github.com/caddyserver/ingress/pkg/store"
|
|
||||||
)
|
|
||||||
|
|
||||||
type HealthzPlugin struct{}
|
|
||||||
|
|
||||||
func (p HealthzPlugin) IngressPlugin() converter.PluginInfo {
|
|
||||||
return converter.PluginInfo{
|
|
||||||
Name: "healthz",
|
|
||||||
Priority: -20,
|
|
||||||
New: func() converter.Plugin { return new(HealthzPlugin) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
converter.RegisterPlugin(HealthzPlugin{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p HealthzPlugin) GlobalHandler(config *converter.Config, store *store.Store) error {
|
|
||||||
healthzHandler := caddyhttp.StaticResponse{StatusCode: caddyhttp.WeakString(strconv.Itoa(http.StatusOK))}
|
|
||||||
|
|
||||||
healthzRoute := caddyhttp.Route{
|
|
||||||
HandlersRaw: []json.RawMessage{
|
|
||||||
caddyconfig.JSONModuleObject(healthzHandler, "handler", healthzHandler.CaddyModule().ID.Name(), nil),
|
|
||||||
},
|
|
||||||
MatcherSetsRaw: []caddy.ModuleMap{{
|
|
||||||
"path": caddyconfig.JSON(caddyhttp.MatchPath{"/healthz"}, nil),
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
|
|
||||||
config.GetMetricsServer().Routes = append(config.GetMetricsServer().Routes, healthzRoute)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interface guards
|
|
||||||
var (
|
|
||||||
_ = converter.GlobalMiddleware(HealthzPlugin{})
|
|
||||||
)
|
|
||||||
@ -1,70 +0,0 @@
|
|||||||
package global
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/caddyserver/caddy/v2"
|
|
||||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
|
||||||
"github.com/caddyserver/ingress/pkg/converter"
|
|
||||||
"github.com/caddyserver/ingress/pkg/store"
|
|
||||||
)
|
|
||||||
|
|
||||||
type IngressPlugin struct{}
|
|
||||||
|
|
||||||
func (p IngressPlugin) IngressPlugin() converter.PluginInfo {
|
|
||||||
return converter.PluginInfo{
|
|
||||||
Name: "ingress",
|
|
||||||
New: func() converter.Plugin { return new(IngressPlugin) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
converter.RegisterPlugin(IngressPlugin{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p IngressPlugin) GlobalHandler(config *converter.Config, store *store.Store) error {
|
|
||||||
ingressHandlers := make([]converter.IngressMiddleware, 0)
|
|
||||||
for _, plugin := range converter.Plugins(store.Options.PluginsOrder) {
|
|
||||||
if m, ok := plugin.(converter.IngressMiddleware); ok {
|
|
||||||
ingressHandlers = append(ingressHandlers, m)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a server route for each ingress route
|
|
||||||
var routes caddyhttp.RouteList
|
|
||||||
for _, ing := range store.Ingresses {
|
|
||||||
for _, rule := range ing.Spec.Rules {
|
|
||||||
for _, path := range rule.HTTP.Paths {
|
|
||||||
r := &caddyhttp.Route{
|
|
||||||
HandlersRaw: []json.RawMessage{},
|
|
||||||
MatcherSetsRaw: []caddy.ModuleMap{},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, middleware := range ingressHandlers {
|
|
||||||
newRoute, err := middleware.IngressHandler(converter.IngressMiddlewareInput{
|
|
||||||
Config: config,
|
|
||||||
Store: store,
|
|
||||||
Ingress: ing,
|
|
||||||
Rule: rule,
|
|
||||||
Path: path,
|
|
||||||
Route: r,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
r = newRoute
|
|
||||||
}
|
|
||||||
|
|
||||||
routes = append(routes, *r)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
config.GetHTTPServer().Routes = routes
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interface guards
|
|
||||||
var (
|
|
||||||
_ = converter.GlobalMiddleware(IngressPlugin{})
|
|
||||||
)
|
|
||||||
@ -1,77 +0,0 @@
|
|||||||
package global
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
|
||||||
"github.com/caddyserver/ingress/pkg/converter"
|
|
||||||
"github.com/caddyserver/ingress/pkg/store"
|
|
||||||
)
|
|
||||||
|
|
||||||
type IngressSortPlugin struct{}
|
|
||||||
|
|
||||||
func (p IngressSortPlugin) IngressPlugin() converter.PluginInfo {
|
|
||||||
return converter.PluginInfo{
|
|
||||||
Name: "ingress_sort",
|
|
||||||
// Must go after ingress are configured
|
|
||||||
Priority: -2,
|
|
||||||
New: func() converter.Plugin { return new(IngressSortPlugin) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
converter.RegisterPlugin(IngressSortPlugin{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func getFirstItemFromJSON(data json.RawMessage) string {
|
|
||||||
var arr []string
|
|
||||||
err := json.Unmarshal(data, &arr)
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
return arr[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
func sortRoutes(routes caddyhttp.RouteList) {
|
|
||||||
sort.SliceStable(routes, func(i, j int) bool {
|
|
||||||
iPath := getFirstItemFromJSON(routes[i].MatcherSetsRaw[0]["path"])
|
|
||||||
jPath := getFirstItemFromJSON(routes[j].MatcherSetsRaw[0]["path"])
|
|
||||||
iPrefixed := strings.HasSuffix(iPath, "*")
|
|
||||||
jPrefixed := strings.HasSuffix(jPath, "*")
|
|
||||||
|
|
||||||
// If both same type check by length
|
|
||||||
if iPrefixed == jPrefixed {
|
|
||||||
return len(jPath) < len(iPath)
|
|
||||||
}
|
|
||||||
// Empty path will be moved last
|
|
||||||
if jPath == "" || iPath == "" {
|
|
||||||
return jPath == ""
|
|
||||||
}
|
|
||||||
// j path is exact so should go first
|
|
||||||
return jPrefixed
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// GlobalHandler in IngressSortPlugin tries to sort routes to have the less conflict.
|
|
||||||
//
|
|
||||||
// It only supports basic conflicts for now. It doesn't support multiple matchers in the same route
|
|
||||||
// nor multiple path/host in the matcher. It shouldn't be an issue with the ingress.matcher plugin.
|
|
||||||
// Sort will prioritize exact paths then prefix paths and finally empty paths.
|
|
||||||
// When 2 exacts paths or 2 prefixed paths are on the same host, we choose the longer first.
|
|
||||||
func (p IngressSortPlugin) GlobalHandler(config *converter.Config, store *store.Store) error {
|
|
||||||
if !store.ConfigMap.ExperimentalSmartSort {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
routes := config.GetHTTPServer().Routes
|
|
||||||
|
|
||||||
sortRoutes(routes)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interface guards
|
|
||||||
var (
|
|
||||||
_ = converter.GlobalMiddleware(IngressSortPlugin{})
|
|
||||||
)
|
|
||||||
@ -1,109 +0,0 @@
|
|||||||
package global
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"reflect"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/caddyserver/caddy/v2"
|
|
||||||
"github.com/caddyserver/caddy/v2/caddyconfig"
|
|
||||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestIngressSort(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
routes []struct {
|
|
||||||
id int
|
|
||||||
path string
|
|
||||||
}
|
|
||||||
expect []int
|
|
||||||
}{
|
|
||||||
|
|
||||||
{
|
|
||||||
name: "multiple exact paths",
|
|
||||||
routes: []struct {
|
|
||||||
id int
|
|
||||||
path string
|
|
||||||
}{
|
|
||||||
{id: 0, path: "/path/a"},
|
|
||||||
{id: 1, path: "/path/"},
|
|
||||||
{id: 2, path: "/other"},
|
|
||||||
},
|
|
||||||
expect: []int{0, 1, 2},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "multiple prefix paths",
|
|
||||||
routes: []struct {
|
|
||||||
id int
|
|
||||||
path string
|
|
||||||
}{
|
|
||||||
{id: 0, path: "/path/*"},
|
|
||||||
{id: 1, path: "/path/auth/*"},
|
|
||||||
{id: 2, path: "/other/*"},
|
|
||||||
{id: 3, path: "/login/*"},
|
|
||||||
},
|
|
||||||
expect: []int{1, 2, 3, 0},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "mixed exact and prefixed",
|
|
||||||
routes: []struct {
|
|
||||||
id int
|
|
||||||
path string
|
|
||||||
}{
|
|
||||||
{id: 0, path: "/path/*"},
|
|
||||||
{id: 1, path: "/path/auth/"},
|
|
||||||
{id: 2, path: "/path/v2/*"},
|
|
||||||
{id: 3, path: "/path/new"},
|
|
||||||
},
|
|
||||||
expect: []int{1, 3, 2, 0},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "mixed exact, prefix and empty",
|
|
||||||
routes: []struct {
|
|
||||||
id int
|
|
||||||
path string
|
|
||||||
}{
|
|
||||||
{id: 0, path: "/path/*"},
|
|
||||||
{id: 1, path: ""},
|
|
||||||
{id: 2, path: "/path/v2/*"},
|
|
||||||
{id: 3, path: "/path/new"},
|
|
||||||
{id: 4, path: ""},
|
|
||||||
},
|
|
||||||
expect: []int{3, 2, 0, 1, 4},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
routes := []caddyhttp.Route{}
|
|
||||||
|
|
||||||
for _, route := range test.routes {
|
|
||||||
match := caddy.ModuleMap{}
|
|
||||||
match["id"] = caddyconfig.JSON(route.id, nil)
|
|
||||||
|
|
||||||
if route.path != "" {
|
|
||||||
match["path"] = caddyconfig.JSON(caddyhttp.MatchPath{route.path}, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
r := caddyhttp.Route{MatcherSetsRaw: []caddy.ModuleMap{match}}
|
|
||||||
routes = append(routes, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
sortRoutes(routes)
|
|
||||||
|
|
||||||
var got []int
|
|
||||||
for i := range test.expect {
|
|
||||||
var currentId int
|
|
||||||
err := json.Unmarshal(routes[i].MatcherSetsRaw[0]["id"], ¤tId)
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("error unmarshaling id for i %v, %v", i, err)
|
|
||||||
}
|
|
||||||
got = append(got, currentId)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !reflect.DeepEqual(test.expect, got) {
|
|
||||||
t.Errorf("expected order to match: got %v, expected %v, %s", got, test.expect, routes[1].MatcherSetsRaw)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,43 +0,0 @@
|
|||||||
package global
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/caddyserver/caddy/v2"
|
|
||||||
"github.com/caddyserver/caddy/v2/caddyconfig"
|
|
||||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
|
||||||
"github.com/caddyserver/ingress/pkg/converter"
|
|
||||||
"github.com/caddyserver/ingress/pkg/store"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MetricsPlugin struct{}
|
|
||||||
|
|
||||||
func (p MetricsPlugin) IngressPlugin() converter.PluginInfo {
|
|
||||||
return converter.PluginInfo{
|
|
||||||
Name: "metrics",
|
|
||||||
New: func() converter.Plugin { return new(MetricsPlugin) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
converter.RegisterPlugin(MetricsPlugin{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p MetricsPlugin) GlobalHandler(config *converter.Config, store *store.Store) error {
|
|
||||||
if store.ConfigMap.Metrics {
|
|
||||||
metricsRoute := caddyhttp.Route{
|
|
||||||
HandlersRaw: []json.RawMessage{json.RawMessage(`{ "handler": "metrics" }`)},
|
|
||||||
MatcherSetsRaw: []caddy.ModuleMap{{
|
|
||||||
"path": caddyconfig.JSON(caddyhttp.MatchPath{"/metrics"}, nil),
|
|
||||||
}},
|
|
||||||
}
|
|
||||||
|
|
||||||
config.GetMetricsServer().Routes = append(config.GetMetricsServer().Routes, metricsRoute)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interface guards
|
|
||||||
var (
|
|
||||||
_ = converter.GlobalMiddleware(MetricsPlugin{})
|
|
||||||
)
|
|
||||||
@ -1,36 +0,0 @@
|
|||||||
package global
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/caddyserver/ingress/pkg/converter"
|
|
||||||
"github.com/caddyserver/ingress/pkg/store"
|
|
||||||
)
|
|
||||||
|
|
||||||
type SecretsStorePlugin struct{}
|
|
||||||
|
|
||||||
func (p SecretsStorePlugin) IngressPlugin() converter.PluginInfo {
|
|
||||||
return converter.PluginInfo{
|
|
||||||
Name: "secrets_store",
|
|
||||||
New: func() converter.Plugin { return new(SecretsStorePlugin) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
converter.RegisterPlugin(SecretsStorePlugin{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p SecretsStorePlugin) GlobalHandler(config *converter.Config, store *store.Store) error {
|
|
||||||
config.Storage = converter.Storage{
|
|
||||||
System: "secret_store",
|
|
||||||
StorageValues: converter.StorageValues{
|
|
||||||
Namespace: store.CurrentPod.Namespace,
|
|
||||||
LeaseId: store.Options.LeaseId,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interface guards
|
|
||||||
var (
|
|
||||||
_ = converter.GlobalMiddleware(SecretsStorePlugin{})
|
|
||||||
)
|
|
||||||
@ -1,53 +0,0 @@
|
|||||||
package global
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"slices"
|
|
||||||
|
|
||||||
"github.com/caddyserver/ingress/internal/controller"
|
|
||||||
"github.com/caddyserver/ingress/pkg/converter"
|
|
||||||
"github.com/caddyserver/ingress/pkg/store"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TLSPlugin struct{}
|
|
||||||
|
|
||||||
func (p TLSPlugin) IngressPlugin() converter.PluginInfo {
|
|
||||||
return converter.PluginInfo{
|
|
||||||
Name: "tls",
|
|
||||||
New: func() converter.Plugin { return new(TLSPlugin) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
converter.RegisterPlugin(TLSPlugin{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p TLSPlugin) GlobalHandler(config *converter.Config, store *store.Store) error {
|
|
||||||
tlsApp := config.GetTLSApp()
|
|
||||||
httpServer := config.GetHTTPServer()
|
|
||||||
|
|
||||||
var hosts []string
|
|
||||||
|
|
||||||
// Get all Hosts subject to custom TLS certs
|
|
||||||
for _, ing := range store.Ingresses {
|
|
||||||
for _, tlsRule := range ing.Spec.TLS {
|
|
||||||
for _, h := range tlsRule.Hosts {
|
|
||||||
if !slices.Contains(hosts, h) {
|
|
||||||
hosts = append(hosts, h)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(hosts) > 0 {
|
|
||||||
tlsApp.CertificatesRaw["load_folders"] = json.RawMessage(`["` + controller.CertFolder + `"]`)
|
|
||||||
// do not manage certificates for those hosts
|
|
||||||
httpServer.AutoHTTPS.SkipCerts = hosts
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interface guards
|
|
||||||
var (
|
|
||||||
_ = converter.GlobalMiddleware(TLSPlugin{})
|
|
||||||
)
|
|
||||||
@ -1,211 +0,0 @@
|
|||||||
package global
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/caddyserver/ingress/pkg/converter"
|
|
||||||
"github.com/caddyserver/ingress/pkg/store"
|
|
||||||
|
|
||||||
networkingv1 "k8s.io/api/networking/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
types "k8s.io/apimachinery/pkg/types"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestIngressTlsSkipCertificates(t *testing.T) {
|
|
||||||
testCases := []struct {
|
|
||||||
desc string
|
|
||||||
skippedCertsDomains []string
|
|
||||||
ingresses []*networkingv1.Ingress
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
desc: "No ingress registered",
|
|
||||||
skippedCertsDomains: []string{},
|
|
||||||
ingresses: []*networkingv1.Ingress{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "One ingress registered with certificate with one domain",
|
|
||||||
skippedCertsDomains: []string{"domain1.tld"},
|
|
||||||
ingresses: []*networkingv1.Ingress{
|
|
||||||
{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
UID: types.UID("first"),
|
|
||||||
},
|
|
||||||
Spec: networkingv1.IngressSpec{
|
|
||||||
TLS: []networkingv1.IngressTLS{{
|
|
||||||
Hosts: []string{"domain1.tld"},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "One ingress registered with certificate with multiple domains",
|
|
||||||
skippedCertsDomains: []string{"domain1.tld", "domain2.tld", "domain3.tld"},
|
|
||||||
ingresses: []*networkingv1.Ingress{
|
|
||||||
{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
UID: types.UID("first"),
|
|
||||||
},
|
|
||||||
Spec: networkingv1.IngressSpec{
|
|
||||||
TLS: []networkingv1.IngressTLS{{
|
|
||||||
Hosts: []string{"domain1.tld", "domain2.tld", "domain3.tld"},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "Two ingress registered with certificate one domain each",
|
|
||||||
skippedCertsDomains: []string{"domain1.tld", "domain2.tld"},
|
|
||||||
ingresses: []*networkingv1.Ingress{
|
|
||||||
{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
UID: types.UID("first"),
|
|
||||||
},
|
|
||||||
Spec: networkingv1.IngressSpec{
|
|
||||||
TLS: []networkingv1.IngressTLS{{
|
|
||||||
Hosts: []string{"domain1.tld"},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
UID: types.UID("second"),
|
|
||||||
},
|
|
||||||
Spec: networkingv1.IngressSpec{
|
|
||||||
TLS: []networkingv1.IngressTLS{{
|
|
||||||
Hosts: []string{"domain2.tld"},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "Two ingress registered with certificate the same domain",
|
|
||||||
skippedCertsDomains: []string{"domain1.tld"},
|
|
||||||
ingresses: []*networkingv1.Ingress{
|
|
||||||
{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
UID: types.UID("first"),
|
|
||||||
},
|
|
||||||
Spec: networkingv1.IngressSpec{
|
|
||||||
TLS: []networkingv1.IngressTLS{{
|
|
||||||
Hosts: []string{"domain1.tld"},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
UID: types.UID("second"),
|
|
||||||
},
|
|
||||||
Spec: networkingv1.IngressSpec{
|
|
||||||
TLS: []networkingv1.IngressTLS{{
|
|
||||||
Hosts: []string{"domain1.tld"},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "Two ingress registered with certificate with multiple domains each",
|
|
||||||
skippedCertsDomains: []string{"domain1a.tld", "domain1b.tld", "domain2a.tld", "domain2b.tld"},
|
|
||||||
ingresses: []*networkingv1.Ingress{
|
|
||||||
{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
UID: types.UID("first"),
|
|
||||||
},
|
|
||||||
Spec: networkingv1.IngressSpec{
|
|
||||||
TLS: []networkingv1.IngressTLS{{
|
|
||||||
Hosts: []string{"domain1a.tld", "domain1b.tld"},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
UID: types.UID("second"),
|
|
||||||
},
|
|
||||||
Spec: networkingv1.IngressSpec{
|
|
||||||
TLS: []networkingv1.IngressTLS{{
|
|
||||||
Hosts: []string{"domain2a.tld", "domain2b.tld"},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "Two ingress registered with certificate with multiple domains each and partial domain overlap",
|
|
||||||
skippedCertsDomains: []string{"domain1.tld", "domain2a.tld", "domain2b.tld"},
|
|
||||||
ingresses: []*networkingv1.Ingress{
|
|
||||||
{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
UID: types.UID("first"),
|
|
||||||
},
|
|
||||||
Spec: networkingv1.IngressSpec{
|
|
||||||
TLS: []networkingv1.IngressTLS{{
|
|
||||||
Hosts: []string{"domain1.tld", "domain2a.tld"},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
UID: types.UID("second"),
|
|
||||||
},
|
|
||||||
Spec: networkingv1.IngressSpec{
|
|
||||||
TLS: []networkingv1.IngressTLS{{
|
|
||||||
Hosts: []string{"domain1.tld", "domain2b.tld"},
|
|
||||||
}},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "One ingress registered without certificate",
|
|
||||||
skippedCertsDomains: []string{},
|
|
||||||
ingresses: []*networkingv1.Ingress{
|
|
||||||
{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
UID: types.UID("first"),
|
|
||||||
},
|
|
||||||
Spec: networkingv1.IngressSpec{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
desc: "Two ingresses registered without certificate",
|
|
||||||
skippedCertsDomains: []string{},
|
|
||||||
ingresses: []*networkingv1.Ingress{
|
|
||||||
{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
UID: types.UID("first"),
|
|
||||||
},
|
|
||||||
Spec: networkingv1.IngressSpec{},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
UID: types.UID("second"),
|
|
||||||
},
|
|
||||||
Spec: networkingv1.IngressSpec{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tC := range testCases {
|
|
||||||
t.Run(tC.desc, func(t *testing.T) {
|
|
||||||
tp := TLSPlugin{}
|
|
||||||
c := converter.NewConfig()
|
|
||||||
s := store.NewStore(store.Options{}, &store.PodInfo{})
|
|
||||||
|
|
||||||
for _, ing := range tC.ingresses {
|
|
||||||
s.AddIngress(ing)
|
|
||||||
}
|
|
||||||
|
|
||||||
tp.GlobalHandler(c, s)
|
|
||||||
|
|
||||||
toSkip := c.GetHTTPServer().AutoHTTPS.SkipCerts
|
|
||||||
assert.ElementsMatch(t, toSkip, tC.skippedCertsDomains, "List of certificate to skip don't match expectation")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,28 +0,0 @@
|
|||||||
package ingress
|
|
||||||
|
|
||||||
import v1 "k8s.io/api/networking/v1"
|
|
||||||
|
|
||||||
const (
|
|
||||||
annotationPrefix = "caddy.ingress.kubernetes.io"
|
|
||||||
rewriteToAnnotation = "rewrite-to"
|
|
||||||
rewriteStripPrefixAnnotation = "rewrite-strip-prefix"
|
|
||||||
disableSSLRedirect = "disable-ssl-redirect"
|
|
||||||
backendProtocol = "backend-protocol"
|
|
||||||
insecureSkipVerify = "insecure-skip-verify"
|
|
||||||
permanentRedirectAnnotation = "permanent-redirect"
|
|
||||||
permanentRedirectCodeAnnotation = "permanent-redirect-code"
|
|
||||||
temporaryRedirectAnnotation = "temporal-redirect"
|
|
||||||
trustedProxies = "trusted-proxies"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getAnnotation(ing *v1.Ingress, rule string) string {
|
|
||||||
return ing.Annotations[annotationPrefix+"/"+rule]
|
|
||||||
}
|
|
||||||
|
|
||||||
func getAnnotationBool(ing *v1.Ingress, rule string, def bool) bool {
|
|
||||||
val := getAnnotation(ing, rule)
|
|
||||||
if val == "" {
|
|
||||||
return def
|
|
||||||
}
|
|
||||||
return val == "true"
|
|
||||||
}
|
|
||||||
@ -1,52 +0,0 @@
|
|||||||
package ingress
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/caddyserver/caddy/v2"
|
|
||||||
"github.com/caddyserver/caddy/v2/caddyconfig"
|
|
||||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
|
||||||
"github.com/caddyserver/ingress/pkg/converter"
|
|
||||||
v1 "k8s.io/api/networking/v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MatcherPlugin struct{}
|
|
||||||
|
|
||||||
func (p MatcherPlugin) IngressPlugin() converter.PluginInfo {
|
|
||||||
return converter.PluginInfo{
|
|
||||||
Name: "ingress.matcher",
|
|
||||||
New: func() converter.Plugin { return new(MatcherPlugin) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IngressHandler Generate matchers for the route.
|
|
||||||
func (p MatcherPlugin) IngressHandler(input converter.IngressMiddlewareInput) (*caddyhttp.Route, error) {
|
|
||||||
match := caddy.ModuleMap{}
|
|
||||||
|
|
||||||
if getAnnotation(input.Ingress, disableSSLRedirect) != "true" {
|
|
||||||
match["protocol"] = caddyconfig.JSON(caddyhttp.MatchProtocol("https"), nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
if input.Rule.Host != "" {
|
|
||||||
match["host"] = caddyconfig.JSON(caddyhttp.MatchHost{input.Rule.Host}, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
if input.Path.Path != "" {
|
|
||||||
p := input.Path.Path
|
|
||||||
|
|
||||||
if *input.Path.PathType == v1.PathTypePrefix {
|
|
||||||
p += "*"
|
|
||||||
}
|
|
||||||
match["path"] = caddyconfig.JSON(caddyhttp.MatchPath{p}, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
input.Route.MatcherSetsRaw = append(input.Route.MatcherSetsRaw, match)
|
|
||||||
return input.Route, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
converter.RegisterPlugin(MatcherPlugin{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interface guards
|
|
||||||
var (
|
|
||||||
_ = converter.IngressMiddleware(MatcherPlugin{})
|
|
||||||
)
|
|
||||||
@ -1,82 +0,0 @@
|
|||||||
package ingress
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
"strconv"
|
|
||||||
|
|
||||||
"github.com/caddyserver/caddy/v2/caddyconfig"
|
|
||||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
|
||||||
"github.com/caddyserver/ingress/pkg/converter"
|
|
||||||
)
|
|
||||||
|
|
||||||
type RedirectPlugin struct{}
|
|
||||||
|
|
||||||
func (p RedirectPlugin) IngressPlugin() converter.PluginInfo {
|
|
||||||
return converter.PluginInfo{
|
|
||||||
Name: "ingress.redirect",
|
|
||||||
Priority: 10,
|
|
||||||
New: func() converter.Plugin { return new(RedirectPlugin) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IngressHandler Converts redirect annotations to static_response handler
|
|
||||||
func (p RedirectPlugin) IngressHandler(input converter.IngressMiddlewareInput) (*caddyhttp.Route, error) {
|
|
||||||
ing := input.Ingress
|
|
||||||
|
|
||||||
permanentRedir := getAnnotation(ing, permanentRedirectAnnotation)
|
|
||||||
temporaryRedir := getAnnotation(ing, temporaryRedirectAnnotation)
|
|
||||||
|
|
||||||
var code string = "301"
|
|
||||||
var redirectTo string = ""
|
|
||||||
|
|
||||||
// Don't allow both redirect annotations to be set
|
|
||||||
if permanentRedir != "" && temporaryRedir != "" {
|
|
||||||
return nil, fmt.Errorf("cannot use permanent-redirect annotation with temporal-redirect")
|
|
||||||
}
|
|
||||||
|
|
||||||
if permanentRedir != "" {
|
|
||||||
redirectCode := getAnnotation(ing, permanentRedirectCodeAnnotation)
|
|
||||||
if redirectCode != "" {
|
|
||||||
codeInt, err := strconv.Atoi(redirectCode)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("not a supported redirection code type or not a valid integer: '%s'", redirectCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
if codeInt < 300 || (codeInt > 399 && codeInt != 401) {
|
|
||||||
return nil, fmt.Errorf("redirection code not in the 3xx range or 401: '%v'", codeInt)
|
|
||||||
}
|
|
||||||
|
|
||||||
code = redirectCode
|
|
||||||
}
|
|
||||||
redirectTo = permanentRedir
|
|
||||||
}
|
|
||||||
|
|
||||||
if temporaryRedir != "" {
|
|
||||||
code = "302"
|
|
||||||
redirectTo = temporaryRedir
|
|
||||||
}
|
|
||||||
|
|
||||||
if redirectTo != "" {
|
|
||||||
handler := caddyconfig.JSONModuleObject(
|
|
||||||
caddyhttp.StaticResponse{
|
|
||||||
StatusCode: caddyhttp.WeakString(code),
|
|
||||||
Headers: http.Header{"Location": []string{redirectTo}},
|
|
||||||
},
|
|
||||||
"handler", "static_response", nil,
|
|
||||||
)
|
|
||||||
|
|
||||||
input.Route.HandlersRaw = append(input.Route.HandlersRaw, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
return input.Route, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
converter.RegisterPlugin(RedirectPlugin{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interface guards
|
|
||||||
var (
|
|
||||||
_ = converter.IngressMiddleware(RedirectPlugin{})
|
|
||||||
)
|
|
||||||
@ -1,135 +0,0 @@
|
|||||||
package ingress
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
|
||||||
"github.com/caddyserver/ingress/pkg/converter"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
networkingv1 "k8s.io/api/networking/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestRedirectConvertToCaddyConfig(t *testing.T) {
|
|
||||||
rp := RedirectPlugin{}
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
expectedConfigPath string
|
|
||||||
expectedError string
|
|
||||||
annotations map[string]string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "Check permanent redirect without any specific redirect code",
|
|
||||||
expectedConfigPath: "test_data/redirect_default.json",
|
|
||||||
annotations: map[string]string{
|
|
||||||
"caddy.ingress.kubernetes.io/permanent-redirect": "http://example.com",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Check permanent redirect with custom redirect code",
|
|
||||||
expectedConfigPath: "test_data/redirect_custom_code.json",
|
|
||||||
annotations: map[string]string{
|
|
||||||
"caddy.ingress.kubernetes.io/permanent-redirect": "http://example.com",
|
|
||||||
"caddy.ingress.kubernetes.io/permanent-redirect-code": "308",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Check permanent redirect with 401 as redirect code",
|
|
||||||
expectedConfigPath: "test_data/redirect_401.json",
|
|
||||||
annotations: map[string]string{
|
|
||||||
"caddy.ingress.kubernetes.io/permanent-redirect": "http://example.com",
|
|
||||||
"caddy.ingress.kubernetes.io/permanent-redirect-code": "401",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Check temporary redirect",
|
|
||||||
expectedConfigPath: "test_data/redirect_temporary.json",
|
|
||||||
annotations: map[string]string{
|
|
||||||
"caddy.ingress.kubernetes.io/temporal-redirect": "http://example.com",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
input := converter.IngressMiddlewareInput{
|
|
||||||
Ingress: &networkingv1.Ingress{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Annotations: test.annotations,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Route: &caddyhttp.Route{},
|
|
||||||
}
|
|
||||||
|
|
||||||
route, err := rp.IngressHandler(input)
|
|
||||||
assert.NoError(t, err, "failed to generate ingress route")
|
|
||||||
|
|
||||||
expectedCfg, err := os.ReadFile(test.expectedConfigPath)
|
|
||||||
assert.NoError(t, err, "failed to find config file for comparison")
|
|
||||||
|
|
||||||
cfgJson, err := json.Marshal(&route)
|
|
||||||
assert.NoError(t, err, "failed to marshal route to JSON")
|
|
||||||
|
|
||||||
assert.JSONEq(t, string(cfgJson), string(expectedCfg))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMisconfiguredRedirectConvertToCaddyConfig(t *testing.T) {
|
|
||||||
rp := RedirectPlugin{}
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
expectedError string
|
|
||||||
annotations map[string]string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "Check permanent redirect with invalid custom redirect code",
|
|
||||||
annotations: map[string]string{
|
|
||||||
"caddy.ingress.kubernetes.io/permanent-redirect": "http://example.com",
|
|
||||||
"caddy.ingress.kubernetes.io/permanent-redirect-code": "502",
|
|
||||||
},
|
|
||||||
expectedError: "redirection code not in the 3xx range or 401: '502'",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Check permanent redirect with invalid custom redirect code string",
|
|
||||||
annotations: map[string]string{
|
|
||||||
"caddy.ingress.kubernetes.io/permanent-redirect": "http://example.com",
|
|
||||||
"caddy.ingress.kubernetes.io/permanent-redirect-code": "randomstring",
|
|
||||||
},
|
|
||||||
expectedError: "not a supported redirection code type or not a valid integer: 'randomstring'",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Check if both permanent and temporary redirection annotations are set",
|
|
||||||
annotations: map[string]string{
|
|
||||||
"caddy.ingress.kubernetes.io/permanent-redirect": "http://example.com",
|
|
||||||
"caddy.ingress.kubernetes.io/temporal-redirect": "http://example2.com",
|
|
||||||
},
|
|
||||||
expectedError: "cannot use permanent-redirect annotation with temporal-redirect",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
input := converter.IngressMiddlewareInput{
|
|
||||||
Ingress: &networkingv1.Ingress{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Annotations: test.annotations,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Route: &caddyhttp.Route{},
|
|
||||||
}
|
|
||||||
|
|
||||||
route, err := rp.IngressHandler(input)
|
|
||||||
if assert.Error(t, err, "expected an error while generating the ingress route") {
|
|
||||||
assert.EqualError(t, err, test.expectedError)
|
|
||||||
}
|
|
||||||
|
|
||||||
cfgJson, err := json.Marshal(&route)
|
|
||||||
assert.NoError(t, err, "failed to marshal route to JSON")
|
|
||||||
|
|
||||||
assert.JSONEq(t, string(cfgJson), "null")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,103 +0,0 @@
|
|||||||
package ingress
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/netip"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/caddyserver/caddy/v2/caddyconfig"
|
|
||||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
|
||||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp/reverseproxy"
|
|
||||||
"github.com/caddyserver/ingress/pkg/converter"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ReverseProxyPlugin struct{}
|
|
||||||
|
|
||||||
func (p ReverseProxyPlugin) IngressPlugin() converter.PluginInfo {
|
|
||||||
return converter.PluginInfo{
|
|
||||||
Name: "ingress.reverseproxy",
|
|
||||||
// Should always go last by default
|
|
||||||
Priority: -10,
|
|
||||||
New: func() converter.Plugin { return new(ReverseProxyPlugin) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IngressHandler Add a reverse proxy handler to the route
|
|
||||||
func (p ReverseProxyPlugin) IngressHandler(input converter.IngressMiddlewareInput) (*caddyhttp.Route, error) {
|
|
||||||
path := input.Path
|
|
||||||
ing := input.Ingress
|
|
||||||
backendProtocol := strings.ToLower(getAnnotation(ing, backendProtocol))
|
|
||||||
trustedProxiesAnnotation := strings.ToLower(getAnnotation(ing, trustedProxies))
|
|
||||||
|
|
||||||
// TODO :-
|
|
||||||
// when setting the upstream url we should bypass kube-dns and get the ip address of
|
|
||||||
// the pod for the deployment we are proxying to so that we can proxy to that ip address port.
|
|
||||||
// this is good for session affinity and increases performance.
|
|
||||||
clusterHostName := fmt.Sprintf("%v.%v.svc.cluster.local:%d", path.Backend.Service.Name, ing.Namespace, path.Backend.Service.Port.Number)
|
|
||||||
|
|
||||||
transport := &reverseproxy.HTTPTransport{}
|
|
||||||
|
|
||||||
if backendProtocol == "https" {
|
|
||||||
transport.TLS = &reverseproxy.TLSConfig{
|
|
||||||
InsecureSkipVerify: getAnnotationBool(ing, insecureSkipVerify, true),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
var parsedProxies []string
|
|
||||||
if trustedProxiesAnnotation != "" {
|
|
||||||
trustedProxies := strings.Split(trustedProxiesAnnotation, ",")
|
|
||||||
parsedProxies, err = parseTrustedProxies(trustedProxies)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handler := reverseproxy.Handler{
|
|
||||||
TransportRaw: caddyconfig.JSONModuleObject(transport, "protocol", "http", nil),
|
|
||||||
Upstreams: reverseproxy.UpstreamPool{
|
|
||||||
{Dial: clusterHostName},
|
|
||||||
},
|
|
||||||
TrustedProxies: parsedProxies,
|
|
||||||
}
|
|
||||||
|
|
||||||
handlerModule := caddyconfig.JSONModuleObject(
|
|
||||||
handler,
|
|
||||||
"handler",
|
|
||||||
"reverse_proxy",
|
|
||||||
nil,
|
|
||||||
)
|
|
||||||
input.Route.HandlersRaw = append(input.Route.HandlersRaw, handlerModule)
|
|
||||||
return input.Route, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copied from https://github.com/caddyserver/caddy/blob/21af88fefc9a8239a024f635f1c6fdd9defd7eb7/modules/caddyhttp/reverseproxy/reverseproxy.go#L270-L286
|
|
||||||
func parseTrustedProxies(trustedProxies []string) (parsedProxies []string, err error) {
|
|
||||||
for _, trustedProxy := range trustedProxies {
|
|
||||||
trustedProxy = strings.TrimSpace(trustedProxy)
|
|
||||||
if strings.Contains(trustedProxy, "/") {
|
|
||||||
ipNet, err := netip.ParsePrefix(trustedProxy)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to parse IP: %q", trustedProxy)
|
|
||||||
}
|
|
||||||
parsedProxies = append(parsedProxies, ipNet.String())
|
|
||||||
} else {
|
|
||||||
ipAddr, err := netip.ParseAddr(trustedProxy)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to parse IP: %q", trustedProxy)
|
|
||||||
}
|
|
||||||
ipNew := netip.PrefixFrom(ipAddr, ipAddr.BitLen())
|
|
||||||
parsedProxies = append(parsedProxies, ipNew.String())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return parsedProxies, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
converter.RegisterPlugin(ReverseProxyPlugin{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interface guards
|
|
||||||
var (
|
|
||||||
_ = converter.IngressMiddleware(ReverseProxyPlugin{})
|
|
||||||
)
|
|
||||||
@ -1,168 +0,0 @@
|
|||||||
package ingress
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"os"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
|
||||||
"github.com/caddyserver/ingress/pkg/converter"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
networkingv1 "k8s.io/api/networking/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestTrustedProxesConvertToCaddyConfig(t *testing.T) {
|
|
||||||
rpp := ReverseProxyPlugin{}
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
annotations map[string]string
|
|
||||||
expectedConfigPath string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "ipv4 trusted proxies",
|
|
||||||
annotations: map[string]string{
|
|
||||||
"caddy.ingress.kubernetes.io/trusted-proxies": "192.168.1.0, 10.0.0.1",
|
|
||||||
},
|
|
||||||
expectedConfigPath: "test_data/reverseproxy_trusted_proxies_ipv4.json",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "ipv4 trusted proxies wit subnet",
|
|
||||||
annotations: map[string]string{
|
|
||||||
"caddy.ingress.kubernetes.io/trusted-proxies": "192.168.1.0/16,10.0.0.1/8",
|
|
||||||
},
|
|
||||||
expectedConfigPath: "test_data/reverseproxy_trusted_proxies_ipv4_subnet.json",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "ipv6 trusted proxies",
|
|
||||||
annotations: map[string]string{
|
|
||||||
"caddy.ingress.kubernetes.io/trusted-proxies": "2001:db8::1, 2001:db8::5",
|
|
||||||
},
|
|
||||||
expectedConfigPath: "test_data/reverseproxy_trusted_proxies_ipv6.json",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "ipv6 trusted proxies",
|
|
||||||
annotations: map[string]string{
|
|
||||||
"caddy.ingress.kubernetes.io/trusted-proxies": "2001:db8::1/36,2001:db8::5/60",
|
|
||||||
},
|
|
||||||
expectedConfigPath: "test_data/reverseproxy_trusted_proxies_ipv6_subnet.json",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
input := converter.IngressMiddlewareInput{
|
|
||||||
Ingress: &networkingv1.Ingress{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Annotations: test.annotations,
|
|
||||||
Namespace: "namespace",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Path: networkingv1.HTTPIngressPath{
|
|
||||||
Backend: networkingv1.IngressBackend{
|
|
||||||
Service: &networkingv1.IngressServiceBackend{
|
|
||||||
Name: "svcName",
|
|
||||||
Port: networkingv1.ServiceBackendPort{Number: 80},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Route: &caddyhttp.Route{},
|
|
||||||
}
|
|
||||||
|
|
||||||
route, err := rpp.IngressHandler(input)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
expectedCfg, err := os.ReadFile(test.expectedConfigPath)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
cfgJson, err := json.Marshal(&route)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
require.JSONEq(t, string(expectedCfg), string(cfgJson))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestMisconfiguredTrustedProxiesConvertToCaddyConfig(t *testing.T) {
|
|
||||||
rpp := ReverseProxyPlugin{}
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
annotations map[string]string
|
|
||||||
expectedError string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "invalid ipv4 trusted proxy",
|
|
||||||
annotations: map[string]string{
|
|
||||||
"caddy.ingress.kubernetes.io/trusted-proxies": "999.999.999.999",
|
|
||||||
},
|
|
||||||
expectedError: `failed to parse IP: "999.999.999.999"`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid ipv4 with subnet trusted proxy",
|
|
||||||
annotations: map[string]string{
|
|
||||||
"caddy.ingress.kubernetes.io/trusted-proxies": "999.999.999.999/32",
|
|
||||||
},
|
|
||||||
expectedError: `failed to parse IP: "999.999.999.999/32"`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid subnet for ipv4 trusted proxy",
|
|
||||||
annotations: map[string]string{
|
|
||||||
"caddy.ingress.kubernetes.io/trusted-proxies": "10.0.0.0/100",
|
|
||||||
},
|
|
||||||
expectedError: `failed to parse IP: "10.0.0.0/100"`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid ipv6 trusted proxy",
|
|
||||||
annotations: map[string]string{
|
|
||||||
"caddy.ingress.kubernetes.io/trusted-proxies": "2001:db8::g",
|
|
||||||
},
|
|
||||||
expectedError: `failed to parse IP: "2001:db8::g"`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid ipv6 with subnet trusted proxy",
|
|
||||||
annotations: map[string]string{
|
|
||||||
"caddy.ingress.kubernetes.io/trusted-proxies": "2001:db8::g/128",
|
|
||||||
},
|
|
||||||
expectedError: `failed to parse IP: "2001:db8::g/128"`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "invalid subnet for ipv6 trusted proxy",
|
|
||||||
annotations: map[string]string{
|
|
||||||
"caddy.ingress.kubernetes.io/trusted-proxies": "2001:db8::/200",
|
|
||||||
},
|
|
||||||
expectedError: `failed to parse IP: "2001:db8::/200"`,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
input := converter.IngressMiddlewareInput{
|
|
||||||
Ingress: &networkingv1.Ingress{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Annotations: test.annotations,
|
|
||||||
Namespace: "namespace",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Path: networkingv1.HTTPIngressPath{
|
|
||||||
Backend: networkingv1.IngressBackend{
|
|
||||||
Service: &networkingv1.IngressServiceBackend{
|
|
||||||
Name: "svcName",
|
|
||||||
Port: networkingv1.ServiceBackendPort{Number: 80},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Route: &caddyhttp.Route{},
|
|
||||||
}
|
|
||||||
|
|
||||||
route, err := rpp.IngressHandler(input)
|
|
||||||
require.EqualError(t, err, test.expectedError)
|
|
||||||
|
|
||||||
cfgJson, err := json.Marshal(&route)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
require.JSONEq(t, string(cfgJson), "null")
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,53 +0,0 @@
|
|||||||
package ingress
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/caddyserver/caddy/v2/caddyconfig"
|
|
||||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
|
||||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp/rewrite"
|
|
||||||
"github.com/caddyserver/ingress/pkg/converter"
|
|
||||||
)
|
|
||||||
|
|
||||||
type RewritePlugin struct{}
|
|
||||||
|
|
||||||
func (p RewritePlugin) IngressPlugin() converter.PluginInfo {
|
|
||||||
return converter.PluginInfo{
|
|
||||||
Name: "ingress.rewrite",
|
|
||||||
Priority: 10,
|
|
||||||
New: func() converter.Plugin { return new(RewritePlugin) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// IngressHandler Converts rewrite annotations to rewrite handler
|
|
||||||
func (p RewritePlugin) IngressHandler(input converter.IngressMiddlewareInput) (*caddyhttp.Route, error) {
|
|
||||||
ing := input.Ingress
|
|
||||||
|
|
||||||
rewriteTo := getAnnotation(ing, rewriteToAnnotation)
|
|
||||||
if rewriteTo != "" {
|
|
||||||
handler := caddyconfig.JSONModuleObject(
|
|
||||||
rewrite.Rewrite{URI: rewriteTo},
|
|
||||||
"handler", "rewrite", nil,
|
|
||||||
)
|
|
||||||
|
|
||||||
input.Route.HandlersRaw = append(input.Route.HandlersRaw, handler)
|
|
||||||
}
|
|
||||||
|
|
||||||
rewriteStripPrefix := getAnnotation(ing, rewriteStripPrefixAnnotation)
|
|
||||||
if rewriteStripPrefix != "" {
|
|
||||||
handler := caddyconfig.JSONModuleObject(
|
|
||||||
rewrite.Rewrite{StripPathPrefix: rewriteStripPrefix},
|
|
||||||
"handler", "rewrite", nil,
|
|
||||||
)
|
|
||||||
|
|
||||||
input.Route.HandlersRaw = append(input.Route.HandlersRaw, handler)
|
|
||||||
}
|
|
||||||
return input.Route, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
converter.RegisterPlugin(RewritePlugin{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Interface guards
|
|
||||||
var (
|
|
||||||
_ = converter.IngressMiddleware(RewritePlugin{})
|
|
||||||
)
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"handle": [
|
|
||||||
{
|
|
||||||
"handler": "static_response",
|
|
||||||
"headers": {
|
|
||||||
"Location": [
|
|
||||||
"http://example.com"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"status_code": 401
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"handle": [
|
|
||||||
{
|
|
||||||
"handler": "static_response",
|
|
||||||
"headers": {
|
|
||||||
"Location": [
|
|
||||||
"http://example.com"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"status_code": 308
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"handle": [
|
|
||||||
{
|
|
||||||
"handler": "static_response",
|
|
||||||
"headers": {
|
|
||||||
"Location": [
|
|
||||||
"http://example.com"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"status_code": 301
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"handle": [
|
|
||||||
{
|
|
||||||
"handler": "static_response",
|
|
||||||
"headers": {
|
|
||||||
"Location": [
|
|
||||||
"http://example.com"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"status_code": 302
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"handle": [
|
|
||||||
{
|
|
||||||
"handler": "reverse_proxy",
|
|
||||||
"transport": {
|
|
||||||
"protocol": "http"
|
|
||||||
},
|
|
||||||
"trusted_proxies": [
|
|
||||||
"192.168.1.0/32",
|
|
||||||
"10.0.0.1/32"
|
|
||||||
],
|
|
||||||
"upstreams": [
|
|
||||||
{
|
|
||||||
"dial": "svcName.namespace.svc.cluster.local:80"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"handle": [
|
|
||||||
{
|
|
||||||
"handler": "reverse_proxy",
|
|
||||||
"transport": {
|
|
||||||
"protocol": "http"
|
|
||||||
},
|
|
||||||
"trusted_proxies": [
|
|
||||||
"192.168.1.0/16",
|
|
||||||
"10.0.0.1/8"
|
|
||||||
],
|
|
||||||
"upstreams": [
|
|
||||||
{
|
|
||||||
"dial": "svcName.namespace.svc.cluster.local:80"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"handle": [
|
|
||||||
{
|
|
||||||
"handler": "reverse_proxy",
|
|
||||||
"transport": {
|
|
||||||
"protocol": "http"
|
|
||||||
},
|
|
||||||
"trusted_proxies": [
|
|
||||||
"2001:db8::1/128",
|
|
||||||
"2001:db8::5/128"
|
|
||||||
],
|
|
||||||
"upstreams": [
|
|
||||||
{
|
|
||||||
"dial": "svcName.namespace.svc.cluster.local:80"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"handle": [
|
|
||||||
{
|
|
||||||
"handler": "reverse_proxy",
|
|
||||||
"transport": {
|
|
||||||
"protocol": "http"
|
|
||||||
},
|
|
||||||
"trusted_proxies": [
|
|
||||||
"2001:db8::1/36",
|
|
||||||
"2001:db8::5/60"
|
|
||||||
],
|
|
||||||
"upstreams": [
|
|
||||||
{
|
|
||||||
"dial": "svcName.namespace.svc.cluster.local:80"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
{
|
|
||||||
"admin": {},
|
|
||||||
"storage": { "module": "secret_store", "namespace": "", "leaseId": "" },
|
|
||||||
"apps": {
|
|
||||||
"http": {
|
|
||||||
"servers": {
|
|
||||||
"ingress_server": {
|
|
||||||
"listen": [":80", ":443"],
|
|
||||||
"tls_connection_policies": [{}],
|
|
||||||
"automatic_https": {}
|
|
||||||
},
|
|
||||||
"metrics_server": {
|
|
||||||
"listen": [":9765"],
|
|
||||||
"routes": [
|
|
||||||
{
|
|
||||||
"match": [{ "path": ["/healthz"] }],
|
|
||||||
"handle": [{ "handler": "static_response", "status_code": 200 }]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"automatic_https": { "disable": true }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"tls": {}
|
|
||||||
},
|
|
||||||
"logging": {}
|
|
||||||
}
|
|
||||||
@ -1,71 +0,0 @@
|
|||||||
package controller
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/caddyserver/ingress/pkg/store"
|
|
||||||
v1 "k8s.io/api/core/v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ConfigMapAddedAction provides an implementation of the action interface.
|
|
||||||
type ConfigMapAddedAction struct {
|
|
||||||
resource *v1.ConfigMap
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConfigMapUpdatedAction provides an implementation of the action interface.
|
|
||||||
type ConfigMapUpdatedAction struct {
|
|
||||||
resource *v1.ConfigMap
|
|
||||||
oldResource *v1.ConfigMap
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConfigMapDeletedAction provides an implementation of the action interface.
|
|
||||||
type ConfigMapDeletedAction struct {
|
|
||||||
resource *v1.ConfigMap
|
|
||||||
}
|
|
||||||
|
|
||||||
// onConfigMapAdded runs when a configmap is added to the namespace.
|
|
||||||
func (c *CaddyController) onConfigMapAdded(obj *v1.ConfigMap) {
|
|
||||||
c.syncQueue.Add(ConfigMapAddedAction{
|
|
||||||
resource: obj,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// onConfigMapUpdated is run when a configmap is updated in the namespace.
|
|
||||||
func (c *CaddyController) onConfigMapUpdated(old *v1.ConfigMap, new *v1.ConfigMap) {
|
|
||||||
c.syncQueue.Add(ConfigMapUpdatedAction{
|
|
||||||
resource: new,
|
|
||||||
oldResource: old,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// onConfigMapDeleted is run when a configmap is deleted from the namespace.
|
|
||||||
func (c *CaddyController) onConfigMapDeleted(obj *v1.ConfigMap) {
|
|
||||||
c.syncQueue.Add(ConfigMapDeletedAction{
|
|
||||||
resource: obj,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r ConfigMapAddedAction) handle(c *CaddyController) error {
|
|
||||||
c.logger.Infof("ConfigMap created (%s/%s)", r.resource.Namespace, r.resource.Name)
|
|
||||||
|
|
||||||
cfg, err := store.ParseConfigMap(r.resource)
|
|
||||||
if err == nil {
|
|
||||||
c.resourceStore.ConfigMap = cfg
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r ConfigMapUpdatedAction) handle(c *CaddyController) error {
|
|
||||||
c.logger.Infof("ConfigMap updated (%s/%s)", r.resource.Namespace, r.resource.Name)
|
|
||||||
|
|
||||||
cfg, err := store.ParseConfigMap(r.resource)
|
|
||||||
if err == nil {
|
|
||||||
c.resourceStore.ConfigMap = cfg
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r ConfigMapDeletedAction) handle(c *CaddyController) error {
|
|
||||||
c.logger.Infof("ConfigMap deleted (%s/%s)", r.resource.Namespace, r.resource.Name)
|
|
||||||
|
|
||||||
c.resourceStore.ConfigMap = nil
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@ -1,70 +0,0 @@
|
|||||||
package controller
|
|
||||||
|
|
||||||
import (
|
|
||||||
v1 "k8s.io/api/networking/v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
// IngressAddedAction provides an implementation of the action interface.
|
|
||||||
type IngressAddedAction struct {
|
|
||||||
resource *v1.Ingress
|
|
||||||
}
|
|
||||||
|
|
||||||
// IngressUpdatedAction provides an implementation of the action interface.
|
|
||||||
type IngressUpdatedAction struct {
|
|
||||||
resource *v1.Ingress
|
|
||||||
oldResource *v1.Ingress
|
|
||||||
}
|
|
||||||
|
|
||||||
// IngressDeletedAction provides an implementation of the action interface.
|
|
||||||
type IngressDeletedAction struct {
|
|
||||||
resource *v1.Ingress
|
|
||||||
}
|
|
||||||
|
|
||||||
// onIngressAdded runs when an ingress resource is added to the cluster.
|
|
||||||
func (c *CaddyController) onIngressAdded(obj *v1.Ingress) {
|
|
||||||
c.syncQueue.Add(IngressAddedAction{
|
|
||||||
resource: obj,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// onIngressUpdated is run when an ingress resource is updated in the cluster.
|
|
||||||
func (c *CaddyController) onIngressUpdated(old *v1.Ingress, new *v1.Ingress) {
|
|
||||||
c.syncQueue.Add(IngressUpdatedAction{
|
|
||||||
resource: new,
|
|
||||||
oldResource: old,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// onIngressDeleted is run when an ingress resource is deleted from the cluster.
|
|
||||||
func (c *CaddyController) onIngressDeleted(obj *v1.Ingress) {
|
|
||||||
c.syncQueue.Add(IngressDeletedAction{
|
|
||||||
resource: obj,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r IngressAddedAction) handle(c *CaddyController) error {
|
|
||||||
c.logger.Infof("Ingress created (%s/%s)", r.resource.Namespace, r.resource.Name)
|
|
||||||
// add this ingress to the internal store
|
|
||||||
c.resourceStore.AddIngress(r.resource)
|
|
||||||
|
|
||||||
// Ingress may now have a TLS config
|
|
||||||
return c.watchTLSSecrets()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r IngressUpdatedAction) handle(c *CaddyController) error {
|
|
||||||
c.logger.Infof("Ingress updated (%s/%s)", r.resource.Namespace, r.resource.Name)
|
|
||||||
|
|
||||||
// add or update this ingress in the internal store
|
|
||||||
c.resourceStore.AddIngress(r.resource)
|
|
||||||
|
|
||||||
// Ingress may now have a TLS config
|
|
||||||
return c.watchTLSSecrets()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r IngressDeletedAction) handle(c *CaddyController) error {
|
|
||||||
c.logger.Infof("Ingress deleted (%s/%s)", r.resource.Namespace, r.resource.Name)
|
|
||||||
|
|
||||||
// delete all resources from caddy config that are associated with this resource
|
|
||||||
c.resourceStore.PluckIngress(r.resource)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@ -1,139 +0,0 @@
|
|||||||
package controller
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/caddyserver/ingress/internal/k8s"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
"gopkg.in/go-playground/pool.v3"
|
|
||||||
networkingv1 "k8s.io/api/networking/v1"
|
|
||||||
"k8s.io/client-go/kubernetes"
|
|
||||||
)
|
|
||||||
|
|
||||||
// dispatchSync is run every syncInterval duration to sync ingress source address fields.
|
|
||||||
func (c *CaddyController) dispatchSync() {
|
|
||||||
c.syncQueue.Add(SyncStatusAction{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// SyncStatusAction provides an implementation of the action interface.
|
|
||||||
type SyncStatusAction struct {
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle is run when a syncStatusAction appears in the queue.
|
|
||||||
func (r SyncStatusAction) handle(c *CaddyController) error {
|
|
||||||
return c.syncStatus(c.resourceStore.Ingresses)
|
|
||||||
}
|
|
||||||
|
|
||||||
// syncStatus ensures that the ingress source address points to this ingress controller's IP address.
|
|
||||||
func (c *CaddyController) syncStatus(ings []*networkingv1.Ingress) error {
|
|
||||||
addrs, err := k8s.GetAddresses(c.resourceStore.CurrentPod, c.kubeClient)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
c.logger.Debugf("Syncing %d Ingress resources source addresses", len(ings))
|
|
||||||
c.updateIngStatuses(sliceToLoadBalancerIngress(addrs), ings)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// updateIngStatuses starts a queue and adds all monitored ingresses to update their status source address to the on
|
|
||||||
// that the ingress controller is running on. This is called by the syncStatus queue.
|
|
||||||
func (c *CaddyController) updateIngStatuses(controllerAddresses []networkingv1.IngressLoadBalancerIngress, ings []*networkingv1.Ingress) {
|
|
||||||
p := pool.NewLimited(10)
|
|
||||||
defer p.Close()
|
|
||||||
|
|
||||||
batch := p.Batch()
|
|
||||||
sort.SliceStable(controllerAddresses, lessLoadBalancerIngress(controllerAddresses))
|
|
||||||
|
|
||||||
for _, ing := range ings {
|
|
||||||
curIPs := ing.Status.LoadBalancer.Ingress
|
|
||||||
sort.SliceStable(curIPs, lessLoadBalancerIngress(curIPs))
|
|
||||||
|
|
||||||
// check to see if ingresses source address does not match the ingress controller's.
|
|
||||||
if ingressSliceEqual(curIPs, controllerAddresses) {
|
|
||||||
c.logger.Debugf("skipping update of Ingress %v/%v (no change)", ing.Namespace, ing.Name)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
batch.Queue(runUpdate(c.logger, ing, controllerAddresses, c.kubeClient))
|
|
||||||
}
|
|
||||||
|
|
||||||
batch.QueueComplete()
|
|
||||||
batch.WaitAll()
|
|
||||||
}
|
|
||||||
|
|
||||||
// runUpdate updates the ingress status field.
|
|
||||||
func runUpdate(logger *zap.SugaredLogger, ing *networkingv1.Ingress, status []networkingv1.IngressLoadBalancerIngress, client *kubernetes.Clientset) pool.WorkFunc {
|
|
||||||
return func(wu pool.WorkUnit) (interface{}, error) {
|
|
||||||
if wu.IsCancelled() {
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
updated, err := k8s.UpdateIngressStatus(client, ing, status)
|
|
||||||
if err != nil {
|
|
||||||
logger.Warnf("error updating ingress rule: %v", err)
|
|
||||||
} else {
|
|
||||||
logger.Debugf(
|
|
||||||
"updating Ingress %v/%v status from %v to %v",
|
|
||||||
ing.Namespace,
|
|
||||||
ing.Name,
|
|
||||||
ing.Status.LoadBalancer.Ingress,
|
|
||||||
updated.Status.LoadBalancer.Ingress,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ingressSliceEqual determines if the ingress source matches the ingress controller's.
|
|
||||||
func ingressSliceEqual(lhs, rhs []networkingv1.IngressLoadBalancerIngress) bool {
|
|
||||||
if len(lhs) != len(rhs) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
for i := range lhs {
|
|
||||||
if lhs[i].IP != rhs[i].IP {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if lhs[i].Hostname != rhs[i].Hostname {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// lessLoadBalancerIngress is a sorting function for ingress hostnames.
|
|
||||||
func lessLoadBalancerIngress(addrs []networkingv1.IngressLoadBalancerIngress) func(int, int) bool {
|
|
||||||
return func(a, b int) bool {
|
|
||||||
switch strings.Compare(addrs[a].Hostname, addrs[b].Hostname) {
|
|
||||||
case -1:
|
|
||||||
return true
|
|
||||||
case 1:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return addrs[a].IP < addrs[b].IP
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// sliceToLoadBalancerIngress converts a slice of IP and/or hostnames to LoadBalancerIngress
|
|
||||||
func sliceToLoadBalancerIngress(endpoints []string) []networkingv1.IngressLoadBalancerIngress {
|
|
||||||
lbi := []networkingv1.IngressLoadBalancerIngress{}
|
|
||||||
for _, ep := range endpoints {
|
|
||||||
if net.ParseIP(ep) == nil {
|
|
||||||
lbi = append(lbi, networkingv1.IngressLoadBalancerIngress{Hostname: ep})
|
|
||||||
} else {
|
|
||||||
lbi = append(lbi, networkingv1.IngressLoadBalancerIngress{IP: ep})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.SliceStable(lbi, func(a, b int) bool {
|
|
||||||
return lbi[a].IP < lbi[b].IP
|
|
||||||
})
|
|
||||||
|
|
||||||
return lbi
|
|
||||||
}
|
|
||||||
@ -1,124 +0,0 @@
|
|||||||
package controller
|
|
||||||
|
|
||||||
import (
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/caddyserver/ingress/internal/k8s"
|
|
||||||
apiv1 "k8s.io/api/core/v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
var CertFolder = filepath.FromSlash("/etc/caddy/certs")
|
|
||||||
|
|
||||||
// SecretAddedAction provides an implementation of the action interface.
|
|
||||||
type SecretAddedAction struct {
|
|
||||||
resource *apiv1.Secret
|
|
||||||
}
|
|
||||||
|
|
||||||
// SecretUpdatedAction provides an implementation of the action interface.
|
|
||||||
type SecretUpdatedAction struct {
|
|
||||||
resource *apiv1.Secret
|
|
||||||
oldResource *apiv1.Secret
|
|
||||||
}
|
|
||||||
|
|
||||||
// SecretDeletedAction provides an implementation of the action interface.
|
|
||||||
type SecretDeletedAction struct {
|
|
||||||
resource *apiv1.Secret
|
|
||||||
}
|
|
||||||
|
|
||||||
// onSecretAdded runs when a TLS secret resource is added to the cluster.
|
|
||||||
func (c *CaddyController) onSecretAdded(obj *apiv1.Secret) {
|
|
||||||
if k8s.IsManagedTLSSecret(obj, c.resourceStore.Ingresses) {
|
|
||||||
c.syncQueue.Add(SecretAddedAction{
|
|
||||||
resource: obj,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// onSecretUpdated is run when a TLS secret resource is updated in the cluster.
|
|
||||||
func (c *CaddyController) onSecretUpdated(old *apiv1.Secret, new *apiv1.Secret) {
|
|
||||||
if k8s.IsManagedTLSSecret(new, c.resourceStore.Ingresses) {
|
|
||||||
c.syncQueue.Add(SecretUpdatedAction{
|
|
||||||
resource: new,
|
|
||||||
oldResource: old,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// onSecretDeleted is run when a TLS secret resource is deleted from the cluster.
|
|
||||||
func (c *CaddyController) onSecretDeleted(obj *apiv1.Secret) {
|
|
||||||
c.syncQueue.Add(SecretDeletedAction{
|
|
||||||
resource: obj,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// writeFile writes a secret to a .pem file on disk.
|
|
||||||
func writeFile(s *apiv1.Secret) error {
|
|
||||||
content := make([]byte, 0)
|
|
||||||
|
|
||||||
for _, cert := range s.Data {
|
|
||||||
content = append(content, cert...)
|
|
||||||
}
|
|
||||||
|
|
||||||
err := os.WriteFile(filepath.Join(CertFolder, s.Name+".pem"), content, 0644)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r SecretAddedAction) handle(c *CaddyController) error {
|
|
||||||
c.logger.Infof("TLS secret created (%s/%s)", r.resource.Namespace, r.resource.Name)
|
|
||||||
return writeFile(r.resource)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r SecretUpdatedAction) handle(c *CaddyController) error {
|
|
||||||
c.logger.Infof("TLS secret updated (%s/%s)", r.resource.Namespace, r.resource.Name)
|
|
||||||
return writeFile(r.resource)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r SecretDeletedAction) handle(c *CaddyController) error {
|
|
||||||
c.logger.Infof("TLS secret deleted (%s/%s)", r.resource.Namespace, r.resource.Name)
|
|
||||||
return os.Remove(filepath.Join(CertFolder, r.resource.Name+".pem"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// watchTLSSecrets Start listening to TLS secrets if at least one ingress needs it.
|
|
||||||
// It will sync the CertFolder with TLS secrets
|
|
||||||
func (c *CaddyController) watchTLSSecrets() error {
|
|
||||||
if c.informers.TLSSecret == nil && c.resourceStore.HasManagedTLS() {
|
|
||||||
// Init informers
|
|
||||||
params := k8s.TLSSecretParams{
|
|
||||||
InformerFactory: c.factories.WatchedNamespace,
|
|
||||||
}
|
|
||||||
c.informers.TLSSecret = k8s.WatchTLSSecrets(params, k8s.TLSSecretHandlers{
|
|
||||||
AddFunc: c.onSecretAdded,
|
|
||||||
UpdateFunc: c.onSecretUpdated,
|
|
||||||
DeleteFunc: c.onSecretDeleted,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Run it
|
|
||||||
go c.informers.TLSSecret.Run(c.stopChan)
|
|
||||||
|
|
||||||
// Sync secrets
|
|
||||||
secrets, err := k8s.ListTLSSecrets(params, c.resourceStore.Ingresses)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := os.Stat(CertFolder); os.IsNotExist(err) {
|
|
||||||
err = os.MkdirAll(CertFolder, 0755)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, secret := range secrets {
|
|
||||||
if err := writeFile(secret); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@ -1,272 +0,0 @@
|
|||||||
package controller
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"context"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/caddyserver/caddy/v2"
|
|
||||||
"github.com/caddyserver/certmagic"
|
|
||||||
"github.com/caddyserver/ingress/internal/k8s"
|
|
||||||
"github.com/caddyserver/ingress/pkg/store"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
networkingv1 "k8s.io/api/networking/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/util/runtime"
|
|
||||||
"k8s.io/apimachinery/pkg/util/wait"
|
|
||||||
"k8s.io/client-go/informers"
|
|
||||||
"k8s.io/client-go/kubernetes"
|
|
||||||
"k8s.io/client-go/tools/cache"
|
|
||||||
"k8s.io/client-go/util/workqueue"
|
|
||||||
|
|
||||||
// load required caddy plugins
|
|
||||||
_ "github.com/caddyserver/caddy/v2/modules/caddyhttp/proxyprotocol"
|
|
||||||
_ "github.com/caddyserver/caddy/v2/modules/caddyhttp/reverseproxy"
|
|
||||||
_ "github.com/caddyserver/caddy/v2/modules/caddytls"
|
|
||||||
_ "github.com/caddyserver/caddy/v2/modules/caddytls/standardstek"
|
|
||||||
_ "github.com/caddyserver/caddy/v2/modules/metrics"
|
|
||||||
_ "github.com/caddyserver/ingress/pkg/storage"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// how often we should attempt to keep ingress resource's source address in sync
|
|
||||||
syncInterval = time.Second * 30
|
|
||||||
|
|
||||||
// how often we resync informers resources (besides receiving updates)
|
|
||||||
resourcesSyncInterval = time.Hour * 1
|
|
||||||
)
|
|
||||||
|
|
||||||
// Action is an interface for ingress actions.
|
|
||||||
type Action interface {
|
|
||||||
handle(c *CaddyController) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Informer defines the required SharedIndexInformers that interact with the API server.
|
|
||||||
type Informer struct {
|
|
||||||
Ingress cache.SharedIndexInformer
|
|
||||||
ConfigMap cache.SharedIndexInformer
|
|
||||||
TLSSecret cache.SharedIndexInformer
|
|
||||||
}
|
|
||||||
|
|
||||||
// InformerFactory contains shared informer factory
|
|
||||||
// We need to type of factory:
|
|
||||||
// - One used to watch resources in the Pod namespaces (caddy config, secrets...)
|
|
||||||
// - Another one for Ingress resources in the selected namespace
|
|
||||||
type InformerFactory struct {
|
|
||||||
PodNamespace informers.SharedInformerFactory
|
|
||||||
WatchedNamespace informers.SharedInformerFactory
|
|
||||||
}
|
|
||||||
|
|
||||||
type Converter interface {
|
|
||||||
ConvertToCaddyConfig(store *store.Store) (interface{}, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// CaddyController represents a caddy ingress controller.
|
|
||||||
type CaddyController struct {
|
|
||||||
resourceStore *store.Store
|
|
||||||
|
|
||||||
kubeClient *kubernetes.Clientset
|
|
||||||
|
|
||||||
logger *zap.SugaredLogger
|
|
||||||
|
|
||||||
// main queue syncing ingresses, configmaps, ... with caddy
|
|
||||||
syncQueue workqueue.RateLimitingInterface
|
|
||||||
|
|
||||||
// informer factories
|
|
||||||
factories *InformerFactory
|
|
||||||
|
|
||||||
// informer contains the cache Informers
|
|
||||||
informers *Informer
|
|
||||||
|
|
||||||
// save last applied caddy config
|
|
||||||
lastAppliedConfig []byte
|
|
||||||
|
|
||||||
converter Converter
|
|
||||||
|
|
||||||
stopChan chan struct{}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewCaddyController(
|
|
||||||
logger *zap.SugaredLogger,
|
|
||||||
kubeClient *kubernetes.Clientset,
|
|
||||||
opts store.Options,
|
|
||||||
converter Converter,
|
|
||||||
stopChan chan struct{},
|
|
||||||
) *CaddyController {
|
|
||||||
controller := &CaddyController{
|
|
||||||
logger: logger,
|
|
||||||
kubeClient: kubeClient,
|
|
||||||
converter: converter,
|
|
||||||
stopChan: stopChan,
|
|
||||||
syncQueue: workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter()),
|
|
||||||
informers: &Informer{},
|
|
||||||
factories: &InformerFactory{},
|
|
||||||
}
|
|
||||||
|
|
||||||
podInfo, err := k8s.GetPodDetails(kubeClient)
|
|
||||||
if err != nil {
|
|
||||||
logger.Fatalf("Unexpected error obtaining pod information: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create informer factories
|
|
||||||
controller.factories.PodNamespace = informers.NewSharedInformerFactoryWithOptions(
|
|
||||||
kubeClient,
|
|
||||||
resourcesSyncInterval,
|
|
||||||
informers.WithNamespace(podInfo.Namespace),
|
|
||||||
)
|
|
||||||
controller.factories.WatchedNamespace = informers.NewSharedInformerFactoryWithOptions(
|
|
||||||
kubeClient,
|
|
||||||
resourcesSyncInterval,
|
|
||||||
informers.WithNamespace(opts.WatchNamespace),
|
|
||||||
)
|
|
||||||
|
|
||||||
// Watch ingress resources in selected namespaces
|
|
||||||
ingressParams := k8s.IngressParams{
|
|
||||||
InformerFactory: controller.factories.WatchedNamespace,
|
|
||||||
ClassName: opts.ClassName,
|
|
||||||
ClassNameRequired: opts.ClassNameRequired,
|
|
||||||
}
|
|
||||||
controller.informers.Ingress = k8s.WatchIngresses(ingressParams, k8s.IngressHandlers{
|
|
||||||
AddFunc: controller.onIngressAdded,
|
|
||||||
UpdateFunc: controller.onIngressUpdated,
|
|
||||||
DeleteFunc: controller.onIngressDeleted,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Watch Configmap in the pod's namespace for global options
|
|
||||||
cmOptionsParams := k8s.ConfigMapParams{
|
|
||||||
Namespace: podInfo.Namespace,
|
|
||||||
InformerFactory: controller.factories.PodNamespace,
|
|
||||||
ConfigMapName: opts.ConfigMapName,
|
|
||||||
}
|
|
||||||
controller.informers.ConfigMap = k8s.WatchConfigMaps(cmOptionsParams, k8s.ConfigMapHandlers{
|
|
||||||
AddFunc: controller.onConfigMapAdded,
|
|
||||||
UpdateFunc: controller.onConfigMapUpdated,
|
|
||||||
DeleteFunc: controller.onConfigMapDeleted,
|
|
||||||
})
|
|
||||||
|
|
||||||
// Create resource store
|
|
||||||
controller.resourceStore = store.NewStore(opts, podInfo)
|
|
||||||
|
|
||||||
return controller
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shutdown stops the caddy controller.
|
|
||||||
func (c *CaddyController) Shutdown() error {
|
|
||||||
// remove this ingress controller's ip from ingress resources.
|
|
||||||
c.updateIngStatuses([]networkingv1.IngressLoadBalancerIngress{{}}, c.resourceStore.Ingresses)
|
|
||||||
|
|
||||||
if err := caddy.Stop(); err != nil {
|
|
||||||
c.logger.Error("failed to stop caddy server", zap.Error(err))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
certmagic.CleanUpOwnLocks(context.TODO(), c.logger.Desugar())
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run method starts the ingress controller.
|
|
||||||
func (c *CaddyController) Run() {
|
|
||||||
defer runtime.HandleCrash()
|
|
||||||
defer c.syncQueue.ShutDown()
|
|
||||||
|
|
||||||
// start informers where we listen to new / updated resources
|
|
||||||
go c.informers.ConfigMap.Run(c.stopChan)
|
|
||||||
go c.informers.Ingress.Run(c.stopChan)
|
|
||||||
|
|
||||||
// wait for all involved caches to be synced before processing items
|
|
||||||
// from the queue
|
|
||||||
if !cache.WaitForCacheSync(c.stopChan,
|
|
||||||
c.informers.ConfigMap.HasSynced,
|
|
||||||
c.informers.Ingress.HasSynced,
|
|
||||||
) {
|
|
||||||
runtime.HandleError(fmt.Errorf("timed out waiting for caches to sync"))
|
|
||||||
}
|
|
||||||
|
|
||||||
// start processing events for syncing ingress resources
|
|
||||||
go wait.Until(c.runWorker, time.Second, c.stopChan)
|
|
||||||
|
|
||||||
// start ingress status syncher and run every syncInterval
|
|
||||||
go wait.Until(c.dispatchSync, syncInterval, c.stopChan)
|
|
||||||
|
|
||||||
// wait for SIGTERM
|
|
||||||
<-c.stopChan
|
|
||||||
c.logger.Info("stopping ingress controller")
|
|
||||||
|
|
||||||
var exitCode int
|
|
||||||
err := c.Shutdown()
|
|
||||||
if err != nil {
|
|
||||||
c.logger.Error("could not shutdown ingress controller properly, " + err.Error())
|
|
||||||
exitCode = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
os.Exit(exitCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
// runWorker processes items in the event queue.
|
|
||||||
func (c *CaddyController) runWorker() {
|
|
||||||
for c.processNextItem() {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// processNextItem determines if there is an ingress item in the event queue and processes it.
|
|
||||||
func (c *CaddyController) processNextItem() bool {
|
|
||||||
// Wait until there is a new item in the working queue
|
|
||||||
action, quit := c.syncQueue.Get()
|
|
||||||
if quit {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tell the queue that we are done with processing this key. This unblocks the key for other workers
|
|
||||||
// This allows safe parallel processing because two ingresses with the same key are never processed in
|
|
||||||
// parallel.
|
|
||||||
defer c.syncQueue.Done(action)
|
|
||||||
|
|
||||||
// Invoke the method containing the business logic
|
|
||||||
err := action.(Action).handle(c)
|
|
||||||
if err != nil {
|
|
||||||
c.handleErr(err, action)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
err = c.reloadCaddy()
|
|
||||||
if err != nil {
|
|
||||||
c.logger.Error("could not reload caddy: " + err.Error())
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// handleErrs reports errors received from queue actions.
|
|
||||||
//
|
|
||||||
//goland:noinspection GoUnusedParameter
|
|
||||||
func (c *CaddyController) handleErr(err error, action interface{}) {
|
|
||||||
c.logger.Error(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
// reloadCaddy generate a caddy config from controller's store
|
|
||||||
func (c *CaddyController) reloadCaddy() error {
|
|
||||||
config, err := c.converter.ConvertToCaddyConfig(c.resourceStore)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
j, err := json.Marshal(config)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if bytes.Equal(c.lastAppliedConfig, j) {
|
|
||||||
c.logger.Debug("caddy config did not change, skipping reload")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
c.logger.Debug("reloading caddy with config", string(j))
|
|
||||||
err = caddy.Load(j, false)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("could not reload caddy config %v", err.Error())
|
|
||||||
}
|
|
||||||
c.lastAppliedConfig = j
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@ -1,54 +0,0 @@
|
|||||||
package k8s
|
|
||||||
|
|
||||||
import (
|
|
||||||
v1 "k8s.io/api/core/v1"
|
|
||||||
"k8s.io/client-go/informers"
|
|
||||||
"k8s.io/client-go/tools/cache"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ConfigMapHandlers struct {
|
|
||||||
AddFunc func(obj *v1.ConfigMap)
|
|
||||||
UpdateFunc func(oldObj, newObj *v1.ConfigMap)
|
|
||||||
DeleteFunc func(obj *v1.ConfigMap)
|
|
||||||
}
|
|
||||||
|
|
||||||
type ConfigMapParams struct {
|
|
||||||
Namespace string
|
|
||||||
InformerFactory informers.SharedInformerFactory
|
|
||||||
ConfigMapName string
|
|
||||||
}
|
|
||||||
|
|
||||||
func isControllerConfigMap(cm *v1.ConfigMap, name string) bool {
|
|
||||||
return cm.GetName() == name
|
|
||||||
}
|
|
||||||
|
|
||||||
func WatchConfigMaps(options ConfigMapParams, funcs ConfigMapHandlers) cache.SharedIndexInformer {
|
|
||||||
informer := options.InformerFactory.Core().V1().ConfigMaps().Informer()
|
|
||||||
|
|
||||||
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
|
||||||
AddFunc: func(obj interface{}) {
|
|
||||||
cm, ok := obj.(*v1.ConfigMap)
|
|
||||||
|
|
||||||
if ok && isControllerConfigMap(cm, options.ConfigMapName) {
|
|
||||||
funcs.AddFunc(cm)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
UpdateFunc: func(oldObj, newObj interface{}) {
|
|
||||||
oldCM, ok1 := oldObj.(*v1.ConfigMap)
|
|
||||||
newCM, ok2 := newObj.(*v1.ConfigMap)
|
|
||||||
|
|
||||||
if ok1 && ok2 && isControllerConfigMap(newCM, options.ConfigMapName) {
|
|
||||||
funcs.UpdateFunc(oldCM, newCM)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
DeleteFunc: func(obj interface{}) {
|
|
||||||
cm, ok := obj.(*v1.ConfigMap)
|
|
||||||
|
|
||||||
if ok && isControllerConfigMap(cm, options.ConfigMapName) {
|
|
||||||
funcs.DeleteFunc(cm)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return informer
|
|
||||||
}
|
|
||||||
@ -1,82 +0,0 @@
|
|||||||
package k8s
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
networkingv1 "k8s.io/api/networking/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/client-go/informers"
|
|
||||||
"k8s.io/client-go/kubernetes"
|
|
||||||
"k8s.io/client-go/tools/cache"
|
|
||||||
)
|
|
||||||
|
|
||||||
type IngressHandlers struct {
|
|
||||||
AddFunc func(obj *networkingv1.Ingress)
|
|
||||||
UpdateFunc func(oldObj, newObj *networkingv1.Ingress)
|
|
||||||
DeleteFunc func(obj *networkingv1.Ingress)
|
|
||||||
}
|
|
||||||
|
|
||||||
type IngressParams struct {
|
|
||||||
InformerFactory informers.SharedInformerFactory
|
|
||||||
ClassName string
|
|
||||||
ClassNameRequired bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func WatchIngresses(options IngressParams, funcs IngressHandlers) cache.SharedIndexInformer {
|
|
||||||
informer := options.InformerFactory.Networking().V1().Ingresses().Informer()
|
|
||||||
|
|
||||||
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
|
||||||
AddFunc: func(obj interface{}) {
|
|
||||||
ingress, ok := obj.(*networkingv1.Ingress)
|
|
||||||
|
|
||||||
if ok && isControllerIngress(options, ingress) {
|
|
||||||
funcs.AddFunc(ingress)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
UpdateFunc: func(oldObj, newObj interface{}) {
|
|
||||||
oldIng, ok1 := oldObj.(*networkingv1.Ingress)
|
|
||||||
newIng, ok2 := newObj.(*networkingv1.Ingress)
|
|
||||||
|
|
||||||
if ok1 && ok2 && isControllerIngress(options, newIng) {
|
|
||||||
funcs.UpdateFunc(oldIng, newIng)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
DeleteFunc: func(obj interface{}) {
|
|
||||||
ingress, ok := obj.(*networkingv1.Ingress)
|
|
||||||
|
|
||||||
if ok && isControllerIngress(options, ingress) {
|
|
||||||
funcs.DeleteFunc(ingress)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return informer
|
|
||||||
}
|
|
||||||
|
|
||||||
// isControllerIngress check if the ingress object can be controlled by us
|
|
||||||
func isControllerIngress(options IngressParams, ingress *networkingv1.Ingress) bool {
|
|
||||||
ingressClass := ingress.Annotations["kubernetes.io/ingress.class"]
|
|
||||||
if ingressClass == "" && ingress.Spec.IngressClassName != nil {
|
|
||||||
ingressClass = *ingress.Spec.IngressClassName
|
|
||||||
}
|
|
||||||
|
|
||||||
if !options.ClassNameRequired && ingressClass == "" {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
return ingressClass == options.ClassName
|
|
||||||
}
|
|
||||||
|
|
||||||
func UpdateIngressStatus(kubeClient *kubernetes.Clientset, ing *networkingv1.Ingress, status []networkingv1.IngressLoadBalancerIngress) (*networkingv1.Ingress, error) {
|
|
||||||
ingClient := kubeClient.NetworkingV1().Ingresses(ing.Namespace)
|
|
||||||
|
|
||||||
currIng, err := ingClient.Get(context.TODO(), ing.Name, metav1.GetOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unexpected error searching Ingress %v/%v: %w", ing.Namespace, ing.Name, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
currIng.Status.LoadBalancer.Ingress = status
|
|
||||||
|
|
||||||
return ingClient.UpdateStatus(context.TODO(), currIng, metav1.UpdateOptions{})
|
|
||||||
}
|
|
||||||
@ -1,99 +0,0 @@
|
|||||||
package k8s
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"github.com/caddyserver/ingress/pkg/store"
|
|
||||||
|
|
||||||
apiv1 "k8s.io/api/core/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
|
||||||
"k8s.io/client-go/kubernetes"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetAddresses gets the ip address or name of the node in the cluster that the
|
|
||||||
// ingress controller is running on.
|
|
||||||
func GetAddresses(p *store.PodInfo, kubeClient *kubernetes.Clientset) ([]string, error) {
|
|
||||||
var addrs []string
|
|
||||||
|
|
||||||
// Get services that may select this pod
|
|
||||||
svcs, err := kubeClient.CoreV1().Services(p.Namespace).List(context.TODO(), metav1.ListOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, svc := range svcs.Items {
|
|
||||||
if isSubset(svc.Spec.Selector, p.Labels) {
|
|
||||||
addr := GetAddressFromService(&svc)
|
|
||||||
if addr != "" {
|
|
||||||
addrs = append(addrs, addr)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return addrs, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copied from https://github.com/kubernetes/kubernetes/pull/95179
|
|
||||||
func isSubset(subSet, superSet labels.Set) bool {
|
|
||||||
if len(superSet) == 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range subSet {
|
|
||||||
value, ok := superSet[k]
|
|
||||||
if !ok {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if value != v {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAddressFromService returns the IP address or the name of a node in the cluster
|
|
||||||
func GetAddressFromService(service *apiv1.Service) string {
|
|
||||||
switch service.Spec.Type {
|
|
||||||
case apiv1.ServiceTypeNodePort:
|
|
||||||
case apiv1.ServiceTypeClusterIP:
|
|
||||||
if service.Spec.ClusterIP != apiv1.ClusterIPNone {
|
|
||||||
return service.Spec.ClusterIP
|
|
||||||
}
|
|
||||||
case apiv1.ServiceTypeExternalName:
|
|
||||||
return service.Spec.ExternalName
|
|
||||||
case apiv1.ServiceTypeLoadBalancer:
|
|
||||||
if len(service.Status.LoadBalancer.Ingress) > 0 {
|
|
||||||
ingress := service.Status.LoadBalancer.Ingress[0]
|
|
||||||
if ingress.Hostname != "" {
|
|
||||||
return ingress.Hostname
|
|
||||||
}
|
|
||||||
return ingress.IP
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPodDetails returns runtime information about the pod:
|
|
||||||
// name, namespace and IP of the node where it is running
|
|
||||||
func GetPodDetails(kubeClient *kubernetes.Clientset) (*store.PodInfo, error) {
|
|
||||||
podName := os.Getenv("POD_NAME")
|
|
||||||
podNs := os.Getenv("POD_NAMESPACE")
|
|
||||||
|
|
||||||
if podName == "" || podNs == "" {
|
|
||||||
return nil, fmt.Errorf("unable to get POD information (missing POD_NAME or POD_NAMESPACE environment variable")
|
|
||||||
}
|
|
||||||
|
|
||||||
pod, _ := kubeClient.CoreV1().Pods(podNs).Get(context.TODO(), podName, metav1.GetOptions{})
|
|
||||||
if pod == nil {
|
|
||||||
return nil, fmt.Errorf("unable to get POD information")
|
|
||||||
}
|
|
||||||
|
|
||||||
return &store.PodInfo{
|
|
||||||
Name: podName,
|
|
||||||
Namespace: podNs,
|
|
||||||
Labels: pod.GetLabels(),
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
@ -1,76 +0,0 @@
|
|||||||
package k8s
|
|
||||||
|
|
||||||
import (
|
|
||||||
v12 "k8s.io/api/core/v1"
|
|
||||||
v1 "k8s.io/api/networking/v1"
|
|
||||||
"k8s.io/client-go/informers"
|
|
||||||
"k8s.io/client-go/tools/cache"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TLSSecretHandlers struct {
|
|
||||||
AddFunc func(obj *v12.Secret)
|
|
||||||
UpdateFunc func(oldObj, newObj *v12.Secret)
|
|
||||||
DeleteFunc func(obj *v12.Secret)
|
|
||||||
}
|
|
||||||
|
|
||||||
type TLSSecretParams struct {
|
|
||||||
InformerFactory informers.SharedInformerFactory
|
|
||||||
}
|
|
||||||
|
|
||||||
func WatchTLSSecrets(options TLSSecretParams, funcs TLSSecretHandlers) cache.SharedIndexInformer {
|
|
||||||
informer := options.InformerFactory.Core().V1().Secrets().Informer()
|
|
||||||
|
|
||||||
informer.AddEventHandler(cache.ResourceEventHandlerFuncs{
|
|
||||||
AddFunc: func(obj interface{}) {
|
|
||||||
secret, ok := obj.(*v12.Secret)
|
|
||||||
|
|
||||||
if ok && secret.Type == v12.SecretTypeTLS {
|
|
||||||
funcs.AddFunc(secret)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
UpdateFunc: func(oldObj, newObj interface{}) {
|
|
||||||
oldSecret, ok1 := oldObj.(*v12.Secret)
|
|
||||||
newSecret, ok2 := newObj.(*v12.Secret)
|
|
||||||
|
|
||||||
if ok1 && ok2 && newSecret.Type == v12.SecretTypeTLS {
|
|
||||||
funcs.UpdateFunc(oldSecret, newSecret)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
DeleteFunc: func(obj interface{}) {
|
|
||||||
secret, ok := obj.(*v12.Secret)
|
|
||||||
|
|
||||||
if ok && secret.Type == v12.SecretTypeTLS {
|
|
||||||
funcs.DeleteFunc(secret)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
return informer
|
|
||||||
}
|
|
||||||
|
|
||||||
func ListTLSSecrets(options TLSSecretParams, ings []*v1.Ingress) ([]*v12.Secret, error) {
|
|
||||||
lister := options.InformerFactory.Core().V1().Secrets().Lister()
|
|
||||||
|
|
||||||
tlsSecrets := []*v12.Secret{}
|
|
||||||
for _, ing := range ings {
|
|
||||||
for _, tlsRule := range ing.Spec.TLS {
|
|
||||||
secret, err := lister.Secrets(ing.Namespace).Get(tlsRule.SecretName)
|
|
||||||
// TODO Handle errors
|
|
||||||
if err == nil {
|
|
||||||
tlsSecrets = append(tlsSecrets, secret)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return tlsSecrets, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func IsManagedTLSSecret(secret *v12.Secret, ings []*v1.Ingress) bool {
|
|
||||||
for _, ing := range ings {
|
|
||||||
for _, tlsRule := range ing.Spec.TLS {
|
|
||||||
if tlsRule.SecretName == secret.Name && ing.Namespace == secret.Namespace {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
apiVersion: v1
|
|
||||||
kind: ConfigMap
|
|
||||||
metadata:
|
|
||||||
name: caddy-global-options
|
|
||||||
namespace: caddy-system
|
|
||||||
data:
|
|
||||||
acmeCA: internal
|
|
||||||
# email: test@example.com
|
|
||||||
# debug: "false"
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: example1
|
|
||||||
labels:
|
|
||||||
app: example1
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: example1
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: example1
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: httpecho
|
|
||||||
image: hashicorp/http-echo
|
|
||||||
args:
|
|
||||||
- "-listen=:8080"
|
|
||||||
- '-text="hello world 1"'
|
|
||||||
ports:
|
|
||||||
- containerPort: 8080
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
apiVersion: apps/v1
|
|
||||||
kind: Deployment
|
|
||||||
metadata:
|
|
||||||
name: example2
|
|
||||||
labels:
|
|
||||||
app: example2
|
|
||||||
spec:
|
|
||||||
replicas: 1
|
|
||||||
selector:
|
|
||||||
matchLabels:
|
|
||||||
app: example2
|
|
||||||
template:
|
|
||||||
metadata:
|
|
||||||
labels:
|
|
||||||
app: example2
|
|
||||||
spec:
|
|
||||||
containers:
|
|
||||||
- name: httpecho
|
|
||||||
image: hashicorp/http-echo
|
|
||||||
args:
|
|
||||||
- "-listen=:8080"
|
|
||||||
- '-text="hello world 2"'
|
|
||||||
ports:
|
|
||||||
- containerPort: 8080
|
|
||||||
@ -1,46 +0,0 @@
|
|||||||
apiVersion: networking.k8s.io/v1
|
|
||||||
kind: Ingress
|
|
||||||
metadata:
|
|
||||||
name: example
|
|
||||||
annotations:
|
|
||||||
kubernetes.io/ingress.class: caddy
|
|
||||||
spec:
|
|
||||||
rules:
|
|
||||||
- host: example1.kubernetes.localhost
|
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
- path: /hello1
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: example1
|
|
||||||
port:
|
|
||||||
number: 8080
|
|
||||||
- path: /hello2
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: example2
|
|
||||||
port:
|
|
||||||
number: 8080
|
|
||||||
- host: example2.kubernetes.localhost
|
|
||||||
http:
|
|
||||||
paths:
|
|
||||||
- path: /hello1
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: example1
|
|
||||||
port:
|
|
||||||
number: 8080
|
|
||||||
- path: /hello2
|
|
||||||
pathType: Prefix
|
|
||||||
backend:
|
|
||||||
service:
|
|
||||||
name: example2
|
|
||||||
port:
|
|
||||||
number: 8080
|
|
||||||
# tls:
|
|
||||||
# - secretName: ssl-example2.kubernetes.localhost
|
|
||||||
# hosts:
|
|
||||||
# - example2.caddy.dev
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
kind: Service
|
|
||||||
apiVersion: v1
|
|
||||||
metadata:
|
|
||||||
name: example1
|
|
||||||
spec:
|
|
||||||
type: ClusterIP
|
|
||||||
selector:
|
|
||||||
app: example1
|
|
||||||
ports:
|
|
||||||
- name: http
|
|
||||||
protocol: TCP
|
|
||||||
port: 8080
|
|
||||||
targetPort: 8080
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
kind: Service
|
|
||||||
apiVersion: v1
|
|
||||||
metadata:
|
|
||||||
name: example2
|
|
||||||
spec:
|
|
||||||
type: ClusterIP
|
|
||||||
selector:
|
|
||||||
app: example2
|
|
||||||
ports:
|
|
||||||
- name: http
|
|
||||||
protocol: TCP
|
|
||||||
port: 8080
|
|
||||||
targetPort: 8080
|
|
||||||
@ -1,65 +0,0 @@
|
|||||||
package converter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/caddyserver/caddy/v2"
|
|
||||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
|
||||||
"github.com/caddyserver/caddy/v2/modules/caddytls"
|
|
||||||
)
|
|
||||||
|
|
||||||
// StorageValues represents the config for certmagic storage providers.
|
|
||||||
type StorageValues struct {
|
|
||||||
Namespace string `json:"namespace"`
|
|
||||||
LeaseId string `json:"leaseId"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Storage represents the certmagic storage configuration.
|
|
||||||
type Storage struct {
|
|
||||||
System string `json:"module"`
|
|
||||||
StorageValues
|
|
||||||
}
|
|
||||||
|
|
||||||
// Config represents a caddy2 config file.
|
|
||||||
type Config struct {
|
|
||||||
Admin caddy.AdminConfig `json:"admin,omitempty"`
|
|
||||||
Storage Storage `json:"storage"`
|
|
||||||
Apps map[string]interface{} `json:"apps"`
|
|
||||||
Logging caddy.Logging `json:"logging"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Config) GetHTTPServer() *caddyhttp.Server {
|
|
||||||
return c.Apps["http"].(*caddyhttp.App).Servers[HttpServer]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Config) GetMetricsServer() *caddyhttp.Server {
|
|
||||||
return c.Apps["http"].(*caddyhttp.App).Servers[MetricsServer]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (c Config) GetTLSApp() *caddytls.TLS {
|
|
||||||
return c.Apps["tls"].(*caddytls.TLS)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewConfig() *Config {
|
|
||||||
return &Config{
|
|
||||||
Logging: caddy.Logging{},
|
|
||||||
Apps: map[string]interface{}{
|
|
||||||
"tls": &caddytls.TLS{CertificatesRaw: caddy.ModuleMap{}},
|
|
||||||
"http": &caddyhttp.App{
|
|
||||||
Servers: map[string]*caddyhttp.Server{
|
|
||||||
HttpServer: {
|
|
||||||
AutoHTTPS: &caddyhttp.AutoHTTPSConfig{},
|
|
||||||
// Listen to both :80 and :443 ports in order
|
|
||||||
// to use the same listener wrappers (PROXY protocol use it)
|
|
||||||
Listen: []string{":80", ":443"},
|
|
||||||
TLSConnPolicies: caddytls.ConnectionPolicies{
|
|
||||||
&caddytls.ConnectionPolicy{},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
MetricsServer: {
|
|
||||||
Listen: []string{":9765"},
|
|
||||||
AutoHTTPS: &caddyhttp.AutoHTTPSConfig{Disabled: true},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,113 +0,0 @@
|
|||||||
package converter
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sort"
|
|
||||||
|
|
||||||
"github.com/caddyserver/caddy/v2/modules/caddyhttp"
|
|
||||||
"github.com/caddyserver/ingress/pkg/store"
|
|
||||||
v1 "k8s.io/api/networking/v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
HttpServer = "ingress_server"
|
|
||||||
MetricsServer = "metrics_server"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GlobalMiddleware is called with a default caddy config
|
|
||||||
// already configured with:
|
|
||||||
// - Secret storage store
|
|
||||||
// - A TLS App (https://caddyserver.com/docs/json/apps/tls/)
|
|
||||||
// - A HTTP App with an HTTP server listening to 80 443 ports (https://caddyserver.com/docs/json/apps/http/)
|
|
||||||
type GlobalMiddleware interface {
|
|
||||||
GlobalHandler(config *Config, store *store.Store) error
|
|
||||||
}
|
|
||||||
|
|
||||||
type IngressMiddlewareInput struct {
|
|
||||||
Config *Config
|
|
||||||
Store *store.Store
|
|
||||||
Ingress *v1.Ingress
|
|
||||||
Rule v1.IngressRule
|
|
||||||
Path v1.HTTPIngressPath
|
|
||||||
Route *caddyhttp.Route
|
|
||||||
}
|
|
||||||
|
|
||||||
// IngressMiddleware is called for each Caddy route that is generated for a specific
|
|
||||||
// ingress. It allows anyone to manipulate caddy routes before sending it to caddy.
|
|
||||||
type IngressMiddleware interface {
|
|
||||||
IngressHandler(input IngressMiddlewareInput) (*caddyhttp.Route, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Plugin interface {
|
|
||||||
IngressPlugin() PluginInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
type PluginInfo struct {
|
|
||||||
Name string
|
|
||||||
Priority int
|
|
||||||
New func() Plugin
|
|
||||||
}
|
|
||||||
|
|
||||||
func RegisterPlugin(m Plugin) {
|
|
||||||
plugin := m.IngressPlugin()
|
|
||||||
|
|
||||||
if _, ok := plugins[plugin.Name]; ok {
|
|
||||||
panic(fmt.Sprintf("plugin already registered: %s", plugin.Name))
|
|
||||||
}
|
|
||||||
plugins[plugin.Name] = plugin
|
|
||||||
pluginInstances[plugin.Name] = plugin.New()
|
|
||||||
}
|
|
||||||
|
|
||||||
func getOrderIndex(order []string, plugin string) int {
|
|
||||||
for idx, o := range order {
|
|
||||||
if plugin == o {
|
|
||||||
return idx
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
func sortPlugins(plugins []PluginInfo, order []string) []PluginInfo {
|
|
||||||
sort.SliceStable(plugins, func(i, j int) bool {
|
|
||||||
iPlugin, jPlugin := plugins[i], plugins[j]
|
|
||||||
|
|
||||||
iSortedIdx := getOrderIndex(order, iPlugin.Name)
|
|
||||||
jSortedIdx := getOrderIndex(order, jPlugin.Name)
|
|
||||||
|
|
||||||
if iSortedIdx != jSortedIdx {
|
|
||||||
return iSortedIdx > jSortedIdx
|
|
||||||
}
|
|
||||||
|
|
||||||
if iPlugin.Priority != jPlugin.Priority {
|
|
||||||
return iPlugin.Priority > jPlugin.Priority
|
|
||||||
}
|
|
||||||
return iPlugin.Name < jPlugin.Name
|
|
||||||
|
|
||||||
})
|
|
||||||
return plugins
|
|
||||||
}
|
|
||||||
|
|
||||||
// Plugins return a sorted array of plugin instances.
|
|
||||||
// Sort is made following these rules:
|
|
||||||
// - Plugins specified in the order slice will always go first (in the order specified in the slice)
|
|
||||||
// - A Plugin with higher priority will go before a plugin with lower priority
|
|
||||||
// - If 2 plugins have the same priority (and not in order slice), they will be sorted by plugin name
|
|
||||||
func Plugins(order []string) []Plugin {
|
|
||||||
sortedPlugins := make([]PluginInfo, 0, len(plugins))
|
|
||||||
for _, p := range plugins {
|
|
||||||
sortedPlugins = append(sortedPlugins, p)
|
|
||||||
}
|
|
||||||
|
|
||||||
sortPlugins(sortedPlugins, order)
|
|
||||||
|
|
||||||
pluginArr := make([]Plugin, 0, len(plugins))
|
|
||||||
for _, p := range sortedPlugins {
|
|
||||||
pluginArr = append(pluginArr, pluginInstances[p.Name])
|
|
||||||
}
|
|
||||||
return pluginArr
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
plugins = make(map[string]PluginInfo)
|
|
||||||
pluginInstances = make(map[string]Plugin)
|
|
||||||
)
|
|
||||||
@ -1,53 +0,0 @@
|
|||||||
package converter
|
|
||||||
|
|
||||||
import "testing"
|
|
||||||
|
|
||||||
func TestSortPlugins(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
order []string
|
|
||||||
plugins []PluginInfo
|
|
||||||
expect []string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "default to alpha sort",
|
|
||||||
order: nil,
|
|
||||||
plugins: []PluginInfo{{Name: "b"}, {Name: "c"}, {Name: "a"}},
|
|
||||||
expect: []string{"a", "b", "c"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "use priority when specified",
|
|
||||||
order: nil,
|
|
||||||
plugins: []PluginInfo{{Name: "b"}, {Name: "a", Priority: 20}, {Name: "c", Priority: 10}},
|
|
||||||
expect: []string{"a", "c", "b"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "fallback to alpha when no priority",
|
|
||||||
order: nil,
|
|
||||||
plugins: []PluginInfo{{Name: "b"}, {Name: "a"}, {Name: "c", Priority: 20}},
|
|
||||||
expect: []string{"c", "a", "b"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "specify order",
|
|
||||||
order: []string{"c"},
|
|
||||||
plugins: []PluginInfo{{Name: "b"}, {Name: "a"}, {Name: "c"}},
|
|
||||||
expect: []string{"c", "a", "b"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "order overrides other settings",
|
|
||||||
order: []string{"c"},
|
|
||||||
plugins: []PluginInfo{{Name: "b", Priority: 10}, {Name: "a"}, {Name: "c"}},
|
|
||||||
expect: []string{"c", "b", "a"},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
sortPlugins(test.plugins, test.order)
|
|
||||||
for i, plugin := range test.plugins {
|
|
||||||
if test.expect[i] != plugin.Name {
|
|
||||||
t.Errorf("expected order to match %v: got %v, expected %v", test.expect, plugin.Name, test.expect[i])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,46 +0,0 @@
|
|||||||
package proxy
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net"
|
|
||||||
|
|
||||||
"github.com/caddyserver/caddy/v2"
|
|
||||||
"github.com/pires/go-proxyproto"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
_ = caddy.Provisioner(&Wrapper{})
|
|
||||||
_ = caddy.Module(&Wrapper{})
|
|
||||||
_ = caddy.ListenerWrapper(&Wrapper{})
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
caddy.RegisterModule(Wrapper{})
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wrapper provides PROXY protocol support to Caddy by implementing the caddy.ListenerWrapper interface.
|
|
||||||
// It must be loaded before the `tls` listener.
|
|
||||||
//
|
|
||||||
// Deprecated: This caddy module should be replaced by the included proxy_protocol listener in Caddy.
|
|
||||||
type Wrapper struct {
|
|
||||||
policy proxyproto.PolicyFunc
|
|
||||||
}
|
|
||||||
|
|
||||||
func (Wrapper) CaddyModule() caddy.ModuleInfo {
|
|
||||||
return caddy.ModuleInfo{
|
|
||||||
ID: "caddy.listeners.proxy_protocol",
|
|
||||||
New: func() caddy.Module { return new(Wrapper) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pp *Wrapper) Provision(ctx caddy.Context) error {
|
|
||||||
pp.policy = func(upstream net.Addr) (proxyproto.Policy, error) {
|
|
||||||
return proxyproto.REQUIRE, nil
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pp *Wrapper) WrapListener(l net.Listener) net.Listener {
|
|
||||||
pL := &proxyproto.Listener{Listener: l, Policy: pp.policy}
|
|
||||||
|
|
||||||
return pL
|
|
||||||
}
|
|
||||||
@ -1,290 +0,0 @@
|
|||||||
package storage
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"io/fs"
|
|
||||||
"regexp"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/caddyserver/caddy/v2"
|
|
||||||
"github.com/caddyserver/certmagic"
|
|
||||||
"github.com/google/uuid"
|
|
||||||
"go.uber.org/zap"
|
|
||||||
corev1 "k8s.io/api/core/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/api/errors"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/apimachinery/pkg/labels"
|
|
||||||
"k8s.io/client-go/kubernetes"
|
|
||||||
"k8s.io/client-go/tools/clientcmd"
|
|
||||||
"k8s.io/client-go/tools/leaderelection/resourcelock"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
leaseDuration = 5 * time.Second
|
|
||||||
leaseRenewInterval = 2 * time.Second
|
|
||||||
leasePollInterval = 5 * time.Second
|
|
||||||
leasePrefix = "caddy-lock-"
|
|
||||||
|
|
||||||
keyPrefix = "caddy.ingress--"
|
|
||||||
)
|
|
||||||
|
|
||||||
// matchLabels are attached to each resource so that they can be found in the future.
|
|
||||||
var matchLabels = map[string]string{
|
|
||||||
"manager": "caddy",
|
|
||||||
}
|
|
||||||
|
|
||||||
// specialChars is a regex that matches all special characters except '-'.
|
|
||||||
var specialChars = regexp.MustCompile("[^\\da-zA-Z-]+")
|
|
||||||
|
|
||||||
// cleanKey strips all special characters that are not supported by kubernetes names and converts them to a '.'.
|
|
||||||
// sequences like '.*.' are also converted to a single '.'.
|
|
||||||
func cleanKey(key string, prefix string) string {
|
|
||||||
return prefix + specialChars.ReplaceAllString(key, ".")
|
|
||||||
}
|
|
||||||
|
|
||||||
// SecretStorage facilitates storing certificates retrieved by certmagic in kubernetes secrets.
|
|
||||||
type SecretStorage struct {
|
|
||||||
Namespace string
|
|
||||||
LeaseId string
|
|
||||||
|
|
||||||
kubeClient *kubernetes.Clientset
|
|
||||||
logger *zap.Logger
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
caddy.RegisterModule(SecretStorage{})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (SecretStorage) CaddyModule() caddy.ModuleInfo {
|
|
||||||
return caddy.ModuleInfo{
|
|
||||||
ID: "caddy.storage.secret_store",
|
|
||||||
New: func() caddy.Module { return new(SecretStorage) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Provisions the SecretStorage instance.
|
|
||||||
func (s *SecretStorage) Provision(ctx caddy.Context) error {
|
|
||||||
config, _ := clientcmd.BuildConfigFromFlags("", "")
|
|
||||||
// creates the clientset
|
|
||||||
clientset, _ := kubernetes.NewForConfig(config)
|
|
||||||
|
|
||||||
s.logger = ctx.Logger(s)
|
|
||||||
s.kubeClient = clientset
|
|
||||||
if s.LeaseId == "" {
|
|
||||||
s.LeaseId = uuid.New().String()
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CertMagicStorage returns a certmagic storage type to be used by caddy.
|
|
||||||
func (s *SecretStorage) CertMagicStorage() (certmagic.Storage, error) {
|
|
||||||
return s, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exists returns true if key exists in fs.
|
|
||||||
func (s *SecretStorage) Exists(ctx context.Context, key string) bool {
|
|
||||||
s.logger.Debug("finding secret", zap.String("name", key))
|
|
||||||
secrets, err := s.kubeClient.CoreV1().Secrets(s.Namespace).List(context.TODO(), metav1.ListOptions{
|
|
||||||
FieldSelector: fmt.Sprintf("metadata.name=%v", cleanKey(key, keyPrefix)),
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
var found bool
|
|
||||||
for _, i := range secrets.Items {
|
|
||||||
if i.ObjectMeta.Name == cleanKey(key, keyPrefix) {
|
|
||||||
found = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return found
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store saves value at key. More than certs and keys are stored by certmagic in secrets.
|
|
||||||
func (s *SecretStorage) Store(ctx context.Context, key string, value []byte) error {
|
|
||||||
se := corev1.Secret{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
Name: cleanKey(key, keyPrefix),
|
|
||||||
Labels: matchLabels,
|
|
||||||
},
|
|
||||||
Data: map[string][]byte{
|
|
||||||
"value": value,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
if s.Exists(ctx, key) {
|
|
||||||
s.logger.Debug("creating secret", zap.String("name", key))
|
|
||||||
_, err = s.kubeClient.CoreV1().Secrets(s.Namespace).Update(context.TODO(), &se, metav1.UpdateOptions{})
|
|
||||||
} else {
|
|
||||||
s.logger.Debug("updating secret", zap.String("name", key))
|
|
||||||
_, err = s.kubeClient.CoreV1().Secrets(s.Namespace).Create(context.TODO(), &se, metav1.CreateOptions{})
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load retrieves the value at the given key.
|
|
||||||
func (s *SecretStorage) Load(ctx context.Context, key string) ([]byte, error) {
|
|
||||||
secret, err := s.kubeClient.CoreV1().Secrets(s.Namespace).Get(context.TODO(), cleanKey(key, keyPrefix), metav1.GetOptions{})
|
|
||||||
if err != nil {
|
|
||||||
if errors.IsNotFound(err) {
|
|
||||||
return nil, fs.ErrNotExist
|
|
||||||
}
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
s.logger.Debug("loading secret", zap.String("name", key))
|
|
||||||
return secret.Data["value"], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete deletes the value at the given key.
|
|
||||||
func (s *SecretStorage) Delete(ctx context.Context, key string) error {
|
|
||||||
err := s.kubeClient.CoreV1().Secrets(s.Namespace).Delete(context.TODO(), cleanKey(key, keyPrefix), metav1.DeleteOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
s.logger.Debug("deleting secret", zap.String("name", key))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// List returns all keys that match prefix.
|
|
||||||
func (s *SecretStorage) List(ctx context.Context, prefix string, recursive bool) ([]string, error) {
|
|
||||||
var keys []string
|
|
||||||
|
|
||||||
s.logger.Debug("listing secrets", zap.String("name", prefix))
|
|
||||||
secrets, err := s.kubeClient.CoreV1().Secrets(s.Namespace).List(context.TODO(), metav1.ListOptions{
|
|
||||||
LabelSelector: labels.SelectorFromSet(matchLabels).String(),
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return keys, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO :- do we need to handle the recursive flag?
|
|
||||||
for _, secret := range secrets.Items {
|
|
||||||
key := secret.ObjectMeta.Name
|
|
||||||
if strings.HasPrefix(key, cleanKey(prefix, keyPrefix)) {
|
|
||||||
keys = append(keys, strings.TrimPrefix(key, keyPrefix))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return keys, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Stat returns information about key.
|
|
||||||
func (s *SecretStorage) Stat(ctx context.Context, key string) (certmagic.KeyInfo, error) {
|
|
||||||
secret, err := s.kubeClient.CoreV1().Secrets(s.Namespace).Get(context.TODO(), cleanKey(key, keyPrefix), metav1.GetOptions{})
|
|
||||||
if err != nil {
|
|
||||||
return certmagic.KeyInfo{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
s.logger.Debug("stats secret", zap.String("name", key))
|
|
||||||
|
|
||||||
return certmagic.KeyInfo{
|
|
||||||
Key: key,
|
|
||||||
Modified: secret.GetCreationTimestamp().UTC(),
|
|
||||||
Size: int64(len(secret.Data["value"])),
|
|
||||||
IsTerminal: false,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SecretStorage) Lock(ctx context.Context, key string) error {
|
|
||||||
for {
|
|
||||||
_, err := s.tryAcquireOrRenew(ctx, cleanKey(key, leasePrefix), false)
|
|
||||||
if err == nil {
|
|
||||||
go s.keepLockUpdated(ctx, cleanKey(key, leasePrefix))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case <-time.After(leasePollInterval):
|
|
||||||
case <-ctx.Done():
|
|
||||||
return ctx.Err()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SecretStorage) keepLockUpdated(ctx context.Context, key string) {
|
|
||||||
for {
|
|
||||||
time.Sleep(leaseRenewInterval)
|
|
||||||
done, err := s.tryAcquireOrRenew(ctx, key, true)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if done {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SecretStorage) tryAcquireOrRenew(ctx context.Context, key string, shouldExist bool) (bool, error) {
|
|
||||||
now := metav1.Now()
|
|
||||||
lock := resourcelock.LeaseLock{
|
|
||||||
LeaseMeta: metav1.ObjectMeta{
|
|
||||||
Name: key,
|
|
||||||
Namespace: s.Namespace,
|
|
||||||
},
|
|
||||||
Client: s.kubeClient.CoordinationV1(),
|
|
||||||
LockConfig: resourcelock.ResourceLockConfig{
|
|
||||||
Identity: s.LeaseId,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
ler := resourcelock.LeaderElectionRecord{
|
|
||||||
HolderIdentity: lock.Identity(),
|
|
||||||
LeaseDurationSeconds: 5,
|
|
||||||
AcquireTime: now,
|
|
||||||
RenewTime: now,
|
|
||||||
}
|
|
||||||
|
|
||||||
currLer, _, err := lock.Get(ctx)
|
|
||||||
|
|
||||||
// 1. obtain or create the ElectionRecord
|
|
||||||
if err != nil {
|
|
||||||
if !errors.IsNotFound(err) {
|
|
||||||
return true, err
|
|
||||||
}
|
|
||||||
if shouldExist {
|
|
||||||
return true, nil // Lock has been released
|
|
||||||
}
|
|
||||||
if err = lock.Create(ctx, ler); err != nil {
|
|
||||||
return true, err
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Record obtained, check the Identity & Time
|
|
||||||
if currLer.HolderIdentity != "" &&
|
|
||||||
currLer.RenewTime.Add(leaseDuration).After(now.Time) &&
|
|
||||||
currLer.HolderIdentity != lock.Identity() {
|
|
||||||
return true, fmt.Errorf("lock is held by %v and has not yet expired", currLer.HolderIdentity)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. We're going to try to update the existing one
|
|
||||||
if currLer.HolderIdentity == lock.Identity() {
|
|
||||||
ler.AcquireTime = currLer.AcquireTime
|
|
||||||
ler.LeaderTransitions = currLer.LeaderTransitions
|
|
||||||
} else {
|
|
||||||
ler.LeaderTransitions = currLer.LeaderTransitions + 1
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = lock.Update(ctx, ler); err != nil {
|
|
||||||
return true, fmt.Errorf("failed to update lock: %v", err)
|
|
||||||
}
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *SecretStorage) Unlock(ctx context.Context, key string) error {
|
|
||||||
err := s.kubeClient.CoordinationV1().Leases(s.Namespace).Delete(context.TODO(), cleanKey(key, leasePrefix), metav1.DeleteOptions{})
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
@ -1,63 +0,0 @@
|
|||||||
package store
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"reflect"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/caddyserver/caddy/v2"
|
|
||||||
"github.com/mitchellh/mapstructure"
|
|
||||||
apiv1 "k8s.io/api/core/v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ConfigMapOptions represents global options set through a configmap
|
|
||||||
type ConfigMapOptions struct {
|
|
||||||
Debug bool `json:"debug,omitempty"`
|
|
||||||
AcmeCA string `json:"acmeCA,omitempty"`
|
|
||||||
AcmeEABKeyId string `json:"acmeEABKeyId,omitempty"`
|
|
||||||
AcmeEABMacKey string `json:"acmeEABMacKey,omitempty"`
|
|
||||||
Email string `json:"email,omitempty"`
|
|
||||||
ExperimentalSmartSort bool `json:"experimentalSmartSort,omitempty"`
|
|
||||||
ProxyProtocol bool `json:"proxyProtocol,omitempty"`
|
|
||||||
Metrics bool `json:"metrics,omitempty"`
|
|
||||||
OnDemandTLS bool `json:"onDemandTLS,omitempty"`
|
|
||||||
OnDemandAsk string `json:"onDemandAsk,omitempty"`
|
|
||||||
OCSPCheckInterval caddy.Duration `json:"ocspCheckInterval,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func stringToCaddyDurationHookFunc() mapstructure.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(caddy.Duration(time.Second)) {
|
|
||||||
return data, nil
|
|
||||||
}
|
|
||||||
return caddy.ParseDuration(data.(string))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseConfigMap(cm *apiv1.ConfigMap) (*ConfigMapOptions, error) {
|
|
||||||
// parse configmap
|
|
||||||
cfgMap := ConfigMapOptions{}
|
|
||||||
config := &mapstructure.DecoderConfig{
|
|
||||||
Metadata: nil,
|
|
||||||
WeaklyTypedInput: true,
|
|
||||||
Result: &cfgMap,
|
|
||||||
TagName: "json",
|
|
||||||
DecodeHook: mapstructure.ComposeDecodeHookFunc(
|
|
||||||
stringToCaddyDurationHookFunc(),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
|
|
||||||
decoder, err := mapstructure.NewDecoder(config)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unexpected error creating decoder: %w", err)
|
|
||||||
}
|
|
||||||
err = decoder.Decode(cm.Data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("unexpected error parsing configmap: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return &cfgMap, nil
|
|
||||||
}
|
|
||||||
@ -1,12 +0,0 @@
|
|||||||
package store
|
|
||||||
|
|
||||||
// Options represents ingress controller config received through cli arguments.
|
|
||||||
type Options struct {
|
|
||||||
WatchNamespace string
|
|
||||||
ConfigMapName string
|
|
||||||
ClassName string
|
|
||||||
ClassNameRequired bool
|
|
||||||
Verbose bool
|
|
||||||
LeaseId string
|
|
||||||
PluginsOrder []string
|
|
||||||
}
|
|
||||||
@ -1,10 +0,0 @@
|
|||||||
package store
|
|
||||||
|
|
||||||
// PodInfo contains runtime information about the pod running the Ingress controller
|
|
||||||
type PodInfo struct {
|
|
||||||
Name string
|
|
||||||
Namespace string
|
|
||||||
// Labels selectors of the running pod
|
|
||||||
// This is used to search for other Ingress controller pods
|
|
||||||
Labels map[string]string
|
|
||||||
}
|
|
||||||
@ -1,72 +0,0 @@
|
|||||||
package store
|
|
||||||
|
|
||||||
import (
|
|
||||||
v1 "k8s.io/api/networking/v1"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Store contains resources used to generate Caddy config
|
|
||||||
type Store struct {
|
|
||||||
Options *Options
|
|
||||||
ConfigMap *ConfigMapOptions
|
|
||||||
Ingresses []*v1.Ingress
|
|
||||||
CurrentPod *PodInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStore returns a new store that keeps track of K8S resources needed by the controller.
|
|
||||||
func NewStore(opts Options, podInfo *PodInfo) *Store {
|
|
||||||
s := &Store{
|
|
||||||
Options: &opts,
|
|
||||||
Ingresses: []*v1.Ingress{},
|
|
||||||
ConfigMap: &ConfigMapOptions{},
|
|
||||||
CurrentPod: podInfo,
|
|
||||||
}
|
|
||||||
return s
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddIngress adds an ingress to the store. It updates the element at the given index if it is unique.
|
|
||||||
func (s *Store) AddIngress(ing *v1.Ingress) {
|
|
||||||
isUniq := true
|
|
||||||
|
|
||||||
for i := range s.Ingresses {
|
|
||||||
in := s.Ingresses[i]
|
|
||||||
if in.GetUID() == ing.GetUID() {
|
|
||||||
isUniq = false
|
|
||||||
s.Ingresses[i] = ing
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if isUniq {
|
|
||||||
s.Ingresses = append(s.Ingresses, ing)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// PluckIngress removes the ingress passed in as an argument from the stores list of ingresses.
|
|
||||||
func (s *Store) PluckIngress(ing *v1.Ingress) {
|
|
||||||
id := ing.GetUID()
|
|
||||||
|
|
||||||
var index int
|
|
||||||
var hasMatch bool
|
|
||||||
for i := range s.Ingresses {
|
|
||||||
if s.Ingresses[i].GetUID() == id {
|
|
||||||
index = i
|
|
||||||
hasMatch = true
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// since order is not important we can swap the element to delete with the one at the end of the slice
|
|
||||||
// and then set ingresses to the n-1 first elements
|
|
||||||
if hasMatch {
|
|
||||||
s.Ingresses[len(s.Ingresses)-1], s.Ingresses[index] = s.Ingresses[index], s.Ingresses[len(s.Ingresses)-1]
|
|
||||||
s.Ingresses = s.Ingresses[:len(s.Ingresses)-1]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (s *Store) HasManagedTLS() bool {
|
|
||||||
for _, ing := range s.Ingresses {
|
|
||||||
if len(ing.Spec.TLS) > 0 {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
@ -1,200 +0,0 @@
|
|||||||
package store
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
v1 "k8s.io/api/networking/v1"
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
typev1 "k8s.io/apimachinery/pkg/types"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStoreIngresses(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
addIngresses []string
|
|
||||||
removeIngresses []string
|
|
||||||
expectCount int
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "No ingress added nor removed",
|
|
||||||
addIngresses: []string{},
|
|
||||||
removeIngresses: []string{},
|
|
||||||
expectCount: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "One ingress added, no ingress removed",
|
|
||||||
addIngresses: []string{"first"},
|
|
||||||
removeIngresses: []string{},
|
|
||||||
expectCount: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Two ingresses added, no ingress removed",
|
|
||||||
addIngresses: []string{"first", "second"},
|
|
||||||
removeIngresses: []string{},
|
|
||||||
expectCount: 2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Two ingresses added, first ingress removed",
|
|
||||||
addIngresses: []string{"first", "second"},
|
|
||||||
removeIngresses: []string{"first"},
|
|
||||||
expectCount: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Two ingresses added, second ingress removed",
|
|
||||||
addIngresses: []string{"first", "second"},
|
|
||||||
removeIngresses: []string{"second"},
|
|
||||||
expectCount: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Two ingresses added, both ingresses removed",
|
|
||||||
addIngresses: []string{"first", "second"},
|
|
||||||
removeIngresses: []string{"second", "first"},
|
|
||||||
expectCount: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Two ingresses added, one existing and one non existing ingress removed",
|
|
||||||
addIngresses: []string{"first", "second"},
|
|
||||||
removeIngresses: []string{"second", "third"},
|
|
||||||
expectCount: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Remove non existing ingresses",
|
|
||||||
addIngresses: []string{"first", "second"},
|
|
||||||
removeIngresses: []string{"third", "forth"},
|
|
||||||
expectCount: 2,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Remove non existing ingresses from an empty list",
|
|
||||||
addIngresses: []string{},
|
|
||||||
removeIngresses: []string{"first", "second"},
|
|
||||||
expectCount: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Add the same ingress multiple time",
|
|
||||||
addIngresses: []string{"first", "first", "first"},
|
|
||||||
removeIngresses: []string{},
|
|
||||||
expectCount: 1,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Remove the same ingress multiple time",
|
|
||||||
addIngresses: []string{"first", "second"},
|
|
||||||
removeIngresses: []string{"second", "second", "second"},
|
|
||||||
expectCount: 1,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
s := NewStore(Options{}, &PodInfo{})
|
|
||||||
for _, uid := range test.addIngresses {
|
|
||||||
i := createIngress(uid)
|
|
||||||
s.AddIngress(&i)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, uid := range test.removeIngresses {
|
|
||||||
i := createIngress(uid)
|
|
||||||
s.PluckIngress(&i)
|
|
||||||
}
|
|
||||||
|
|
||||||
if test.expectCount != len(s.Ingresses) {
|
|
||||||
t.Errorf("Number of ingresses do not match expectation in %s: got %v, expected %v", test.name, len(s.Ingresses), test.expectCount)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestStoreReturnIfHasManagedTLS(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
ingresses []v1.Ingress
|
|
||||||
expect bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "No ingress",
|
|
||||||
ingresses: []v1.Ingress{},
|
|
||||||
expect: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "One ingress without certificate",
|
|
||||||
ingresses: []v1.Ingress{
|
|
||||||
createIngress("first"),
|
|
||||||
},
|
|
||||||
expect: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "One ingress with certificate",
|
|
||||||
ingresses: []v1.Ingress{
|
|
||||||
createIngressTLS("first", []string{"host1"}, "mysecret1"),
|
|
||||||
},
|
|
||||||
expect: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Multiple ingresses without certificate",
|
|
||||||
ingresses: []v1.Ingress{
|
|
||||||
createIngress("first"),
|
|
||||||
createIngress("second"),
|
|
||||||
createIngress("third"),
|
|
||||||
},
|
|
||||||
expect: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Three ingresses mixed with and without certificate",
|
|
||||||
ingresses: []v1.Ingress{
|
|
||||||
createIngressTLS("first", []string{"host1a", "host1b"}, "mysecret1"),
|
|
||||||
createIngress("second"),
|
|
||||||
createIngressTLS("third", []string{"host3"}, "mysecret3"),
|
|
||||||
},
|
|
||||||
expect: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Ingress replaced without a certificate",
|
|
||||||
ingresses: []v1.Ingress{
|
|
||||||
createIngressTLS("first", []string{"host1a", "host1b"}, "mysecret1"),
|
|
||||||
createIngress("first"),
|
|
||||||
},
|
|
||||||
expect: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Ingress replaced with a certificate",
|
|
||||||
ingresses: []v1.Ingress{
|
|
||||||
createIngress("first"),
|
|
||||||
createIngressTLS("first", []string{"host1a", "host1b"}, "mysecret1"),
|
|
||||||
},
|
|
||||||
expect: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.name, func(t *testing.T) {
|
|
||||||
s := NewStore(Options{}, &PodInfo{})
|
|
||||||
for _, i := range test.ingresses {
|
|
||||||
s.AddIngress(&i)
|
|
||||||
}
|
|
||||||
|
|
||||||
if test.expect != s.HasManagedTLS() {
|
|
||||||
t.Errorf("managed TLS do not match expectation in %s: got %v, expected %v", test.name, s.HasManagedTLS(), test.expect)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func createIngressTLS(uid string, hosts []string, secret string) v1.Ingress {
|
|
||||||
i := createIngress(uid)
|
|
||||||
|
|
||||||
i.Spec = v1.IngressSpec{
|
|
||||||
TLS: []v1.IngressTLS{
|
|
||||||
{
|
|
||||||
Hosts: hosts,
|
|
||||||
SecretName: secret,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
|
|
||||||
func createIngress(uid string) v1.Ingress {
|
|
||||||
return v1.Ingress{
|
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
|
||||||
UID: typev1.UID(uid),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,37 +0,0 @@
|
|||||||
apiVersion: skaffold/v4beta7
|
|
||||||
kind: Config
|
|
||||||
metadata:
|
|
||||||
name: caddy-ingress-controller
|
|
||||||
build:
|
|
||||||
artifacts:
|
|
||||||
- image: caddy-ingress
|
|
||||||
context: .
|
|
||||||
docker:
|
|
||||||
dockerfile: Dockerfile.dev
|
|
||||||
deploy:
|
|
||||||
helm:
|
|
||||||
releases:
|
|
||||||
- name: caddy-ingress-development
|
|
||||||
namespace: caddy-system
|
|
||||||
chartPath: charts/caddy-ingress-controller
|
|
||||||
createNamespace: true
|
|
||||||
setValueTemplates:
|
|
||||||
image:
|
|
||||||
repository: "{{ .IMAGE_REPO_NO_DOMAIN_caddy_ingress }}"
|
|
||||||
tag: "{{.IMAGE_TAG_caddy_ingress}}@{{.IMAGE_DIGEST_caddy_ingress}}"
|
|
||||||
manifests:
|
|
||||||
rawYaml:
|
|
||||||
- ./kubernetes/sample/*.yaml
|
|
||||||
portForward:
|
|
||||||
- resourceType: service
|
|
||||||
resourceName: caddy-ingress-development-caddy-ingress-controller
|
|
||||||
namespace: caddy-system
|
|
||||||
address: 0.0.0.0
|
|
||||||
port: 80
|
|
||||||
localPort: 8080
|
|
||||||
- resourceType: service
|
|
||||||
resourceName: caddy-ingress-development-caddy-ingress-controller
|
|
||||||
namespace: caddy-system
|
|
||||||
address: 0.0.0.0
|
|
||||||
port: 443
|
|
||||||
localPort: 8443
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user