feat: node transformer

Node transformer allows you to add extra labels/annotations and modify platform metadata.

Signed-off-by: Serge Logvinov <serge.logvinov@sinextra.dev>
This commit is contained in:
Serge Logvinov
2024-04-21 08:51:56 +03:00
parent db305e7f51
commit 22e398449c
24 changed files with 1194 additions and 146 deletions

View File

@@ -35,10 +35,10 @@ jobs:
go-version-file: 'go.mod' go-version-file: 'go.mod'
- name: Lint - name: Lint
uses: golangci/golangci-lint-action@v4 uses: golangci/golangci-lint-action@v5
with: with:
version: v1.56.2 version: v1.57.1
args: --config=.golangci.yml args: --timeout=5m --config=.golangci.yml
- name: Build - name: Build
timeout-minutes: 10 timeout-minutes: 10
run: make images run: make images

View File

@@ -24,8 +24,6 @@ jobs:
- name: Install Helm - name: Install Helm
uses: azure/setup-helm@v4 uses: azure/setup-helm@v4
with:
version: v3.12.2
- name: Install Cosign - name: Install Cosign
uses: sigstore/cosign-installer@v3.5.0 uses: sigstore/cosign-installer@v3.5.0

View File

@@ -3,7 +3,6 @@
# options for analysis running # options for analysis running
run: run:
go: '1.21'
# default concurrency is a available CPU number # default concurrency is a available CPU number
# concurrency: 4 # concurrency: 4
@@ -20,7 +19,8 @@ run:
# won't be reported. Default value is empty list, but there is # won't be reported. Default value is empty list, but there is
# no need to include all autogenerated files, we confidently recognize # no need to include all autogenerated files, we confidently recognize
# autogenerated files. If it's not please let us know. # autogenerated files. If it's not please let us know.
skip-files: exclude-files:
- charts/
- docs/ - docs/
# list of build tags, all linters use it. Default is empty list. # list of build tags, all linters use it. Default is empty list.
@@ -34,7 +34,13 @@ run:
# output configuration options # output configuration options
output: output:
# colored-line-number|line-number|json|tab|checkstyle, default is "colored-line-number" # colored-line-number|line-number|json|tab|checkstyle, default is "colored-line-number"
format: line-number formats:
- format: line-number
path: stdout
print-issued-lines: true
print-linter-name: true
uniq-by-line: true
sort-results: true
# all available settings of specific linters # all available settings of specific linters
linters-settings: linters-settings:
@@ -46,15 +52,13 @@ linters-settings:
# report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`; # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`;
# default is false: such cases aren't reported by default. # default is false: such cases aren't reported by default.
check-blank: true check-blank: true
govet: govet: {}
# report about shadowed variables
check-shadowing: true
gofmt: gofmt:
# simplify code: gofmt with `-s` option, true by default # simplify code: gofmt with `-s` option, true by default
simplify: true simplify: true
gocyclo: gocyclo:
# minimal code complexity to report, 30 by default (but we recommend 10-20) # minimal code complexity to report, 30 by default (but we recommend 10-20)
min-complexity: 15 min-complexity: 20
maligned: maligned:
# print struct with more effective memory layout or not, false by default # print struct with more effective memory layout or not, false by default
suggest-new: true suggest-new: true
@@ -66,6 +70,8 @@ linters-settings:
min-len: 3 min-len: 3
# minimal occurrences count to trigger, 3 by default # minimal occurrences count to trigger, 3 by default
min-occurrences: 3 min-occurrences: 3
depguard:
list-type: blacklist
misspell: misspell:
# Correct spellings using locale preferences for US or UK. # Correct spellings using locale preferences for US or UK.
# Default is to use a neutral variety of English. # Default is to use a neutral variety of English.
@@ -167,11 +173,21 @@ linters:
- wrapcheck - wrapcheck
- perfsprint - perfsprint
# temporarily disabled linters
- copyloopvar
# https://github.com/golangci/golangci-lint/issues/4606
- intrange
# abandoned linters for which golangci shows the warning that the repo is archived by the owner # abandoned linters for which golangci shows the warning that the repo is archived by the owner
- golint - golint
- interfacer - interfacer
- maligned - maligned
- scopelint - scopelint
- varcheck
- structcheck
- deadcode
- ifshort
- perfsprint
disable-all: false disable-all: false
fast: false fast: false

View File

@@ -11,5 +11,5 @@ keywords:
maintainers: maintainers:
- name: sergelogvinov - name: sergelogvinov
url: https://github.com/sergelogvinov url: https://github.com/sergelogvinov
version: 0.2.3 version: 0.3.0
appVersion: "v1.6.0" appVersion: "v1.6.0"

View File

@@ -1,6 +1,6 @@
# talos-cloud-controller-manager # talos-cloud-controller-manager
![Version: 0.2.3](https://img.shields.io/badge/Version-0.2.3-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v1.6.0](https://img.shields.io/badge/AppVersion-v1.6.0-informational?style=flat-square) ![Version: 0.3.0](https://img.shields.io/badge/Version-0.3.0-informational?style=flat-square) ![Type: application](https://img.shields.io/badge/Type-application-informational?style=flat-square) ![AppVersion: v1.6.0](https://img.shields.io/badge/AppVersion-v1.6.0-informational?style=flat-square)
Talos Cloud Controller Manager Helm Chart Talos Cloud Controller Manager Helm Chart
@@ -79,5 +79,6 @@ helm upgrade -i --namespace=kube-system -f talos-ccm.yaml \
| serviceAccount.create | bool | `true` | Specifies whether a service account should be created. | | serviceAccount.create | bool | `true` | Specifies whether a service account should be created. |
| serviceAccount.name | string | `""` | The name of the service account to use. If not set and create is true, a name is generated using the fullname template. | | serviceAccount.name | string | `""` | The name of the service account to use. If not set and create is true, a name is generated using the fullname template. |
| tolerations | list | `[{"effect":"NoSchedule","key":"node-role.kubernetes.io/control-plane","operator":"Exists"},{"effect":"NoSchedule","key":"node.cloudprovider.kubernetes.io/uninitialized","operator":"Exists"}]` | Tolerations for data pods assignment. ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ | | tolerations | list | `[{"effect":"NoSchedule","key":"node-role.kubernetes.io/control-plane","operator":"Exists"},{"effect":"NoSchedule","key":"node.cloudprovider.kubernetes.io/uninitialized","operator":"Exists"}]` | Tolerations for data pods assignment. ref: https://kubernetes.io/docs/concepts/configuration/taint-and-toleration/ |
| transformations | list | `[]` | List of node transformations. Available matchExpressions key values: https://github.com/siderolabs/talos/blob/main/pkg/machinery/resources/runtime/platform_metadata.go#L28 |
| updateStrategy | object | `{"rollingUpdate":{"maxUnavailable":1},"type":"RollingUpdate"}` | Deployment update stategy type. ref: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#updating-a-deployment | | updateStrategy | object | `{"rollingUpdate":{"maxUnavailable":1},"type":"RollingUpdate"}` | Deployment update stategy type. ref: https://kubernetes.io/docs/concepts/workloads/controllers/deployment/#updating-a-deployment |
| useDaemonSet | bool | `false` | Deploy CCM in Daemonset mode. CCM will use hostNetwork and current node to access kubernetes/talos API You can run it without CNI plugin. | | useDaemonSet | bool | `false` | Deploy CCM in Daemonset mode. CCM will use hostNetwork and current node to access kubernetes/talos API You can run it without CNI plugin. |

View File

@@ -11,3 +11,7 @@ data:
{{- if .Values.features.approveNodeCSR }} {{- if .Values.features.approveNodeCSR }}
approveNodeCSR: true approveNodeCSR: true
{{- end }} {{- end }}
{{- with .Values.transformations }}
transformations:
{{- toYaml . | nindent 6 }}
{{- end }}

View File

@@ -40,6 +40,23 @@ features:
# `approveNodeCSR` - check and approve node CSR. # `approveNodeCSR` - check and approve node CSR.
approveNodeCSR: true approveNodeCSR: true
# -- List of node transformations.
# Available matchExpressions key values: https://github.com/siderolabs/talos/blob/main/pkg/machinery/resources/runtime/platform_metadata.go#L28
transformations: []
# - name: control-plane region
# nodeSelector:
# - matchExpressions:
# - key: hostname
# operator: Regexp
# values:
# - ^control-.+$
# annotations:
# talos.dev/instance-id: "{{ .InstanceID }}"
# labels:
# talos.dev/instance-spot: "{{ .Spot }}"
# platformMetadata:
# Region: "us-west-2"
# -- Log verbosity level. See https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/logging.md # -- Log verbosity level. See https://github.com/kubernetes/community/blob/master/contributors/devel/sig-instrumentation/logging.md
# for description of individual verbosity levels. # for description of individual verbosity levels.
logVerbosityLevel: 2 logVerbosityLevel: 2

View File

@@ -5,7 +5,7 @@ kind: ServiceAccount
metadata: metadata:
name: talos-cloud-controller-manager name: talos-cloud-controller-manager
labels: labels:
helm.sh/chart: talos-cloud-controller-manager-0.2.3 helm.sh/chart: talos-cloud-controller-manager-0.3.0
app.kubernetes.io/name: talos-cloud-controller-manager app.kubernetes.io/name: talos-cloud-controller-manager
app.kubernetes.io/instance: talos-cloud-controller-manager app.kubernetes.io/instance: talos-cloud-controller-manager
app.kubernetes.io/version: "v1.6.0" app.kubernetes.io/version: "v1.6.0"
@@ -18,7 +18,7 @@ kind: ServiceAccount
metadata: metadata:
name: talos-cloud-controller-manager-talos-secrets name: talos-cloud-controller-manager-talos-secrets
labels: labels:
helm.sh/chart: talos-cloud-controller-manager-0.2.3 helm.sh/chart: talos-cloud-controller-manager-0.3.0
app.kubernetes.io/name: talos-cloud-controller-manager app.kubernetes.io/name: talos-cloud-controller-manager
app.kubernetes.io/instance: talos-cloud-controller-manager app.kubernetes.io/instance: talos-cloud-controller-manager
app.kubernetes.io/version: "v1.6.0" app.kubernetes.io/version: "v1.6.0"
@@ -34,7 +34,7 @@ kind: ConfigMap
metadata: metadata:
name: talos-cloud-controller-manager name: talos-cloud-controller-manager
labels: labels:
helm.sh/chart: talos-cloud-controller-manager-0.2.3 helm.sh/chart: talos-cloud-controller-manager-0.3.0
app.kubernetes.io/name: talos-cloud-controller-manager app.kubernetes.io/name: talos-cloud-controller-manager
app.kubernetes.io/instance: talos-cloud-controller-manager app.kubernetes.io/instance: talos-cloud-controller-manager
app.kubernetes.io/version: "v1.6.0" app.kubernetes.io/version: "v1.6.0"
@@ -51,7 +51,7 @@ kind: ClusterRole
metadata: metadata:
name: system:talos-cloud-controller-manager name: system:talos-cloud-controller-manager
labels: labels:
helm.sh/chart: talos-cloud-controller-manager-0.2.3 helm.sh/chart: talos-cloud-controller-manager-0.3.0
app.kubernetes.io/name: talos-cloud-controller-manager app.kubernetes.io/name: talos-cloud-controller-manager
app.kubernetes.io/instance: talos-cloud-controller-manager app.kubernetes.io/instance: talos-cloud-controller-manager
app.kubernetes.io/version: "v1.6.0" app.kubernetes.io/version: "v1.6.0"
@@ -159,7 +159,7 @@ kind: Service
metadata: metadata:
name: talos-cloud-controller-manager name: talos-cloud-controller-manager
labels: labels:
helm.sh/chart: talos-cloud-controller-manager-0.2.3 helm.sh/chart: talos-cloud-controller-manager-0.3.0
app.kubernetes.io/name: talos-cloud-controller-manager app.kubernetes.io/name: talos-cloud-controller-manager
app.kubernetes.io/instance: talos-cloud-controller-manager app.kubernetes.io/instance: talos-cloud-controller-manager
app.kubernetes.io/version: "v1.6.0" app.kubernetes.io/version: "v1.6.0"
@@ -183,7 +183,7 @@ kind: DaemonSet
metadata: metadata:
name: talos-cloud-controller-manager name: talos-cloud-controller-manager
labels: labels:
helm.sh/chart: talos-cloud-controller-manager-0.2.3 helm.sh/chart: talos-cloud-controller-manager-0.3.0
app.kubernetes.io/name: talos-cloud-controller-manager app.kubernetes.io/name: talos-cloud-controller-manager
app.kubernetes.io/instance: talos-cloud-controller-manager app.kubernetes.io/instance: talos-cloud-controller-manager
app.kubernetes.io/version: "v1.6.0" app.kubernetes.io/version: "v1.6.0"

View File

@@ -5,7 +5,7 @@ kind: ServiceAccount
metadata: metadata:
name: talos-cloud-controller-manager name: talos-cloud-controller-manager
labels: labels:
helm.sh/chart: talos-cloud-controller-manager-0.2.3 helm.sh/chart: talos-cloud-controller-manager-0.3.0
app.kubernetes.io/name: talos-cloud-controller-manager app.kubernetes.io/name: talos-cloud-controller-manager
app.kubernetes.io/instance: talos-cloud-controller-manager app.kubernetes.io/instance: talos-cloud-controller-manager
app.kubernetes.io/version: "v1.6.0" app.kubernetes.io/version: "v1.6.0"
@@ -18,7 +18,7 @@ kind: ServiceAccount
metadata: metadata:
name: talos-cloud-controller-manager-talos-secrets name: talos-cloud-controller-manager-talos-secrets
labels: labels:
helm.sh/chart: talos-cloud-controller-manager-0.2.3 helm.sh/chart: talos-cloud-controller-manager-0.3.0
app.kubernetes.io/name: talos-cloud-controller-manager app.kubernetes.io/name: talos-cloud-controller-manager
app.kubernetes.io/instance: talos-cloud-controller-manager app.kubernetes.io/instance: talos-cloud-controller-manager
app.kubernetes.io/version: "v1.6.0" app.kubernetes.io/version: "v1.6.0"
@@ -34,7 +34,7 @@ kind: ConfigMap
metadata: metadata:
name: talos-cloud-controller-manager name: talos-cloud-controller-manager
labels: labels:
helm.sh/chart: talos-cloud-controller-manager-0.2.3 helm.sh/chart: talos-cloud-controller-manager-0.3.0
app.kubernetes.io/name: talos-cloud-controller-manager app.kubernetes.io/name: talos-cloud-controller-manager
app.kubernetes.io/instance: talos-cloud-controller-manager app.kubernetes.io/instance: talos-cloud-controller-manager
app.kubernetes.io/version: "v1.6.0" app.kubernetes.io/version: "v1.6.0"
@@ -51,7 +51,7 @@ kind: ClusterRole
metadata: metadata:
name: system:talos-cloud-controller-manager name: system:talos-cloud-controller-manager
labels: labels:
helm.sh/chart: talos-cloud-controller-manager-0.2.3 helm.sh/chart: talos-cloud-controller-manager-0.3.0
app.kubernetes.io/name: talos-cloud-controller-manager app.kubernetes.io/name: talos-cloud-controller-manager
app.kubernetes.io/instance: talos-cloud-controller-manager app.kubernetes.io/instance: talos-cloud-controller-manager
app.kubernetes.io/version: "v1.6.0" app.kubernetes.io/version: "v1.6.0"
@@ -159,7 +159,7 @@ kind: Service
metadata: metadata:
name: talos-cloud-controller-manager name: talos-cloud-controller-manager
labels: labels:
helm.sh/chart: talos-cloud-controller-manager-0.2.3 helm.sh/chart: talos-cloud-controller-manager-0.3.0
app.kubernetes.io/name: talos-cloud-controller-manager app.kubernetes.io/name: talos-cloud-controller-manager
app.kubernetes.io/instance: talos-cloud-controller-manager app.kubernetes.io/instance: talos-cloud-controller-manager
app.kubernetes.io/version: "v1.6.0" app.kubernetes.io/version: "v1.6.0"
@@ -183,7 +183,7 @@ kind: Deployment
metadata: metadata:
name: talos-cloud-controller-manager name: talos-cloud-controller-manager
labels: labels:
helm.sh/chart: talos-cloud-controller-manager-0.2.3 helm.sh/chart: talos-cloud-controller-manager-0.3.0
app.kubernetes.io/name: talos-cloud-controller-manager app.kubernetes.io/name: talos-cloud-controller-manager
app.kubernetes.io/instance: talos-cloud-controller-manager app.kubernetes.io/instance: talos-cloud-controller-manager
app.kubernetes.io/version: "v1.6.0" app.kubernetes.io/version: "v1.6.0"

View File

@@ -5,7 +5,7 @@ kind: ServiceAccount
metadata: metadata:
name: talos-cloud-controller-manager name: talos-cloud-controller-manager
labels: labels:
helm.sh/chart: talos-cloud-controller-manager-0.2.3 helm.sh/chart: talos-cloud-controller-manager-0.3.0
app.kubernetes.io/name: talos-cloud-controller-manager app.kubernetes.io/name: talos-cloud-controller-manager
app.kubernetes.io/instance: talos-cloud-controller-manager app.kubernetes.io/instance: talos-cloud-controller-manager
app.kubernetes.io/version: "v1.6.0" app.kubernetes.io/version: "v1.6.0"
@@ -18,7 +18,7 @@ kind: ServiceAccount
metadata: metadata:
name: talos-cloud-controller-manager-talos-secrets name: talos-cloud-controller-manager-talos-secrets
labels: labels:
helm.sh/chart: talos-cloud-controller-manager-0.2.3 helm.sh/chart: talos-cloud-controller-manager-0.3.0
app.kubernetes.io/name: talos-cloud-controller-manager app.kubernetes.io/name: talos-cloud-controller-manager
app.kubernetes.io/instance: talos-cloud-controller-manager app.kubernetes.io/instance: talos-cloud-controller-manager
app.kubernetes.io/version: "v1.6.0" app.kubernetes.io/version: "v1.6.0"
@@ -34,7 +34,7 @@ kind: ConfigMap
metadata: metadata:
name: talos-cloud-controller-manager name: talos-cloud-controller-manager
labels: labels:
helm.sh/chart: talos-cloud-controller-manager-0.2.3 helm.sh/chart: talos-cloud-controller-manager-0.3.0
app.kubernetes.io/name: talos-cloud-controller-manager app.kubernetes.io/name: talos-cloud-controller-manager
app.kubernetes.io/instance: talos-cloud-controller-manager app.kubernetes.io/instance: talos-cloud-controller-manager
app.kubernetes.io/version: "v1.6.0" app.kubernetes.io/version: "v1.6.0"
@@ -51,7 +51,7 @@ kind: ClusterRole
metadata: metadata:
name: system:talos-cloud-controller-manager name: system:talos-cloud-controller-manager
labels: labels:
helm.sh/chart: talos-cloud-controller-manager-0.2.3 helm.sh/chart: talos-cloud-controller-manager-0.3.0
app.kubernetes.io/name: talos-cloud-controller-manager app.kubernetes.io/name: talos-cloud-controller-manager
app.kubernetes.io/instance: talos-cloud-controller-manager app.kubernetes.io/instance: talos-cloud-controller-manager
app.kubernetes.io/version: "v1.6.0" app.kubernetes.io/version: "v1.6.0"
@@ -159,7 +159,7 @@ kind: Service
metadata: metadata:
name: talos-cloud-controller-manager name: talos-cloud-controller-manager
labels: labels:
helm.sh/chart: talos-cloud-controller-manager-0.2.3 helm.sh/chart: talos-cloud-controller-manager-0.3.0
app.kubernetes.io/name: talos-cloud-controller-manager app.kubernetes.io/name: talos-cloud-controller-manager
app.kubernetes.io/instance: talos-cloud-controller-manager app.kubernetes.io/instance: talos-cloud-controller-manager
app.kubernetes.io/version: "v1.6.0" app.kubernetes.io/version: "v1.6.0"
@@ -183,7 +183,7 @@ kind: Deployment
metadata: metadata:
name: talos-cloud-controller-manager name: talos-cloud-controller-manager
labels: labels:
helm.sh/chart: talos-cloud-controller-manager-0.2.3 helm.sh/chart: talos-cloud-controller-manager-0.3.0
app.kubernetes.io/name: talos-cloud-controller-manager app.kubernetes.io/name: talos-cloud-controller-manager
app.kubernetes.io/instance: talos-cloud-controller-manager app.kubernetes.io/instance: talos-cloud-controller-manager
app.kubernetes.io/version: "v1.6.0" app.kubernetes.io/version: "v1.6.0"

85
docs/rules.yaml Normal file
View File

@@ -0,0 +1,85 @@
# transformation rules for the node
transformations:
- name: oracle
# Match rules similar as kubernetes matchExpressions
nodeSelector:
- name: oracle vm in frankfurt
matchExpressions:
- key: platform
operator: In
values:
- oracle
- key: region
operator: In
values:
- eu-frankfurt-1
# or
- name: special vm name
matchExpressions:
- key: hostname
operator: Regexp
values:
- ^web-oci-ad2-.+$
# We will annotate and label the nodes
annotations:
oci.oraclecloud.com/instance-id: "{{ .InstanceID }}"
oci.oraclecloud.com/compartment-id: ocid1.compartment.oc1..aaaaaaaabbbbbbccccccdddddd
labels:
talos.dev/zone": "frankfurt"
node-role.kubernetes.io/web: ""
# Transforme the platformMetadata
platformMetadata:
zone: EU-FRANKFURT-1-AD-2
- name: openstack
nodeSelector:
- name: fix openstack zone
matchExpressions:
- key: platform
operator: In
values:
- openstack
- key: zone
operator: In
values:
- nova
- key: hostname
operator: Regexp
values:
- ^web-fra-.+$
annotations: {}
labels:
talos.dev/zone: "frankfurt"
node-role.kubernetes.io/web: ""
# Set providerId/region/zone based on the hostname and platform
platformMetadata:
zone: frankfurt
region: eu-frankfurt-1
providerId: openstack://fra/{{ .InstanceID }}
# Special features for the node
features:
publicIPPredict: true
---
# Metadata for the node
# Oracle
platform: oracle
hostname: web-oci-ad2-1
region: eu-frankfurt-1
zone: EU-FRANKFURT-1-AD-2
instanceType: VM.Standard.A1.Flex
instanceId: ocid1.instance.oc1.eu-frankfurt-1.aaaaaaaabbbbbbccccccdddddd
providerId: oci://ocid1.instance.oc1.eu-frankfurt-1.aaaaaaaabbbbbbccccccdddddd
# Openstack
platform: openstack
hostname: web-fra-de11
zone: nova
instanceType: b2-7
instanceId: 9a1fd9ee-9f05-473e-88d8-1b1b1b1b1b1b
providerId: openstack:///9a1fd9ee-9f05-473e-88d8-1b1b1b1b1b1b

52
go.mod
View File

@@ -3,24 +3,24 @@ module github.com/siderolabs/talos-cloud-controller-manager
go 1.22.2 go 1.22.2
require ( require (
github.com/cosi-project/runtime v0.4.1 github.com/cosi-project/runtime v0.4.2
github.com/siderolabs/net v0.4.0 github.com/siderolabs/net v0.4.0
github.com/siderolabs/talos/pkg/machinery v1.6.7 github.com/siderolabs/talos/pkg/machinery v1.7.1
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.9.0 github.com/stretchr/testify v1.9.0
gopkg.in/yaml.v3 v3.0.1 gopkg.in/yaml.v3 v3.0.1
gotest.tools/v3 v3.5.1 gotest.tools/v3 v3.5.1
k8s.io/api v0.29.3 k8s.io/api v0.30.0
k8s.io/apimachinery v0.29.3 k8s.io/apimachinery v0.30.0
k8s.io/client-go v0.29.3 k8s.io/client-go v0.30.0
k8s.io/cloud-provider v0.29.3 k8s.io/cloud-provider v0.30.0
k8s.io/component-base v0.29.3 k8s.io/component-base v0.30.0
k8s.io/klog/v2 v2.120.1 k8s.io/klog/v2 v2.120.1
k8s.io/utils v0.0.0-20240310230437-4693a0247e57 k8s.io/utils v0.0.0-20240423183400-0849a56e8f22
) )
require ( require (
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/Masterminds/semver/v3 v3.2.1 // indirect github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/NYTimes/gziphandler v1.1.1 // indirect github.com/NYTimes/gziphandler v1.1.1 // indirect
github.com/ProtonMail/go-crypto v1.0.0 // indirect github.com/ProtonMail/go-crypto v1.0.0 // indirect
@@ -41,7 +41,7 @@ require (
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/evanphx/json-patch v5.7.0+incompatible // indirect github.com/evanphx/json-patch v5.9.0+incompatible // indirect
github.com/felixge/httpsnoop v1.0.3 // indirect github.com/felixge/httpsnoop v1.0.3 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gertd/go-pluralize v0.2.1 // indirect github.com/gertd/go-pluralize v0.2.1 // indirect
@@ -53,7 +53,7 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/protobuf v1.5.4 // indirect github.com/golang/protobuf v1.5.4 // indirect
github.com/google/cel-go v0.17.7 // indirect github.com/google/cel-go v0.17.8 // indirect
github.com/google/gnostic-models v0.6.8 // indirect github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect github.com/google/gofuzz v1.2.0 // indirect
@@ -66,21 +66,22 @@ require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect github.com/josharian/intern v1.0.0 // indirect
github.com/josharian/native v1.1.0 // indirect github.com/josharian/native v1.1.0 // indirect
github.com/jsimonetti/rtnetlink v1.4.0 // indirect github.com/jsimonetti/rtnetlink v1.4.1 // indirect
github.com/json-iterator/go v1.1.12 // indirect github.com/json-iterator/go v1.1.12 // indirect
github.com/mailru/easyjson v0.7.7 // indirect github.com/mailru/easyjson v0.7.7 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mdlayher/ethtool v0.1.0 // indirect github.com/mdlayher/ethtool v0.1.0 // indirect
github.com/mdlayher/genetlink v1.3.2 // indirect github.com/mdlayher/genetlink v1.3.2 // indirect
github.com/mdlayher/netlink v1.7.2 // indirect github.com/mdlayher/netlink v1.7.2 // indirect
github.com/mdlayher/socket v0.4.1 // indirect github.com/mdlayher/socket v0.5.1 // indirect
github.com/moby/term v0.5.0 // indirect github.com/moby/term v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/opencontainers/runtime-spec v1.1.0 // indirect github.com/opencontainers/runtime-spec v1.2.0 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/planetscale/vtprotobuf v0.6.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.16.0 // indirect github.com/prometheus/client_golang v1.16.0 // indirect
github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/client_model v0.4.0 // indirect
@@ -110,29 +111,28 @@ require (
go.uber.org/multierr v1.11.0 // indirect go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect go.uber.org/zap v1.27.0 // indirect
golang.org/x/crypto v0.22.0 // indirect golang.org/x/crypto v0.22.0 // indirect
golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 // indirect golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect
golang.org/x/net v0.24.0 // indirect golang.org/x/net v0.24.0 // indirect
golang.org/x/oauth2 v0.16.0 // indirect golang.org/x/oauth2 v0.19.0 // indirect
golang.org/x/sync v0.7.0 // indirect golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.19.0 // indirect golang.org/x/sys v0.19.0 // indirect
golang.org/x/term v0.19.0 // indirect golang.org/x/term v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect golang.org/x/time v0.5.0 // indirect
google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240401170217-c3f982113cda // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240304212257-790db918fca8 // indirect
google.golang.org/grpc v1.62.1 // indirect google.golang.org/grpc v1.62.1 // indirect
google.golang.org/protobuf v1.33.0 // indirect google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect
k8s.io/apiserver v0.29.3 // indirect k8s.io/apiserver v0.30.0 // indirect
k8s.io/component-helpers v0.29.3 // indirect k8s.io/component-helpers v0.30.0 // indirect
k8s.io/controller-manager v0.29.3 // indirect k8s.io/controller-manager v0.30.0 // indirect
k8s.io/kms v0.29.3 // indirect k8s.io/kms v0.30.0 // indirect
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect k8s.io/kube-openapi v0.0.0-20240411171206-dc4e619f62f3 // indirect
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.28.0 // indirect sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.29.0 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect sigs.k8s.io/yaml v1.4.0 // indirect
) )

122
go.sum
View File

@@ -2,8 +2,8 @@ cloud.google.com/go/compute v1.23.3 h1:6sVlXXBmbd7jNX0Ipq0trII3e4n1/MsADLK6a+aiV
cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI= cloud.google.com/go/compute v1.23.3/go.mod h1:VCgBUoMnIVIR0CscqQiPJLAG25E3ZRZMzcFZeQ+h8CI=
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=
cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I=
@@ -47,8 +47,8 @@ github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cosi-project/runtime v0.4.1 h1:9lJWw5cl3Lz1qP32bl2vxAsJs6LM8KdUGLCc9t/EGqw= github.com/cosi-project/runtime v0.4.2 h1:kJkhorzDWierDDbXn1BDHS6iQ7ai9AdvQOnK5uG/g8g=
github.com/cosi-project/runtime v0.4.1/go.mod h1:eXVAHf9QzzSVblLUtHHPFOZ7JBuz+GypHbao1vw+SdQ= github.com/cosi-project/runtime v0.4.2/go.mod h1:eXVAHf9QzzSVblLUtHHPFOZ7JBuz+GypHbao1vw+SdQ=
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
@@ -62,8 +62,8 @@ github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxER
github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= github.com/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A=
github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew=
github.com/evanphx/json-patch v5.7.0+incompatible h1:vgGkfT/9f8zE6tvSCe74nfpAVDQ2tG6yudJd8LBksgI= github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls=
github.com/evanphx/json-patch v5.7.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk=
github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
@@ -77,8 +77,8 @@ github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 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-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ=
github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg=
github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE=
github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs=
github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE=
@@ -95,25 +95,22 @@ github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= 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/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/cel-go v0.17.7 h1:6ebJFzu1xO2n7TLtN+UBqShGBhlD85bhvglh5DpcfqQ= github.com/google/cel-go v0.17.8 h1:j9m730pMZt1Fc4oKhCLUHfjj6527LuhYcYw0Rl8gqto=
github.com/google/cel-go v0.17.7/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY= github.com/google/cel-go v0.17.8/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY=
github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= 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/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.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 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 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 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/pprof v0.0.0-20240402174815-29b9bb013b0f h1:f00RU+zOX+B3rLAmMMkzHUF2h1z4DeYR9tTCvEq2REY=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20240402174815-29b9bb013b0f/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 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/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
@@ -141,8 +138,8 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA= github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w= github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
github.com/jsimonetti/rtnetlink v1.4.0 h1:Z1BF0fRgcETPEa0Kt0MRk3yV5+kF1FWTni6KUFKrq2I= github.com/jsimonetti/rtnetlink v1.4.1 h1:JfD4jthWBqZMEffc5RjgmlzpYttAVw1sdnmiNaPO3hE=
github.com/jsimonetti/rtnetlink v1.4.0/go.mod h1:5W1jDvWdnthFJ7fxYX1GMK07BUpI4oskfOqvPteYS6E= github.com/jsimonetti/rtnetlink v1.4.1/go.mod h1:xJjT7t59UIZ62GLZbv6PLLo8VFrostJMPBAheR6OM8w=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 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/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
@@ -164,8 +161,8 @@ github.com/mdlayher/genetlink v1.3.2 h1:KdrNKe+CTu+IbZnm/GVUMXSqBBLqcGpRDa0xkQy5
github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o= github.com/mdlayher/genetlink v1.3.2/go.mod h1:tcC3pkCrPUGIKKsCsp0B3AdaaKuHtaxoJRz3cc+528o=
github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g= github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw= github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U= github.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=
github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA= github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -179,12 +176,14 @@ github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8
github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs=
github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk= github.com/onsi/gomega v1.32.0 h1:JRYU78fJ1LPxlckP6Txi/EYqJvjtMrDC04/MM5XRHPk=
github.com/onsi/gomega v1.32.0/go.mod h1:a4x4gW6Pz2yK1MAmvluYme5lvYTn61afQ2ETw/8n4Lg= github.com/onsi/gomega v1.32.0/go.mod h1:a4x4gW6Pz2yK1MAmvluYme5lvYTn61afQ2ETw/8n4Lg=
github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bll4AjJ9odEGpg= github.com/opencontainers/runtime-spec v1.2.0 h1:z97+pHb3uELt/yiAWD691HNHQIF07bE7dzrbT927iTk=
github.com/opencontainers/runtime-spec v1.1.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/planetscale/vtprotobuf v0.6.0 h1:nBeETjudeJ5ZgBHUz1fVHvbqUKnYOXNhsIEabROxmNA=
github.com/planetscale/vtprotobuf v0.6.0/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8=
@@ -216,8 +215,8 @@ github.com/siderolabs/net v0.4.0 h1:1bOgVay/ijPkJz4qct98nHsiB/ysLQU0KLoBC4qLm7I=
github.com/siderolabs/net v0.4.0/go.mod h1:/ibG+Hm9HU27agp5r9Q3eZicEfjquzNzQNux5uEk0kM= github.com/siderolabs/net v0.4.0/go.mod h1:/ibG+Hm9HU27agp5r9Q3eZicEfjquzNzQNux5uEk0kM=
github.com/siderolabs/protoenc v0.2.1 h1:BqxEmeWQeMpNP3R6WrPqDatX8sM/r4t97OP8mFmg6GA= github.com/siderolabs/protoenc v0.2.1 h1:BqxEmeWQeMpNP3R6WrPqDatX8sM/r4t97OP8mFmg6GA=
github.com/siderolabs/protoenc v0.2.1/go.mod h1:StTHxjet1g11GpNAWiATgc8K0HMKiFSEVVFOa/H0otc= github.com/siderolabs/protoenc v0.2.1/go.mod h1:StTHxjet1g11GpNAWiATgc8K0HMKiFSEVVFOa/H0otc=
github.com/siderolabs/talos/pkg/machinery v1.6.7 h1:L/IV0+w+0n6Jsxa1LEFOxPaNNluGrZqn3YRvAXk/xzA= github.com/siderolabs/talos/pkg/machinery v1.7.1 h1:sVFQ0lNE6+kOomSZA8iuktzG1A4zSW9KTsB2TLaTPsU=
github.com/siderolabs/talos/pkg/machinery v1.6.7/go.mod h1:X1rb+ZA0tRjclzuH4Kurune4DK6kr58B/vorQ88KwpE= github.com/siderolabs/talos/pkg/machinery v1.7.1/go.mod h1:YBl9KDCD45Uc7N0rXBY1JqovUn1n46ekUPSNbEVZzQU=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js=
@@ -297,8 +296,8 @@ golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2Uz
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2 h1:Jvc7gsqn21cJHCmAWx0LiimpP18LZmUxkT5Mp7EZ1mI= golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY=
golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 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.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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
@@ -314,8 +313,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ= golang.org/x/oauth2 v0.19.0 h1:9+E/EZBCbTLNrbN35fHv/a/d/mOBatymz1zbtQrXpIg=
golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o= golang.org/x/oauth2 v0.19.0/go.mod h1:vYi7skDa1x015PmRRYZ7+s1cWyPgrPiSYRe4rnsexc8=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-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-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -350,7 +349,6 @@ golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
@@ -364,24 +362,20 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.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.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY=
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/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-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-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ= google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80 h1:KAeGQVN3M9nD0/bQXnr/ClcEMJ968gUXJQ9pwfSynuQ=
google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro=
google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8 h1:8eadJkXbwDEMNwcB5O0s5Y5eCfyuCLdvaiOIaGTrWmQ= google.golang.org/genproto/googleapis/api v0.0.0-20240401170217-c3f982113cda h1:b6F6WIV4xHHD0FA4oIyzU6mHWg2WI2X1RBehwa5QN38=
google.golang.org/genproto/googleapis/api v0.0.0-20240304212257-790db918fca8/go.mod h1:O1cOfN1Cy6QEYr7VxtjOyP5AdAuR0aJ/MYZaaof623Y= google.golang.org/genproto/googleapis/api v0.0.0-20240401170217-c3f982113cda/go.mod h1:AHcE/gZH76Bk/ROZhQphlRoWo5xKDEtz3eVEO1LfA8c=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240304212257-790db918fca8 h1:IR+hp6ypxjH24bkMfEJ0yHR21+gwPWdV+/IBrPQyn3k= google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda h1:LI5DOvAxUPMv/50agcLLoo+AdWc1irS9Rzz4vPuD1V4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240304212257-790db918fca8/go.mod h1:UCOku4NytXMJuLQE5VuqA5lX3PcHCBo8pxNyvkf4xBs= google.golang.org/genproto/googleapis/rpc v0.0.0-20240401170217-c3f982113cda/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk= google.golang.org/grpc v1.62.1 h1:B4n+nfKzOICUXMgyrNd19h/I9oH0L1pizfk1d4zSgTk=
google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE= google.golang.org/grpc v1.62.1/go.mod h1:IWTG0VlJLCh1SkC58F7np9ka9mx/WNkjl4PGJaiq+QE=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -399,35 +393,35 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU= gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
k8s.io/api v0.29.3 h1:2ORfZ7+bGC3YJqGpV0KSDDEVf8hdGQ6A03/50vj8pmw= k8s.io/api v0.30.0 h1:siWhRq7cNjy2iHssOB9SCGNCl2spiF1dO3dABqZ8niA=
k8s.io/api v0.29.3/go.mod h1:y2yg2NTyHUUkIoTC+phinTnEa3KFM6RZ3szxt014a80= k8s.io/api v0.30.0/go.mod h1:OPlaYhoHs8EQ1ql0R/TsUgaRPhpKNxIMrKQfWUp8QSE=
k8s.io/apimachinery v0.29.3 h1:2tbx+5L7RNvqJjn7RIuIKu9XTsIZ9Z5wX2G22XAa5EU= k8s.io/apimachinery v0.30.0 h1:qxVPsyDM5XS96NIh9Oj6LavoVFYff/Pon9cZeDIkHHA=
k8s.io/apimachinery v0.29.3/go.mod h1:hx/S4V2PNW4OMg3WizRrHutyB5la0iCUbZym+W0EQIU= k8s.io/apimachinery v0.30.0/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
k8s.io/apiserver v0.29.3 h1:xR7ELlJ/BZSr2n4CnD3lfA4gzFivh0wwfNfz9L0WZcE= k8s.io/apiserver v0.30.0 h1:QCec+U72tMQ+9tR6A0sMBB5Vh6ImCEkoKkTDRABWq6M=
k8s.io/apiserver v0.29.3/go.mod h1:hrvXlwfRulbMbBgmWRQlFru2b/JySDpmzvQwwk4GUOs= k8s.io/apiserver v0.30.0/go.mod h1:smOIBq8t0MbKZi7O7SyIpjPsiKJ8qa+llcFCluKyqiY=
k8s.io/client-go v0.29.3 h1:R/zaZbEAxqComZ9FHeQwOh3Y1ZUs7FaHKZdQtIc2WZg= k8s.io/client-go v0.30.0 h1:sB1AGGlhY/o7KCyCEQ0bPWzYDL0pwOZO4vAtTSh/gJQ=
k8s.io/client-go v0.29.3/go.mod h1:tkDisCvgPfiRpxGnOORfkljmS+UrW+WtXAy2fTvXJB0= k8s.io/client-go v0.30.0/go.mod h1:g7li5O5256qe6TYdAMyX/otJqMhIiGgTapdLchhmOaY=
k8s.io/cloud-provider v0.29.3 h1:y39hNq0lrPD1qmqQ2ykwMJGeWF9LsepVkR2a4wskwLc= k8s.io/cloud-provider v0.30.0 h1:hz1MXkFjsyO167sRZVchXEi2YYMQ6kolBi79nuICjzw=
k8s.io/cloud-provider v0.29.3/go.mod h1:daDV1WkAO6pTrdsn7v8TpN/q9n75ExUC4RJDl7vlPKk= k8s.io/cloud-provider v0.30.0/go.mod h1:iyVcGvDfmZ7m5cliI9TTHj0VTjYDNpc/K71Gp6hukjU=
k8s.io/component-base v0.29.3 h1:Oq9/nddUxlnrCuuR2K/jp6aflVvc0uDvxMzAWxnGzAo= k8s.io/component-base v0.30.0 h1:cj6bp38g0ainlfYtaOQuRELh5KSYjhKxM+io7AUIk4o=
k8s.io/component-base v0.29.3/go.mod h1:Yuj33XXjuOk2BAaHsIGHhCKZQAgYKhqIxIjIr2UXYio= k8s.io/component-base v0.30.0/go.mod h1:V9x/0ePFNaKeKYA3bOvIbrNoluTSG+fSJKjLdjOoeXQ=
k8s.io/component-helpers v0.29.3 h1:1dqZswuZgT2ZMixYeORyCUOAApXxgsvjVSgfoUT+P4o= k8s.io/component-helpers v0.30.0 h1:xbJtNCfSM4SB/Tz5JqCKDZv4eT5LVi/AWQ1VOxhmStU=
k8s.io/component-helpers v0.29.3/go.mod h1:yiDqbRQrnQY+sPju/bL7EkwDJb6LVOots53uZNMZBos= k8s.io/component-helpers v0.30.0/go.mod h1:68HlSwXIumMKmCx8cZe1PoafQEYh581/sEpxMrkhmX4=
k8s.io/controller-manager v0.29.3 h1:pvm3mirypgW7kM6dHRk6O5ANZj4bZTWirfk5gO6RlCo= k8s.io/controller-manager v0.30.0 h1:jqqT8cK0Awdy0IfT0yuqYIRmwskbdzH5AEZqkuhEVMs=
k8s.io/controller-manager v0.29.3/go.mod h1:RNxpf0d1WAo59sOLd32isWJP0oZ7Zxr+q4VEEaSq4gk= k8s.io/controller-manager v0.30.0/go.mod h1:suM1r/pxUuk2ij5Bbm7W9kBLrFujXuzIboNuWK5AfRA=
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw= k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kms v0.29.3 h1:ReljsAUhYlm2spdT4yXmY+9a8x8dc/OT4mXvwQPPteQ= k8s.io/kms v0.30.0 h1:ZlnD/ei5lpvUlPw6eLfVvH7d8i9qZ6HwUQgydNVks8g=
k8s.io/kms v0.29.3/go.mod h1:TBGbJKpRUMk59neTMDMddjIDL+D4HuFUbpuiuzmOPg0= k8s.io/kms v0.30.0/go.mod h1:GrMurD0qk3G4yNgGcsCEmepqf9KyyIrTXYR2lyUOJC4=
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= k8s.io/kube-openapi v0.0.0-20240411171206-dc4e619f62f3 h1:SbdLaI6mM6ffDSJCadEaD4IkuPzepLDGlkd2xV0t1uA=
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= k8s.io/kube-openapi v0.0.0-20240411171206-dc4e619f62f3/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
k8s.io/utils v0.0.0-20240310230437-4693a0247e57 h1:gbqbevonBh57eILzModw6mrkbwM0gQBEuevE/AaBsHY= k8s.io/utils v0.0.0-20240423183400-0849a56e8f22 h1:ao5hUqGhsqdm+bYbjH/pRkCs0unBGe9UyDahzs9zQzQ=
k8s.io/utils v0.0.0-20240310230437-4693a0247e57/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= k8s.io/utils v0.0.0-20240423183400-0849a56e8f22/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.28.0 h1:TgtAeesdhpm2SGwkQasmbeqDo8th5wOBA5h/AjTKA4I= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.29.0 h1:/U5vjBbQn3RChhv7P11uhYvCSm5G2GaIi5AIGBS6r4c=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.28.0/go.mod h1:VHVDI/KrK4fjnV61bE2g3sA7tiETLn8sooImelsCx3Y= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.29.0/go.mod h1:z7+wmGM2dfIiLRfrC6jb5kV2Mq/sK1ZP303cxzkV5Y4=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=

View File

@@ -4,3 +4,14 @@ global:
# endpoints: # endpoints:
# - 1.2.3.4 # - 1.2.3.4
# - 4.3.2.1 # - 4.3.2.1
transformations:
- nodeSelector:
- matchExpressions:
- key: hostname
operator: Regexp
values:
- ^web-.+$
annotations:
talos.dev/instance-id: "id-{{ .InstanceID }}"
labels:
node-role.kubernetes.io/web: ""

View File

@@ -0,0 +1,123 @@
// Package nodeselector provides a mechanism to match node based on node selector rules.
package nodeselector
import (
"fmt"
"regexp"
"slices"
"strconv"
"strings"
)
// Match returns true if the node metadata matches the node selector rules.
func Match(rules []NodeSelectorTerm, fields map[string]string) (bool, error) {
if len(rules) == 0 {
return true, nil
}
for _, rule := range rules {
match, err := MatchExpressions(rule.MatchExpressions, fields)
if err != nil {
return false, err
}
if match {
return true, nil
}
}
return false, nil
}
// MatchExpressions returns true if the node metadata matches the node selector expressions.
//
//nolint:cyclop,gocyclo
func MatchExpressions(rules []NodeSelectorRequirement, fields map[string]string) (bool, error) {
if len(rules) == 0 {
return false, nil
}
matchs := make([]bool, len(rules))
for idx, rule := range rules {
switch rule.Operator {
case NodeSelectorOpIn:
if len(rule.Values) == 0 {
return false, fmt.Errorf("values must be non-empty for operator '%s'", rule.Operator)
}
if value, ok := fields[strings.ToLower(rule.Key)]; ok {
matchs[idx] = slices.Contains(rule.Values, value)
}
case NodeSelectorOpNotIn:
if len(rule.Values) == 0 {
return false, fmt.Errorf("values must be non-empty for operator '%s'", rule.Operator)
}
if value, ok := fields[strings.ToLower(rule.Key)]; ok {
matchs[idx] = !slices.Contains(rule.Values, value)
}
case NodeSelectorOpExists:
if len(rule.Values) > 0 {
return false, fmt.Errorf("values must be empty for operator %s", rule.Operator)
}
if _, ok := fields[strings.ToLower(rule.Key)]; ok {
matchs[idx] = true
}
case NodeSelectorOpDoesNotExist:
if len(rule.Values) > 0 {
return false, fmt.Errorf("values must be empty for operator %s", rule.Operator)
}
if _, ok := fields[strings.ToLower(rule.Key)]; !ok {
matchs[idx] = true
}
case NodeSelectorOpGt, NodeSelectorOpLt:
if len(rule.Values) != 1 {
return false, fmt.Errorf("values must have a single element for operator %s", rule.Operator)
}
if value, ok := fields[strings.ToLower(rule.Key)]; ok {
lsValue, err := strconv.ParseInt(value, 10, 64)
if err != nil {
return false, fmt.Errorf("failed to parse value %s as int", value)
}
rValue, err := strconv.ParseInt(rule.Values[0], 10, 64)
if err != nil {
return false, fmt.Errorf("failed to parse value %s as int", rule.Values[0])
}
matchs[idx] = (rule.Operator == NodeSelectorOpGt && lsValue > rValue) || (rule.Operator == NodeSelectorOpLt && lsValue < rValue)
}
case NodeSelectorOpRegexp:
if len(rule.Values) != 1 {
return false, fmt.Errorf("values must have a single element for operator %s", rule.Operator)
}
if value, ok := fields[strings.ToLower(rule.Key)]; ok {
r := regexp.MustCompile(rule.Values[0])
matchs[idx] = r.MatchString(value)
}
default:
return false, fmt.Errorf("%s not a valid selector operator", rule.Operator)
}
}
// And operation between all matchs
for _, match := range matchs {
if !match {
return false, nil
}
}
return true, nil
}

View File

@@ -0,0 +1,186 @@
package nodeselector_test
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"github.com/siderolabs/talos-cloud-controller-manager/pkg/nodeselector"
)
func TestMatch(t *testing.T) {
fields := map[string]string{
"platform": "test-platform",
"hostname": "test-hostname",
"region": "region-a",
"int": "10",
}
for _, tt := range []struct {
name string
rules []nodeselector.NodeSelectorTerm
fields map[string]string
expected bool
expectedError error
}{
{
name: "NotMatch with empty rules",
rules: []nodeselector.NodeSelectorTerm{},
fields: fields,
expected: true,
},
{
name: "NotMatch with empty expression rules",
rules: []nodeselector.NodeSelectorTerm{
{
MatchExpressions: []nodeselector.NodeSelectorRequirement{},
},
},
fields: fields,
expected: false,
},
{
name: "NotMatch with nonexistent platform",
rules: []nodeselector.NodeSelectorTerm{
{
MatchExpressions: []nodeselector.NodeSelectorRequirement{
{
Key: "platform",
Operator: nodeselector.NodeSelectorOpIn,
Values: []string{"bad-platform"},
},
},
},
},
fields: fields,
expected: false,
},
} {
t.Run(tt.name, func(t *testing.T) {
match, err := nodeselector.Match(tt.rules, tt.fields)
if tt.expectedError != nil {
assert.NotNil(t, err)
assert.Equal(t, tt.expectedError.Error(), err.Error())
} else {
assert.NoError(t, err)
assert.Equal(t, tt.expected, match)
}
})
}
}
func TestMatchExpressions(t *testing.T) {
fields := map[string]string{
"platform": "test-platform",
"hostname": "test-hostname",
"region": "region-a",
"int": "10",
}
for _, tt := range []struct {
name string
rules []nodeselector.NodeSelectorRequirement
fields map[string]string
expected bool
expectedError error
}{
{
name: "MatchExpressions with In operator error",
rules: []nodeselector.NodeSelectorRequirement{
{
Key: "platform",
Operator: nodeselector.NodeSelectorOpIn,
Values: []string{},
},
},
fields: fields,
expected: false,
expectedError: fmt.Errorf("values must be non-empty for operator 'In'"),
},
{
name: "MatchExpressions with In and Exists operator",
rules: []nodeselector.NodeSelectorRequirement{
{
Key: "platform",
Operator: nodeselector.NodeSelectorOpIn,
Values: []string{"test-platform"},
},
{
Key: "region",
Operator: nodeselector.NodeSelectorOpExists,
},
{
Key: "fake-key",
Operator: nodeselector.NodeSelectorOpDoesNotExist,
},
},
fields: fields,
expected: true,
},
{
name: "MatchExpressions with GtLt operator",
rules: []nodeselector.NodeSelectorRequirement{
{
Key: "platform",
Operator: nodeselector.NodeSelectorOpIn,
Values: []string{"test-platform"},
},
{
Key: "int",
Operator: nodeselector.NodeSelectorOpGt,
Values: []string{"5"},
},
},
fields: fields,
expected: true,
},
{
name: "MatchExpressions with regexp operator",
rules: []nodeselector.NodeSelectorRequirement{
{
Key: "platform",
Operator: nodeselector.NodeSelectorOpIn,
Values: []string{"test-platform"},
},
{
Key: "hostname",
Operator: nodeselector.NodeSelectorOpRegexp,
Values: []string{"^test.+$"},
},
},
fields: fields,
expected: true,
},
{
name: "MatchExpressions with regexp operator did not match",
rules: []nodeselector.NodeSelectorRequirement{
{
Key: "platform",
Operator: nodeselector.NodeSelectorOpIn,
Values: []string{"test-platform"},
},
{
Key: "hostname",
Operator: nodeselector.NodeSelectorOpRegexp,
Values: []string{"^somename.+$"},
},
},
fields: fields,
expected: false,
},
} {
t.Run(tt.name, func(t *testing.T) {
match, err := nodeselector.MatchExpressions(tt.rules, tt.fields)
if tt.expectedError != nil {
assert.NotNil(t, err)
assert.Equal(t, tt.expectedError.Error(), err.Error())
} else {
assert.NoError(t, err)
assert.Equal(t, tt.expected, match)
}
})
}
}

66
pkg/nodeselector/types.go Normal file
View File

@@ -0,0 +1,66 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package nodeselector
// Source(04/2024): https://github.com/kubernetes/kubernetes/blob/master/pkg/apis/core/types.go with modifications (NodeSelectorOpRegexp)
// NodeSelectorTerm represents expressions and fields required to select nodes.
// A null or empty node selector term matches no objects. The requirements of
// them are ANDed.
// The TopologySelectorTerm type implements a subset of the NodeSelectorTerm.
//
//nolint:revive
type NodeSelectorTerm struct {
// A list of node selector requirements by node's labels.
MatchExpressions []NodeSelectorRequirement `yaml:"matchExpressions,omitempty"`
}
// NodeSelectorRequirement is a selector that contains values, a key, and an operator
// that relates the key and values.
//
//nolint:revive
type NodeSelectorRequirement struct {
// The label key that the selector applies to.
Key string `yaml:"key,omitempty"`
// Represents a key's relationship to a set of values.
// Valid operators are In, NotIn, Exists, DoesNotExist. Gt, and Lt.
Operator NodeSelectorOperator `yaml:"operator,omitempty"`
// An array of string values. If the operator is In or NotIn,
// the values array must be non-empty. If the operator is Exists or DoesNotExist,
// the values array must be empty. If the operator is Gt or Lt, the values
// array must have a single element, which will be interpreted as an integer.
// This array is replaced during a strategic merge patch.
// +optional
Values []string `yaml:"values,omitempty"`
}
// NodeSelectorOperator is the set of operators that can be used in
// a node selector requirement.
//
//nolint:revive
type NodeSelectorOperator string
// These are valid values of NodeSelectorOperator.
const (
NodeSelectorOpIn NodeSelectorOperator = "In"
NodeSelectorOpNotIn NodeSelectorOperator = "NotIn"
NodeSelectorOpExists NodeSelectorOperator = "Exists"
NodeSelectorOpDoesNotExist NodeSelectorOperator = "DoesNotExist"
NodeSelectorOpGt NodeSelectorOperator = "Gt"
NodeSelectorOpLt NodeSelectorOperator = "Lt"
NodeSelectorOpRegexp NodeSelectorOperator = "Regexp"
)

View File

@@ -7,12 +7,16 @@ import (
yaml "gopkg.in/yaml.v3" yaml "gopkg.in/yaml.v3"
"github.com/siderolabs/talos-cloud-controller-manager/pkg/transformer"
"k8s.io/klog/v2" "k8s.io/klog/v2"
) )
type cloudConfig struct { type cloudConfig struct {
// Global configuration. // Global configuration.
Global cloudConfigGlobal `yaml:"global,omitempty"` Global cloudConfigGlobal `yaml:"global,omitempty"`
// Node transformation configuration.
Transformations []transformer.NodeTerm `yaml:"transformations,omitempty"`
} }
type cloudConfigGlobal struct { type cloudConfigGlobal struct {

View File

@@ -29,8 +29,21 @@ func TestReadCloudConfig(t *testing.T) {
cfg, err := readCloudConfig(strings.NewReader(` cfg, err := readCloudConfig(strings.NewReader(`
global: global:
approveNodeCSR: true approveNodeCSR: true
preferIPv6: true preferIPv6: true
transformations:
- name: cluster
nodeSelector:
- name: cluter-1
matchExpressions:
- key: platform
operator: In
values:
- cluter
annotations:
cluster-platform: "{{ .Platform }}"
labels:
node-role.kubernetes.io/web: ""
`)) `))
if err != nil { if err != nil {
t.Fatalf("Should succeed when a valid config is provided: %s", err) t.Fatalf("Should succeed when a valid config is provided: %s", err)

View File

@@ -3,6 +3,7 @@ package talos
import ( import (
"context" "context"
"crypto/x509" "crypto/x509"
"encoding/json"
"fmt" "fmt"
"strings" "strings"
@@ -12,6 +13,8 @@ import (
v1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/strategicpatch"
clientkubernetes "k8s.io/client-go/kubernetes" clientkubernetes "k8s.io/client-go/kubernetes"
cloudproviderapi "k8s.io/cloud-provider/api" cloudproviderapi "k8s.io/cloud-provider/api"
cloudnodeutil "k8s.io/cloud-provider/node/helpers" cloudnodeutil "k8s.io/cloud-provider/node/helpers"
@@ -72,24 +75,79 @@ func getNodeAddresses(config *cloudConfig, platform string, nodeIPs []string, if
return addresses return addresses
} }
func syncNodeLabels(c *client, node *v1.Node, meta *runtime.PlatformMetadataSpec) error { func syncNodeAnnotations(ctx context.Context, c *client, node *v1.Node, nodeAnnotations map[string]string) error {
nodeLabels := node.ObjectMeta.Labels nodeAnnotationsOrig := node.ObjectMeta.Labels
annotationsToUpdate := map[string]string{}
for k, v := range nodeAnnotations {
if r, ok := nodeAnnotationsOrig[k]; !ok || r != v {
annotationsToUpdate[k] = v
}
}
if len(annotationsToUpdate) > 0 {
oldData, err := json.Marshal(node)
if err != nil {
return fmt.Errorf("failed to marshal the existing node %#v: %w", node, err)
}
newNode := node.DeepCopy()
if newNode.Annotations == nil {
newNode.Annotations = make(map[string]string)
}
for k, v := range annotationsToUpdate {
newNode.Annotations[k] = v
}
newData, err := json.Marshal(newNode)
if err != nil {
return fmt.Errorf("failed to marshal the new node %#v: %w", newNode, err)
}
patchBytes, err := strategicpatch.CreateTwoWayMergePatch(oldData, newData, &v1.Node{})
if err != nil {
return fmt.Errorf("failed to create a two-way merge patch: %v", err)
}
if _, err := c.kclient.CoreV1().Nodes().Patch(ctx, node.Name, types.StrategicMergePatchType, patchBytes, metav1.PatchOptions{}); err != nil {
return fmt.Errorf("failed to patch the node: %v", err)
}
}
return nil
}
func setTalosNodeLabels(c *client, meta *runtime.PlatformMetadataSpec) map[string]string {
if meta == nil {
return make(map[string]string)
}
labels := make(map[string]string, 3)
if meta.Platform != "" {
labels[ClusterNodePlatformLabel] = meta.Platform
}
if meta.Spot {
labels[ClusterNodeLifeCycleLabel] = "spot"
}
if clusterName := c.talos.GetClusterName(); clusterName != "" {
labels[ClusterNameNodeLabel] = clusterName
}
return labels
}
func syncNodeLabels(c *client, node *v1.Node, nodeLabels map[string]string) error {
nodeLabelsOrig := node.ObjectMeta.Labels
labelsToUpdate := map[string]string{} labelsToUpdate := map[string]string{}
if nodeLabels == nil { for k, v := range nodeLabels {
nodeLabels = map[string]string{} if r, ok := nodeLabelsOrig[k]; !ok || r != v {
} labelsToUpdate[k] = v
}
if meta.Platform != "" && nodeLabels[ClusterNodePlatformLabel] != meta.Platform {
labelsToUpdate[ClusterNodePlatformLabel] = meta.Platform
}
if meta.Spot && nodeLabels[ClusterNodeLifeCycleLabel] != "spot" {
labelsToUpdate[ClusterNodeLifeCycleLabel] = "spot"
}
if clusterName := c.talos.GetClusterName(); clusterName != "" && nodeLabels[ClusterNameNodeLabel] != clusterName {
labelsToUpdate[ClusterNameNodeLabel] = clusterName
} }
if len(labelsToUpdate) > 0 { if len(labelsToUpdate) > 0 {

View File

@@ -4,6 +4,7 @@ import (
"context" "context"
"crypto/x509" "crypto/x509"
"fmt" "fmt"
"maps"
"net" "net"
"net/netip" "net/netip"
"strings" "strings"
@@ -11,6 +12,8 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/siderolabs/talos-cloud-controller-manager/pkg/nodeselector"
"github.com/siderolabs/talos-cloud-controller-manager/pkg/transformer"
"github.com/siderolabs/talos/pkg/machinery/resources/network" "github.com/siderolabs/talos/pkg/machinery/resources/network"
"github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/runtime"
@@ -151,10 +154,30 @@ func TestGetNodeAddresses(t *testing.T) {
func TestSyncNodeLabels(t *testing.T) { func TestSyncNodeLabels(t *testing.T) {
t.Setenv("TALOSCONFIG", "../../hack/talosconfig") t.Setenv("TALOSCONFIG", "../../hack/talosconfig")
cfg := cloudConfig{Global: cloudConfigGlobal{ cfg := cloudConfig{
ClusterName: "test-cluster", Global: cloudConfigGlobal{
Endpoints: []string{"127.0.0.1"}, ClusterName: "test-cluster",
}} Endpoints: []string{"127.0.0.1"},
},
Transformations: []transformer.NodeTerm{
{
NodeSelector: []nodeselector.NodeSelectorTerm{
{
MatchExpressions: []nodeselector.NodeSelectorRequirement{
{
Key: "Hostname",
Operator: "Regexp",
Values: []string{"^web-.+$"},
},
},
},
},
Labels: map[string]string{
"node-role.kubernetes.io/web": "",
},
},
},
}
ctx := context.Background() ctx := context.Background()
nodes := &v1.NodeList{ nodes := &v1.NodeList{
Items: []v1.Node{ Items: []v1.Node{
@@ -167,6 +190,15 @@ func TestSyncNodeLabels(t *testing.T) {
Name: "node1", Name: "node1",
}, },
}, },
{
TypeMeta: metav1.TypeMeta{
Kind: "Node",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "web-1",
},
},
}, },
} }
@@ -258,9 +290,45 @@ func TestSyncNodeLabels(t *testing.T) {
}, },
}, },
}, },
{
name: "node with custom labels",
node: &v1.Node{
ObjectMeta: metav1.ObjectMeta{
Name: "web-1",
},
},
meta: &runtime.PlatformMetadataSpec{
Platform: "nocloud",
Hostname: "web-1",
},
expectedError: nil,
expectedNode: &v1.Node{
TypeMeta: metav1.TypeMeta{
Kind: "Node",
APIVersion: "v1",
},
ObjectMeta: metav1.ObjectMeta{
Name: "web-1",
Labels: map[string]string{
ClusterNameNodeLabel: "test-cluster",
ClusterNodePlatformLabel: "nocloud",
"node-role.kubernetes.io/web": "",
},
},
},
},
} { } {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
err := syncNodeLabels(client, tt.node, tt.meta) nodeSpec, err := transformer.TransformNode(client.config.Transformations, tt.meta)
assert.NoError(t, err)
labels := setTalosNodeLabels(client, tt.meta)
if nodeSpec != nil && nodeSpec.Labels != nil {
maps.Copy(labels, nodeSpec.Labels)
}
err = syncNodeLabels(client, tt.node, labels)
assert.Equal(t, tt.expectedError, err) assert.Equal(t, tt.expectedError, err)

View File

@@ -3,8 +3,10 @@ package talos
import ( import (
"context" "context"
"fmt" "fmt"
"maps"
"strings" "strings"
"github.com/siderolabs/talos-cloud-controller-manager/pkg/transformer"
"github.com/siderolabs/talos-cloud-controller-manager/pkg/utils/net" "github.com/siderolabs/talos-cloud-controller-manager/pkg/utils/net"
"github.com/siderolabs/talos-cloud-controller-manager/pkg/utils/platform" "github.com/siderolabs/talos-cloud-controller-manager/pkg/utils/platform"
"github.com/siderolabs/talos/pkg/machinery/resources/runtime" "github.com/siderolabs/talos/pkg/machinery/resources/runtime"
@@ -44,6 +46,8 @@ func (i *instances) InstanceShutdown(_ context.Context, node *v1.Node) (bool, er
// InstanceMetadata returns the instance's metadata. The values returned in InstanceMetadata are // InstanceMetadata returns the instance's metadata. The values returned in InstanceMetadata are
// translated into specific fields in the Node object on registration. // translated into specific fields in the Node object on registration.
// Use the node.name or node.spec.providerID field to find the node in the cloud provider. // Use the node.name or node.spec.providerID field to find the node in the cloud provider.
//
//nolint:gocyclo,cyclop
func (i *instances) InstanceMetadata(ctx context.Context, node *v1.Node) (*cloudprovider.InstanceMetadata, error) { func (i *instances) InstanceMetadata(ctx context.Context, node *v1.Node) (*cloudprovider.InstanceMetadata, error) {
klog.V(4).Info("instances.InstanceMetadata() called, node: ", node.Name) klog.V(4).Info("instances.InstanceMetadata() called, node: ", node.Name)
@@ -90,6 +94,11 @@ func (i *instances) InstanceMetadata(ctx context.Context, node *v1.Node) (*cloud
} }
} }
nodeSpec, err := transformer.TransformNode(i.c.config.Transformations, meta)
if err != nil {
return nil, fmt.Errorf("error transforming node: %w", err)
}
ifaces, err := i.c.getNodeIfaces(ctx, nodeIP) ifaces, err := i.c.getNodeIfaces(ctx, nodeIP)
if err != nil { if err != nil {
return nil, fmt.Errorf("error getting interfaces list from the node %s: %w", node.Name, err) return nil, fmt.Errorf("error getting interfaces list from the node %s: %w", node.Name, err)
@@ -112,7 +121,23 @@ func (i *instances) InstanceMetadata(ctx context.Context, node *v1.Node) (*cloud
}, nil }, nil
} }
if err := syncNodeLabels(i.c, node, meta); err != nil { if nodeSpec != nil && nodeSpec.Annotations != nil {
klog.V(4).Infof("instances.InstanceMetadata() node %s has annotations: %+v", node.Name, nodeSpec.Annotations)
if err := syncNodeAnnotations(ctx, i.c, node, nodeSpec.Annotations); err != nil {
klog.Errorf("failed update annotations for node %s, %v", node.Name, err)
}
}
nodeLabels := setTalosNodeLabels(i.c, meta)
if nodeSpec != nil && nodeSpec.Labels != nil {
klog.V(4).Infof("instances.InstanceMetadata() node %s has labels: %+v", node.Name, nodeSpec.Labels)
maps.Copy(nodeLabels, nodeSpec.Labels)
}
if err := syncNodeLabels(i.c, node, nodeLabels); err != nil {
klog.Errorf("failed update labels for node %s, %v", node.Name, err) klog.Errorf("failed update labels for node %s, %v", node.Name, err)
} }

View File

@@ -0,0 +1,169 @@
// Package transformer provides a mechanism to transform node specification based on node transformation rules.
package transformer
import (
"bytes"
"fmt"
"html/template"
"reflect"
"slices"
"strings"
"github.com/siderolabs/talos-cloud-controller-manager/pkg/nodeselector"
"github.com/siderolabs/talos/pkg/machinery/resources/runtime"
)
// NodeTerm represents expressions and fields to transform node metadata.
type NodeTerm struct {
Name string `yaml:"name,omitempty"`
NodeSelector []nodeselector.NodeSelectorTerm `yaml:"nodeSelector,omitempty"`
Annotations map[string]string `yaml:"annotations,omitempty"`
Labels map[string]string `yaml:"labels,omitempty"`
PlatformMetadata map[string]string `yaml:"platformMetadata,omitempty"`
}
// NodeSpec represents the transformed node specifcations.
type NodeSpec struct {
Annotations map[string]string
Labels map[string]string
}
var prohibitedPlatformMetadataKeys = []string{"hostname", "platform"}
// TransformNode transforms the node metadata based on the node transformation rules.
func TransformNode(terms []NodeTerm, platformMetadata *runtime.PlatformMetadataSpec) (*NodeSpec, error) {
if len(terms) == 0 {
return nil, nil
}
metadata := metadataFromStruct(platformMetadata)
for _, term := range terms {
match, err := nodeselector.Match(term.NodeSelector, metadata)
if err != nil {
return nil, err
}
if match {
node := &NodeSpec{
Annotations: make(map[string]string),
Labels: make(map[string]string),
}
if term.Annotations != nil {
for k, v := range term.Annotations {
t, err := executeTemplate(v, platformMetadata)
if err != nil {
return nil, fmt.Errorf("failed to transformer annotation '%q': %w", k, err)
}
node.Annotations[k] = t
}
}
if term.Labels != nil {
for k, v := range term.Labels {
t, err := executeTemplate(v, platformMetadata)
if err != nil {
return nil, fmt.Errorf("failed to transformer label '%s': %w", k, err)
}
node.Labels[k] = t
}
}
if term.PlatformMetadata != nil {
p := reflect.ValueOf(platformMetadata)
ps := p.Elem()
for k, v := range term.PlatformMetadata {
if slices.Contains(prohibitedPlatformMetadataKeys, strings.ToLower(k)) {
continue
}
t, err := executeTemplate(v, platformMetadata)
if err != nil {
return nil, fmt.Errorf("failed to transformer platform metadata '%s': %w", k, err)
}
f := ps.FieldByNameFunc(func(fieldName string) bool {
return strings.EqualFold(fieldName, k)
})
if f.IsValid() {
switch f.Kind() { //nolint:exhaustive
case reflect.Bool:
f.SetBool(t == "true")
case reflect.String:
f.SetString(strings.TrimSpace(t))
default:
return nil, fmt.Errorf("unsupported platform metadata field %s", k)
}
}
}
}
return node, nil
}
}
return nil, nil
}
func executeTemplate(tmpl string, data interface{}) (string, error) {
t, err := template.New("transformer").Parse(tmpl)
if err != nil {
return "", fmt.Errorf("failed to parse template %q: %w", tmpl, err)
}
var buf bytes.Buffer
if err := t.Execute(&buf, data); err != nil {
return "", err
}
return buf.String(), nil
}
func metadataFromStruct(in *runtime.PlatformMetadataSpec) map[string]string {
if in == nil {
return nil
}
metadata := make(map[string]string)
v := reflect.ValueOf(in)
if v.Kind() == reflect.Ptr {
v = v.Elem()
}
if v.Kind() == reflect.Struct {
for i := 0; i < v.NumField(); i++ {
// skip unexported fields
if !v.Field(i).CanInterface() {
continue
}
tag := v.Type().Field(i).Tag.Get("yaml")
if tag == "" {
continue
}
fieldName := strings.ToLower(strings.Split(tag, ",")[0])
reflectedValue := reflect.Indirect(v.Field(i))
if reflectedValue.IsValid() {
switch reflectedValue.Kind() { //nolint:exhaustive
case reflect.Bool:
metadata[fieldName] = fmt.Sprintf("%t", reflectedValue.Bool())
case reflect.String:
v := reflectedValue.String()
if v != "" {
metadata[fieldName] = v
}
}
}
}
}
return metadata
}

View File

@@ -0,0 +1,210 @@
package transformer_test
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"github.com/siderolabs/talos-cloud-controller-manager/pkg/transformer"
"github.com/siderolabs/talos/pkg/machinery/resources/runtime"
)
func TestMatch(t *testing.T) {
for _, tt := range []struct {
name string
terms []transformer.NodeTerm
metadata runtime.PlatformMetadataSpec
expected *transformer.NodeSpec
expectedMeta *runtime.PlatformMetadataSpec
expectedError error
}{
{
name: "Empty rules",
terms: []transformer.NodeTerm{},
metadata: runtime.PlatformMetadataSpec{
Platform: "test-platform",
Hostname: "test-hostname",
},
},
{
name: "Transform labels",
terms: []transformer.NodeTerm{
{
Name: "my-transformer",
Labels: map[string]string{
"my-label-name": "my-value",
},
},
},
metadata: runtime.PlatformMetadataSpec{
Platform: "test-platform",
Hostname: "test-hostname",
},
expected: &transformer.NodeSpec{
Annotations: map[string]string{},
Labels: map[string]string{
"my-label-name": "my-value",
},
},
},
{
name: "Transform annotations and labels",
terms: []transformer.NodeTerm{
{
Name: "my-transformer",
Labels: map[string]string{
"my-label-name": "my-value",
},
Annotations: map[string]string{
"my-annotation-name": "my-annotation-value",
},
},
},
metadata: runtime.PlatformMetadataSpec{
Platform: "test-platform",
Hostname: "test-hostname",
},
expected: &transformer.NodeSpec{
Annotations: map[string]string{
"my-annotation-name": "my-annotation-value",
},
Labels: map[string]string{
"my-label-name": "my-value",
},
},
},
{
name: "Transform bad template",
terms: []transformer.NodeTerm{
{
Name: "my-transformer",
Labels: map[string]string{
"label-template": "my-value-{{ .Spot",
},
},
},
metadata: runtime.PlatformMetadataSpec{
Platform: "test-platform",
Hostname: "test-hostname",
},
expectedError: fmt.Errorf("failed to transformer label 'label-template': failed to parse template \"my-value-{{ .Spot\": template: transformer:1: unclosed action"),
},
{
name: "Transform annotations with template",
terms: []transformer.NodeTerm{
{
Name: "my-transformer",
Annotations: map[string]string{
"annotation-template": "my-value-{{ .Platform }}",
},
},
},
metadata: runtime.PlatformMetadataSpec{
Platform: "test-platform",
Hostname: "test-hostname",
},
expected: &transformer.NodeSpec{
Annotations: map[string]string{
"annotation-template": "my-value-test-platform",
},
Labels: map[string]string{},
},
},
{
name: "Transform labels with template and missing metadata fields",
terms: []transformer.NodeTerm{
{
Name: "my-transformer",
Labels: map[string]string{
"label-template": "my-value-{{ .Spot }}-{{ .Zone }}",
},
},
},
metadata: runtime.PlatformMetadataSpec{
Platform: "test-platform",
Hostname: "test-hostname",
},
expected: &transformer.NodeSpec{
Annotations: map[string]string{},
Labels: map[string]string{
"label-template": "my-value-false-",
},
},
},
{
name: "Transform metadata fields",
terms: []transformer.NodeTerm{
{
Name: "my-transformer",
PlatformMetadata: map[string]string{
"Spot": "true",
"Zone": "us-west1",
},
},
},
metadata: runtime.PlatformMetadataSpec{
Platform: "test-platform",
Hostname: "test-hostname",
},
expected: &transformer.NodeSpec{
Annotations: map[string]string{},
Labels: map[string]string{},
},
expectedMeta: &runtime.PlatformMetadataSpec{
Platform: "test-platform",
Hostname: "test-hostname",
Spot: true,
Zone: "us-west1",
},
},
{
name: "Transform metadata with wrong fields",
terms: []transformer.NodeTerm{
{
Name: "my-transformer",
PlatformMetadata: map[string]string{
"Hostname": "fake-hostname",
"spot": "true",
"zoNe": "us-west1",
"wrong": "value",
},
},
},
metadata: runtime.PlatformMetadataSpec{
Platform: "test-platform",
Hostname: "test-hostname",
},
expected: &transformer.NodeSpec{
Annotations: map[string]string{},
Labels: map[string]string{},
},
expectedMeta: &runtime.PlatformMetadataSpec{
Platform: "test-platform",
Hostname: "test-hostname",
Spot: true,
Zone: "us-west1",
},
},
} {
t.Run(tt.name, func(t *testing.T) {
node, err := transformer.TransformNode(tt.terms, &tt.metadata)
if tt.expectedError != nil {
assert.NotNil(t, err)
assert.Equal(t, tt.expectedError.Error(), err.Error())
} else {
assert.NoError(t, err)
if tt.expected != nil {
assert.NotNil(t, node)
assert.EqualValues(t, tt.expected, node)
if tt.expectedMeta != nil {
assert.EqualValues(t, tt.expectedMeta, &tt.metadata)
}
}
}
})
}
}